Skip to content

Commit 4e991cb

Browse files
committed
fix(modelql): unresolvable node reference in ModelQL should result in a 404
1 parent 83aa1d6 commit 4e991cb

File tree

6 files changed

+40
-3
lines changed

6 files changed

+40
-3
lines changed

model-api/src/commonMain/kotlin/org/modelix/model/api/INodeReference.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,5 @@ class NodeReferenceKSerializer : KSerializer<INodeReference> {
7373
encoder.encodeString(value.serialize())
7474
}
7575
}
76+
77+
class UnresolvableNodeReferenceException(val reference: INodeReference) : IllegalArgumentException("Node not found: $reference")

model-server-openapi/specifications/model-server-v2.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,8 @@ paths:
322322
responses:
323323
"200":
324324
$ref: '#/components/responses/200json'
325+
"404":
326+
$ref: '#/components/responses/modelql-404'
325327
default:
326328
$ref: '#/components/responses/GeneralError'
327329
/repositories/{repository}/init:
@@ -427,6 +429,8 @@ paths:
427429
responses:
428430
"200":
429431
$ref: '#/components/responses/200json'
432+
"404":
433+
$ref: '#/components/responses/modelql-404'
430434
default:
431435
$ref: '#/components/responses/GeneralError'
432436

@@ -499,6 +503,8 @@ components:
499503
application/problem+json:
500504
schema:
501505
$ref: 'problem.yaml#/Problem'
506+
"modelql-404":
507+
description: "A IProducingStep<INodeReference>.resolve() call failed to find the node."
502508
GeneralError:
503509
description: Unexpected error
504510
content:

modelql-client/src/commonMain/kotlin/org/modelix/modelql/client/ModelQLClient.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package org.modelix.modelql.client
1616
import io.ktor.client.HttpClient
1717
import io.ktor.client.request.post
1818
import io.ktor.client.request.setBody
19+
import io.ktor.client.statement.HttpResponse
1920
import io.ktor.client.statement.bodyAsText
2021
import io.ktor.http.HttpStatusCode
2122
import kotlinx.serialization.encodeToString
@@ -82,7 +83,7 @@ class ModelQLClient(val url: String, val client: HttpClient, includedSerializers
8283
LOG.debug { "result: $text" }
8384
return text
8485
}
85-
else -> throw RuntimeException("Query failed : $query \nclient version: $MODELIX_VERSION\n${response.status}\n${response.bodyAsText()}")
86+
else -> throw ModelQueryRequestException(query, response, response.bodyAsText())
8687
}
8788
}
8889

@@ -93,3 +94,9 @@ class ModelQLClient(val url: String, val client: HttpClient, includedSerializers
9394
}
9495

9596
expect fun <ResultT> ModelQLClient.blockingQuery(body: (IMonoStep<INode>) -> IMonoStep<ResultT>): ResultT
97+
98+
class ModelQueryRequestException(
99+
val query: UnboundQuery<INode, *, *>,
100+
val httpResponse: HttpResponse,
101+
val responseBody: String,
102+
) : RuntimeException("Query failed : $query \nclient version: $MODELIX_VERSION\n${httpResponse.status}\n$responseBody")

modelql-client/src/jvmTest/kotlin/org/modelix/modelql/client/ModelQLClientTest.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@
1414
package org.modelix.modelql.client
1515

