Skip to content

Commit

Permalink
Merge pull request #660 from modelix/fix/content-explorer-null-refere…
Browse files Browse the repository at this point in the history
…nces

MODELIX-846 Null-References lead to exception in data explorer
  • Loading branch information
mhuster23 authored Apr 3, 2024
2 parents 8e82ab7 + 1533d6e commit 8e04371
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import kotlinx.html.small
import kotlinx.html.stream.appendHTML
import kotlinx.html.style
import kotlinx.html.table
import kotlinx.html.tbody
import kotlinx.html.td
import kotlinx.html.th
import kotlinx.html.thead
Expand Down Expand Up @@ -250,10 +251,12 @@ class ContentExplorer(private val client: IModelClient, private val repoManager:
th { +"Value" }
}
}
for (propertyRole in node.getPropertyRoles()) {
tr {
td { +propertyRole }
td { +"${node.getPropertyValue(propertyRole)}" }
tbody {
for (propertyRole in node.getPropertyRoles()) {
tr {
td { +propertyRole }
td { +"${node.getPropertyValue(propertyRole)}" }
}
}
}
}
Expand All @@ -265,19 +268,21 @@ class ContentExplorer(private val client: IModelClient, private val repoManager:
thead {
tr {
th { +"ReferenceRole" }
th { +"NodeId" }
th { +"Value" }
th { +"Target NodeId" }
th { +"Target Reference" }
}
}
INodeResolutionScope.runWithAdditionalScope(node.getArea()) {
for (referenceRole in node.getReferenceRoles()) {
tr {
td { +referenceRole }
td {
+"${(node.getReferenceTarget(referenceRole) as PNodeAdapter).nodeId}"
}
td {
+"${node.getReferenceTarget(referenceRole)}"
tbody {
INodeResolutionScope.runWithAdditionalScope(node.getArea()) {
for (referenceRole in node.getReferenceRoles()) {
tr {
td { +referenceRole }
td {
+"${(node.getReferenceTarget(referenceRole) as? PNodeAdapter)?.nodeId}"
}
td {
+"${node.getReferenceTargetRef(referenceRole)?.serialize()}"
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,8 @@

package org.modelix.model.server

import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.install
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.resources.Resources
import io.ktor.server.routing.IgnoreTrailingSlash
import io.ktor.server.testing.ApplicationTestBuilder
import io.ktor.server.testing.testApplication
import io.ktor.server.websocket.WebSockets
import org.modelix.authorization.installAuthentication
import org.modelix.model.api.IConceptReference
import org.modelix.model.api.ITree
Expand Down Expand Up @@ -50,23 +44,12 @@ class ModelClientV2Test {
private fun runTest(block: suspend ApplicationTestBuilder.() -> Unit) = testApplication {
application {
installAuthentication(unitTestMode = true)
install(ContentNegotiation) {
json()
}
install(WebSockets)
install(Resources)
install(IgnoreTrailingSlash)
installDefaultServerPlugins()
ModelReplicationServer(InMemoryStoreClient()).init(this)
}
block()
}

private suspend fun ApplicationTestBuilder.createModelClient(): ModelClientV2 {
val url = "http://localhost/v2"
val modelClient = ModelClientV2.builder().url(url).client(client).build().also { it.init() }
return modelClient
}

@Test
fun test_t1() = runTest {
val client = createModelClient()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2024.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.modelix.model.server

import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.Application
import io.ktor.server.application.install
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.resources.Resources
import io.ktor.server.routing.IgnoreTrailingSlash
import io.ktor.server.testing.ApplicationTestBuilder
import io.ktor.server.websocket.WebSockets
import org.modelix.model.client2.ModelClientV2

suspend fun ApplicationTestBuilder.createModelClient(): ModelClientV2 {
val url = "http://localhost/v2"
return ModelClientV2.builder().url(url).client(client).build().also { it.init() }
}

fun Application.installDefaultServerPlugins() {
install(WebSockets)
install(ContentNegotiation) { json() }
install(Resources)
install(IgnoreTrailingSlash)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,8 @@

package org.modelix.model.server

import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.install
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.resources.Resources
import io.ktor.server.routing.IgnoreTrailingSlash
import io.ktor.server.testing.ApplicationTestBuilder
import io.ktor.server.testing.testApplication
import io.ktor.server.websocket.WebSockets
import kotlinx.coroutines.coroutineScope
import org.modelix.authorization.installAuthentication
import org.modelix.model.api.IChildLink
Expand All @@ -48,12 +42,7 @@ class PullPerformanceTest {
val repositoriesManager = RepositoriesManager(LocalModelClient(storeClientWithStatistics))
application {
installAuthentication(unitTestMode = true)
install(ContentNegotiation) {
json()
}
install(WebSockets)
install(Resources)
install(IgnoreTrailingSlash)
installDefaultServerPlugins()
ModelReplicationServer(repositoriesManager).init(this)
KeyValueLikeModelServer(repositoriesManager).init(this)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,8 @@

package org.modelix.model.server

import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.install
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.resources.Resources
import io.ktor.server.routing.IgnoreTrailingSlash
import io.ktor.server.testing.ApplicationTestBuilder
import io.ktor.server.testing.testApplication
import io.ktor.server.websocket.WebSockets
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
Expand Down Expand Up @@ -73,12 +67,7 @@ class ReplicatedRepositoryTest {
private fun runTest(block: suspend ApplicationTestBuilder.(scope: CoroutineScope) -> Unit) = testApplication {
application {
installAuthentication(unitTestMode = true)
install(ContentNegotiation) {
json()
}
install(WebSockets)
install(Resources)
install(IgnoreTrailingSlash)
installDefaultServerPlugins()
val repositoriesManager = RepositoriesManager(LocalModelClient(InMemoryStoreClient()))
ModelReplicationServer(repositoriesManager).init(this)
KeyValueLikeModelServer(repositoriesManager).init(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,25 @@ import io.ktor.client.statement.bodyAsText
import io.ktor.http.ContentType
import io.ktor.http.contentType
import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.resources.Resources
import io.ktor.server.routing.IgnoreTrailingSlash
import io.ktor.server.testing.ApplicationTestBuilder
import io.ktor.server.testing.testApplication
import io.ktor.server.websocket.WebSockets
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
import org.jsoup.select.Evaluator
import org.modelix.model.api.IReferenceLink
import org.modelix.model.api.ITree
import org.modelix.model.api.NodeReferenceById
import org.modelix.model.client.successful
import org.modelix.model.client2.runWrite
import org.modelix.model.lazy.CLVersion
import org.modelix.model.lazy.RepositoryId
import org.modelix.model.server.api.v2.VersionDelta
import org.modelix.model.server.createModelClient
import org.modelix.model.server.installDefaultServerPlugins
import org.modelix.model.server.store.InMemoryStoreClient
import org.modelix.model.server.store.LocalModelClient
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation as ClientContentNegotiation
Expand All @@ -48,26 +52,22 @@ class ContentExplorerTest {
private val modelClient = LocalModelClient(InMemoryStoreClient())
private val repoManager = RepositoriesManager(modelClient)

private fun runTest(body: suspend (ApplicationTestBuilder.() -> Unit)) {
testApplication {
install(WebSockets)
install(ContentNegotiation) { json() }
install(Resources)
install(IgnoreTrailingSlash)
application {
ModelReplicationServer(repoManager).init(this)
ContentExplorer(modelClient, repoManager).init(this)
}

body()
private fun runTest(body: suspend (ApplicationTestBuilder.() -> Unit)) = testApplication {
application {
installDefaultServerPlugins()
ModelReplicationServer(repoManager).init(this)
ContentExplorer(modelClient, repoManager).init(this)
}
body()
}

private fun ApplicationTestBuilder.createHttpClient() = createClient {
install(ClientContentNegotiation) { json() }
}

@Test
fun `node inspector finds root node`() = runTest {
val client = createClient {
install(ClientContentNegotiation) { json() }
}
val client = createHttpClient()

val delta: VersionDelta = client.post("/v2/repositories/node-inspector/init").body()

Expand All @@ -80,11 +80,37 @@ class ContentExplorerTest {
}

@Test
fun `nodes can be expanded`() = runTest {
val client = createClient {
install(ClientContentNegotiation) { json() }
fun `node inspector can handle unresolvable references`() = runTest {
val modelClient = createModelClient()
val repoId = RepositoryId("node-inspector-null-ref")
val branchRef = repoId.getBranchReference("master")
val refLinkName = "myUnresolvableRef"
val refLinkTargetRef = NodeReferenceById("notAResolvableId")

modelClient.initRepository(repoId)

modelClient.runWrite(branchRef) { root ->
root.setReferenceTarget(IReferenceLink.fromName(refLinkName), refLinkTargetRef)
}

val versionHash = modelClient.pullHash(branchRef)

val response = client.get("/content/$versionHash/${ITree.ROOT_ID}/")
val html = Jsoup.parse(response.bodyAsText())
val nameCell = html.selectXpath("""//td[text()="$refLinkName"]""").first() ?: error("table cell not found")
val row = checkNotNull(nameCell.parent()) { "table row not found" }
val targetNodeIdCell = row.allElements[2] // index 0 is the row itself and 1 the nameCell
val targetRefCell = row.allElements[3]

assertTrue(response.successful)
assertEquals("null", targetNodeIdCell.text())
assertEquals(refLinkTargetRef.serialize(), targetRefCell.text())
}

@Test
fun `nodes can be expanded`() = runTest {
val client = createHttpClient()

val delta: VersionDelta = client.post("/v2/repositories/node-expansion/init").body()

val versionHash = delta.versionHash
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,8 @@ package org.modelix.model.server.handlers
import io.ktor.client.request.get
import io.ktor.http.appendPathSegments
import io.ktor.http.takeFrom
import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.install
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.resources.Resources
import io.ktor.server.routing.IgnoreTrailingSlash
import io.ktor.server.testing.ApplicationTestBuilder
import io.ktor.server.testing.testApplication
import io.ktor.server.websocket.WebSockets
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope
import org.modelix.authorization.installAuthentication
Expand All @@ -35,6 +29,7 @@ import org.modelix.model.client2.readVersionDelta
import org.modelix.model.client2.runWrite
import org.modelix.model.client2.useVersionStreamFormat
import org.modelix.model.lazy.RepositoryId
import org.modelix.model.server.installDefaultServerPlugins
import org.modelix.model.server.store.InMemoryStoreClient
import org.modelix.model.server.store.LocalModelClient
import kotlin.test.Test
Expand All @@ -45,12 +40,7 @@ class ModelReplicationServerTest {
private fun runTest(block: suspend ApplicationTestBuilder.(scope: CoroutineScope) -> Unit) = testApplication {
application {
installAuthentication(unitTestMode = true)
install(ContentNegotiation) {
json()
}
install(WebSockets)
install(Resources)
install(IgnoreTrailingSlash)
installDefaultServerPlugins()
val repositoriesManager = RepositoriesManager(LocalModelClient(InMemoryStoreClient()))
ModelReplicationServer(repositoriesManager).init(this)
KeyValueLikeModelServer(repositoriesManager).init(this)
Expand Down

0 comments on commit 8e04371

Please sign in to comment.