Skip to content

Commit

Permalink
fix(model-client): keep using role names by default
Browse files Browse the repository at this point in the history
There is still too much code that needs to be migrated first.
It's just important to have the new API to use it in new code, but enabling role IDs can be done later.
  • Loading branch information
slisson committed Mar 28, 2023
1 parent 04a96fd commit a239fb6
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 20 deletions.
64 changes: 48 additions & 16 deletions model-client/src/commonMain/kotlin/org/modelix/model/lazy/CLTree.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class CLTree : ITree, IBulkTree {
constructor(hash: String?, store: IDeserializingKeyValueStore) : this(if (hash == null) null else store.get<CPTree>(hash) { CPTree.deserialize(it) }, store)
constructor(store: IDeserializingKeyValueStore) : this(null as CPTree?, store)
constructor(data: CPTree?, store_: IDeserializingKeyValueStore) : this(data, null as RepositoryId?, store_)
private constructor(data: CPTree?, repositoryId_: RepositoryId?, store_: IDeserializingKeyValueStore) {
private constructor(data: CPTree?, repositoryId_: RepositoryId?, store_: IDeserializingKeyValueStore, useRoleIds: Boolean = false) {
var repositoryId = repositoryId_
var store = store_
if (data == null) {
Expand All @@ -55,7 +55,7 @@ class CLTree : ITree, IBulkTree {
arrayOf()
)
val idToHash = storeElement(root, CLHamtInternal.createEmpty(store))
this.data = CPTree(repositoryId.id, KVEntryReference(idToHash.getData()), true)
this.data = CPTree(repositoryId.id, KVEntryReference(idToHash.getData()), useRoleIds)
} else {
this.data = data
}
Expand Down Expand Up @@ -113,33 +113,33 @@ class CLTree : ITree, IBulkTree {
get() = resolveElement(ITree.ROOT_ID)

override fun setProperty(nodeId: Long, role: String, value: String?): ITree {
checkRoleId(nodeId, role)
checkPropertyRoleId(nodeId, role)
var newIdToHash = nodesMap
val newNodeData = resolveElement(nodeId)!!.getData().withPropertyValue(role, value)
newIdToHash = newIdToHash!!.put(newNodeData)
return CLTree(data.id, newIdToHash!!, store, data.usesRoleIds)
}

override fun addNewChild(parentId: Long, role: String?, index: Int, childId: Long, concept: IConceptReference?): ITree {
checkRoleId(parentId, role)
checkChildRoleId(parentId, role)
if (containsNode(childId)) {
throw DuplicateNodeId("Node ID already exists: ${childId.toString(16)}")
}
return createNewNode(childId, concept).addChild(parentId, role, index, childId)
}

override fun addNewChild(parentId: Long, role: String?, index: Int, childId: Long, concept: IConcept?): ITree {
checkRoleId(parentId, role)
checkChildRoleId(parentId, role)
return addNewChild(parentId, role, index, childId, concept?.getReference())
}

override fun addNewChildren(parentId: Long, role: String?, index: Int, newIds: LongArray, concepts: Array<IConcept?>): ITree {
checkRoleId(parentId, role)
checkChildRoleId(parentId, role)
throw UnsupportedOperationException("Not implemented yet")
}

override fun addNewChildren(parentId: Long, role: String?, index: Int, newIds: LongArray, concepts: Array<IConceptReference?>): ITree {
checkRoleId(parentId, role)
checkChildRoleId(parentId, role)
TODO("Not yet implemented")
}

Expand Down Expand Up @@ -219,7 +219,7 @@ class CLTree : ITree, IBulkTree {
}

override fun setReferenceTarget(sourceId: Long, role: String, target: INodeReference?): ITree {
checkRoleId(sourceId, role)
checkReferenceRoleId(sourceId, role)
val source = resolveElement(sourceId)!!
val refData: CPNodeRef? = when (target) {
null -> null
Expand Down Expand Up @@ -303,7 +303,7 @@ class CLTree : ITree, IBulkTree {
}

override fun getChildren(parentId: Long, role: String?): Iterable<Long> {
checkRoleId(parentId, role)
checkChildRoleId(parentId, role)
val parent = resolveElement(parentId)
val children = parent!!.getChildren(BulkQuery(store)).execute()
return children
Expand Down Expand Up @@ -340,7 +340,7 @@ class CLTree : ITree, IBulkTree {
}

override fun getProperty(nodeId: Long, role: String): String? {
checkRoleId(nodeId, role)
checkPropertyRoleId(nodeId, role)
val node = resolveElement(nodeId)
return node!!.getData().getPropertyValue(role)
}
Expand All @@ -356,7 +356,7 @@ class CLTree : ITree, IBulkTree {
}

override fun getReferenceTarget(sourceId: Long, role: String): INodeReference? {
checkRoleId(sourceId, role)
checkReferenceRoleId(sourceId, role)
val node = resolveElement(sourceId)!!
val targetRef = node.getData().getReferenceTarget(role)
return when {
Expand All @@ -373,7 +373,7 @@ class CLTree : ITree, IBulkTree {
}

override fun moveChild(targetParentId: Long, targetRole: String?, targetIndex_: Int, childId: Long): ITree {
checkRoleId(targetParentId, targetRole)
checkChildRoleId(targetParentId, targetRole)
if (childId == ITree.ROOT_ID) throw RuntimeException("Moving the root node is not allowed")
var ancestor = targetParentId
while (ancestor != ITree.ROOT_ID) {
Expand Down Expand Up @@ -556,13 +556,45 @@ class CLTree : ITree, IBulkTree {
return "CLTree[$hash]"
}

private fun checkRoleId(nodeId: Long, role: String?) {
if (role != null && usesRoleIds() && role.matches(roleNamePattern) && getConceptReference(nodeId) != null) {
throw IllegalArgumentException("A role UID is expected, but this looks like a name: $role")
private fun checkChildRoleId(nodeId: Long, role: String?) = checkRoleId(nodeId, role) { it.getAllChildLinks() }
private fun checkReferenceRoleId(nodeId: Long, role: String?) = checkRoleId(nodeId, role) { it.getAllReferenceLinks() }
private fun checkPropertyRoleId(nodeId: Long, role: String?) = checkRoleId(nodeId, role) { it.getAllProperties() }
private fun checkRoleId(nodeId: Long, role: String?, rolesGetter: (IConcept) -> Iterable<IRole>) {
if (role != null && usesRoleIds()) {
val concept = getConceptReference(nodeId)?.tryResolve()
if (concept != null && rolesGetter(concept).any { it.getSimpleName() == role }) {
throw IllegalArgumentException("A role UID is expected, but a name was provided: $role")
}
}
}

companion object {
var roleNamePattern: Regex = Regex("""[a-zA-Z]+""")
fun builder(store: IDeserializingKeyValueStore) = Builder(store)
}

class Builder(var store: IDeserializingKeyValueStore) {
private var repositoryId: RepositoryId? = null
private var useRoleIds: Boolean = false

fun useRoleIds(value: Boolean = true): Builder {
this.useRoleIds = value
return this
}

fun repositoryId(id: RepositoryId): Builder {
this.repositoryId = id
return this
}

fun repositoryId(id: String): Builder = repositoryId(RepositoryId(id))

fun build(): CLTree {
return CLTree(
data = null as CPTree?,
repositoryId_ = repositoryId ?: RepositoryId.random(),
store_ = store,
useRoleIds = useRoleIds
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class TreeSerializationTest {
val mapStore = MapBaseStore()
var store = mapStore
var objectStore = ObjectStoreCache(store)
val initialTree = CLTree(RepositoryId("tree01"), objectStore)
val initialTree = CLTree.builder(objectStore).repositoryId("tree01").useRoleIds(true).build()
val initialVersion = CLVersion.createRegularVersion(
id = 1,
time = null,
Expand Down Expand Up @@ -315,7 +315,7 @@ class TreeSerializationTest {
val deserializedTree = deserializedVersion.tree
assertTree(deserializedTree)

val branch = PBranch(CLTree(RepositoryId("tree01"), ObjectStoreCache(MapBaseStore())), IdGenerator.newInstance(2))
val branch = PBranch(CLTree.builder(ObjectStoreCache(MapBaseStore())).repositoryId("tree01").useRoleIds(true).build(), IdGenerator.newInstance(2))
branch.runWrite {
for (operation in deserializedVersion.operations) {
operation.apply(branch.writeTransaction, deserializedVersion.store)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ actual open class ReplicatedRepository actual constructor(
var initialVersion = if (versionHash.isNullOrEmpty()) null else loadFromHash(versionHash, store)
val initialTree = MutableObject<CLTree>()
if (initialVersion == null) {
initialTree.setValue(CLTree(branchReference.repositoryId, store))
initialTree.setValue(CLTree.builder(store).repositoryId(branchReference.repositoryId).build())
initialVersion = createVersion(initialTree.value, arrayOf(), null)
client.asyncStore!!.put(branchReference.getKey(), initialVersion.hash)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ class DeprecatedLightModelServer(val client: LocalModelClient) {
post("/{repositoryId}/init") {
// TODO error if it already exists
val repositoryId = RepositoryId(call.parameters["repositoryId"]!!)
val newTree = CLTree(repositoryId, getStore())
val newTree = CLTree.builder(getStore()).repositoryId(repositoryId).build()
val userId = call.getUserName()
val newVersion = CLVersion.createRegularVersion(
client.idGenerator.generate(),
Expand Down

0 comments on commit a239fb6

Please sign in to comment.