Skip to content

Commit f62380f

Browse files
committed
feat(model-client): added IModelClientV2.query
Executes a ModelQL query on the given branch/version. The endpoint on the server already existed, but wasn't used by the client yet.
1 parent 6c8f96a commit f62380f

File tree

6 files changed

+74
-10
lines changed

6 files changed

+74
-10
lines changed

model-client/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ kotlin {
4343
api(project(":model-api"))
4444
api(project(":model-datastructure"))
4545
api(project(":model-server-api"))
46+
implementation(project(":modelql-client"))
47+
api(project(":modelql-core"))
4648
implementation(kotlin("stdlib-common"))
4749
implementation(libs.kotlin.collections.immutable)
4850
implementation(libs.kotlin.coroutines.core)

model-client/src/commonMain/kotlin/org/modelix/model/client2/IModelClientV2.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ package org.modelix.model.client2
1515

1616
import org.modelix.model.IVersion
1717
import org.modelix.model.api.IIdGenerator
18+
import org.modelix.model.api.INode
1819
import org.modelix.model.lazy.BranchReference
1920
import org.modelix.model.lazy.RepositoryId
21+
import org.modelix.modelql.core.IMonoStep
2022

2123
/**
2224
* This interface is meant exclusively for model client usage.
@@ -62,4 +64,8 @@ interface IModelClientV2 {
6264
suspend fun poll(branch: BranchReference, lastKnownVersion: IVersion?): IVersion
6365

6466
suspend fun pollHash(branch: BranchReference, lastKnownVersion: IVersion?): String
67+
68+
suspend fun <R> query(branch: BranchReference, body: (IMonoStep<INode>) -> IMonoStep<R>): R
69+
70+
suspend fun <R> query(repositoryId: RepositoryId, versionHash: String, body: (IMonoStep<INode>) -> IMonoStep<R>): R
6571
}

model-client/src/commonMain/kotlin/org/modelix/model/client2/ModelClientV2.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ import org.modelix.model.operations.OTBranch
4949
import org.modelix.model.persistent.HashUtil
5050
import org.modelix.model.persistent.MapBasedStore
5151
import org.modelix.model.server.api.v2.VersionDelta
52+
import org.modelix.modelql.client.ModelQLClient
53+
import org.modelix.modelql.core.IMonoStep
5254
import kotlin.time.Duration.Companion.seconds
5355

5456
class ModelClientV2(
@@ -212,6 +214,22 @@ class ModelClientV2(
212214
return receivedVersion
213215
}
214216

217+
override suspend fun <R> query(branch: BranchReference, body: (IMonoStep<INode>) -> IMonoStep<R>): R {
218+
val url = URLBuilder().apply {
219+
takeFrom(baseUrl)
220+
appendPathSegmentsEncodingSlash("repositories", branch.repositoryId.id, "branches", branch.branchName, "query")
221+
}
222+
return ModelQLClient.builder().httpClient(httpClient).url(url.buildString()).build().query(body)
223+
}
224+
225+
override suspend fun <R> query(repository: RepositoryId, versionHash: String, body: (IMonoStep<INode>) -> IMonoStep<R>): R {
226+
val url = URLBuilder().apply {
227+
takeFrom(baseUrl)
228+
appendPathSegmentsEncodingSlash("repositories", repository.id, "versions", versionHash, "query")
229+
}
230+
return ModelQLClient.builder().httpClient(httpClient).url(url.buildString()).build().query(body)
231+
}
232+
215233
override fun close() {
216234
httpClient.close()
217235
}

model-server/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ dependencies {
6363
testImplementation(libs.cucumber.java)
6464
testImplementation(libs.ktor.server.test.host)
6565
testImplementation(kotlin("test"))
66+
testImplementation(project(":modelql-untyped"))
6667
}
6768

6869
tasks.test {

model-server/src/main/kotlin/org/modelix/model/server/handlers/ModelReplicationServer.kt

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,17 @@
1414

1515
package org.modelix.model.server.handlers
1616

17-
import io.ktor.http.ContentType
1817
import io.ktor.http.HttpStatusCode
1918
import io.ktor.server.application.Application
2019
import io.ktor.server.application.ApplicationCall
2120
import io.ktor.server.application.call
2221
import io.ktor.server.plugins.origin
2322
import io.ktor.server.request.receive
24-
import io.ktor.server.request.receiveParameters
2523
import io.ktor.server.response.respond
2624
import io.ktor.server.response.respondText
2725
import io.ktor.server.routing.Route
2826
import io.ktor.server.routing.get
2927
import io.ktor.server.routing.post
30-
import io.ktor.server.routing.put
3128
import io.ktor.server.routing.route
3229
import io.ktor.server.routing.routing
3330
import io.ktor.server.websocket.webSocket
@@ -38,6 +35,7 @@ import kotlinx.serialization.encodeToString
3835
import kotlinx.serialization.json.Json
3936
import org.modelix.authorization.getUserName
4037
import org.modelix.model.api.PBranch
38+
import org.modelix.model.api.TreePointer
4139
import org.modelix.model.api.getRootNode
4240
import org.modelix.model.area.getArea
4341
import org.modelix.model.client2.checkObjectHashes
@@ -46,14 +44,11 @@ import org.modelix.model.lazy.CLTree
4644
import org.modelix.model.lazy.CLVersion
4745
import org.modelix.model.lazy.RepositoryId
4846
import org.modelix.model.operations.OTBranch
49-
import org.modelix.model.persistent.HashUtil
50-
import org.modelix.model.server.api.ModelQuery
5147
import org.modelix.model.server.api.v2.VersionDelta
5248
import org.modelix.model.server.store.IStoreClient
5349
import org.modelix.model.server.store.LocalModelClient
5450
import org.modelix.modelql.server.ModelQLServer
5551
import org.slf4j.LoggerFactory
56-
import java.util.UUID
5752

5853
/**
5954
* Implements the endpoints used by the 'model-client', but compared to KeyValueLikeModelServer also understands what
@@ -65,10 +60,6 @@ class ModelReplicationServer(val repositoriesManager: RepositoriesManager) {
6560

6661
companion object {
6762
private val LOG = LoggerFactory.getLogger(ModelReplicationServer::class.java)
68-
69-
private fun randomUUID(): String {
70-
return UUID.randomUUID().toString().replace("[^a-zA-Z0-9]".toRegex(), "")
71-
}
7263
}
7364

7465
private val modelClient: LocalModelClient get() = repositoriesManager.client
@@ -191,6 +182,35 @@ class ModelReplicationServer(val repositoriesManager: RepositoriesManager) {
191182
}
192183
}
193184
}
185+
route("versions") {
186+
route("{versionHash}") {
187+
get {
188+
// TODO permission check on the repository ID is not sufficient, because the client could
189+
// provide any repository ID to access a version inside a different repository.
190+
// A check if the version belongs to the repository is required.
191+
val baseVersionHash = call.request.queryParameters["lastKnown"]
192+
val versionHash = call.parameters["versionHash"]!!
193+
if (storeClient[versionHash] == null) {
194+
call.respondText(
195+
"Version '$versionHash' doesn't exist",
196+
status = HttpStatusCode.NotFound,
197+
)
198+
return@get
199+
}
200+
call.respondDelta(versionHash, baseVersionHash)
201+
}
202+
get("history/{oldestVersionHash}") {
203+
TODO()
204+
}
205+
post("query") {
206+
val versionHash = call.parameters["versionHash"]!!
207+
val version = CLVersion.loadFromHash(versionHash, repositoriesManager.client.storeCache)
208+
val initialTree = version.getTree()
209+
val branch = TreePointer(initialTree)
210+
ModelQLServer.handleCall(call, branch.getRootNode(), branch.getArea())
211+
}
212+
}
213+
}
194214
}
195215
}
196216
route("versions") {

model-server/src/test/kotlin/org/modelix/model/server/ModelClientV2Test.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import org.modelix.model.lazy.RepositoryId
3232
import org.modelix.model.operations.OTBranch
3333
import org.modelix.model.server.handlers.ModelReplicationServer
3434
import org.modelix.model.server.store.InMemoryStoreClient
35+
import org.modelix.modelql.core.count
36+
import org.modelix.modelql.untyped.allChildren
3537
import kotlin.test.Test
3638
import kotlin.test.assertEquals
3739

@@ -87,6 +89,21 @@ class ModelClientV2Test {
8789
)
8890
}
8991

92+
@Test
93+
fun testModelQLEndpoint() = runTest {
94+
val url = "http://localhost/v2"
95+
val client = ModelClientV2.builder().url(url).client(client).build().also { it.init() }
96+
97+
val repositoryId = RepositoryId("repo1")
98+
val branchRef = repositoryId.getBranchReference()
99+
val initialVersion = client.initRepository(repositoryId)
100+
val size = client.query(branchRef) { it.allChildren().count() }
101+
assertEquals(0, size)
102+
103+
val size2 = client.query(repositoryId, initialVersion.getContentHash()) { it.allChildren().count() }
104+
assertEquals(0, size2)
105+
}
106+
90107
@Test
91108
fun testSlashesInPathSegmentsFromRepositoryIdAndBranchId() = runTest {
92109
val url = "http://localhost/v2"

0 commit comments

Comments
 (0)