1616
import io.ktor.client.HttpClient
17+
import io.ktor.http.HttpStatusCode
1718
import io.ktor.server.routing.routing
1819
import io.ktor.server.testing.testApplication
1920
import kotlinx.coroutines.withTimeout
21+
import org.junit.jupiter.api.assertThrows
2022
import org.modelix.model.api.ConceptReference
2123
import org.modelix.model.api.IConceptReference
2224
import org.modelix.model.api.INode
25+
import org.modelix.model.api.NodeReference
2326
import org.modelix.model.api.PBranch
2427
import org.modelix.model.api.getRootNode
2528
import org.modelix.model.client.IdGenerator
@@ -53,6 +56,7 @@ import org.modelix.modelql.untyped.descendants
5356
import org.modelix.modelql.untyped.nodeReference
5457
import org.modelix.modelql.untyped.property
5558
import org.modelix.modelql.untyped.remove
59+
import org.modelix.modelql.untyped.resolve
5660
import org.modelix.modelql.untyped.setProperty
5761
import org.modelix.modelql.untyped.setReference
5862
import kotlin.test.Test
@@ -279,4 +283,15 @@ class ModelQLClientTest {
279283
}
280284
assertEquals(3, result)
281285
}
286+
287+
@Test
288+
fun `resolving a non-existing node reference returns 404`() = runTest { httpClient ->
289+
val client = ModelQLClient("http://localhost/query", httpClient)
290+
val ex = assertThrows<ModelQueryRequestException> {
291+
client.query<INode> {
292+
NodeReference("doesnotexist").asMono().resolve()
293+
}
294+
}
295+
assertEquals(HttpStatusCode.NotFound, ex.httpResponse.status)
296+
}
282297
}

modelql-server/src/main/kotlin/org/modelix/modelql/server/ModelQLServer.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import io.ktor.server.routing.post
2424
import kotlinx.coroutines.runBlocking
2525
import kotlinx.serialization.KSerializer
2626
import org.modelix.model.api.INode
27+
import org.modelix.model.api.UnresolvableNodeReferenceException
2728
import org.modelix.model.area.IArea
2829
import org.modelix.modelql.core.IMonoUnboundQuery
2930
import org.modelix.modelql.core.IStepOutput
@@ -113,6 +114,12 @@ class ModelQLServer private constructor(val rootNodeProvider: () -> INode?, val
113114
val serializedResult = json.encodeToString(VersionAndData.serializer(serializer), versionAndResult)
114115
afterQueryExecution()
115116
call.respondText(text = serializedResult, contentType = ContentType.Application.Json)
117+
} catch (ex: UnresolvableNodeReferenceException) {
118+
afterQueryExecution()
119+
call.respondText(
120+
text = "server version: $MODELIX_VERSION\n" + ex.stackTraceToString(),
121+
status = HttpStatusCode.NotFound,
122+
)
116123
} catch (ex: Throwable) {
117124
afterQueryExecution()
118125
call.respondText(

modelql-untyped/src/commonMain/kotlin/org/modelix/modelql/untyped/ResolveNodeStep.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import kotlinx.serialization.SerialName
1919
import kotlinx.serialization.Serializable
2020
import org.modelix.model.api.INode
2121
import org.modelix.model.api.INodeReference
22+
import org.modelix.model.api.UnresolvableNodeReferenceException
2223
import org.modelix.model.api.resolveInCurrentContext
2324
import org.modelix.modelql.core.IFlowInstantiationContext
2425
import org.modelix.modelql.core.IFluxStep
@@ -34,12 +35,11 @@ import org.modelix.modelql.core.StepDescriptor
3435
import org.modelix.modelql.core.StepFlow
3536
import org.modelix.modelql.core.asStepFlow
3637
import org.modelix.modelql.core.stepOutputSerializer
37-
import org.modelix.modelql.untyped.AllReferencesTraversalStep.Descriptor
3838

3939
class ResolveNodeStep() : MonoTransformingStep<INodeReference, INode>() {
4040
override fun createFlow(input: StepFlow<INodeReference>, context: IFlowInstantiationContext): StepFlow<INode> {
4141
return input.map {
42-
it.value.resolveInCurrentContext() ?: throw IllegalArgumentException("Node not found: ${it.value}")
42+
it.value.resolveInCurrentContext() ?: throw UnresolvableNodeReferenceException(it.value)
4343
}.asStepFlow(this)
4444
}
4545

0 commit comments

Comments
 (0)