From 8553fd0d92798cb4cd79853e0d20aec968936068 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Mon, 5 Feb 2024 17:03:20 +0100 Subject: [PATCH 1/6] feat(bulk-model-sync-gradle): ability to add meta properties for root node --- .../model/sync/bulk/gradle/ModelSyncGradlePlugin.kt | 1 + .../sync/bulk/gradle/config/ModelSyncGradleSettings.kt | 1 + .../sync/bulk/gradle/tasks/ImportIntoModelServer.kt | 10 ++++++++++ 3 files changed, 12 insertions(+) diff --git a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/ModelSyncGradlePlugin.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/ModelSyncGradlePlugin.kt index 318eec43c1..2f0afbd819 100644 --- a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/ModelSyncGradlePlugin.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/ModelSyncGradlePlugin.kt @@ -161,6 +161,7 @@ class ModelSyncGradlePlugin : Plugin { it.includedModulePrefixes.set(syncDirection.includedModulePrefixes) it.continueOnError.set(syncDirection.continueOnError) it.requestTimeoutSeconds.set(serverTarget.requestTimeoutSeconds) + it.metaProperties.set(serverTarget.metaProperties) } project.tasks.register("runSync${syncDirection.name.replaceFirstChar { it.uppercaseChar() }}") { diff --git a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/config/ModelSyncGradleSettings.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/config/ModelSyncGradleSettings.kt index 3aaaffee2a..b189d2586c 100644 --- a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/config/ModelSyncGradleSettings.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/config/ModelSyncGradleSettings.kt @@ -184,6 +184,7 @@ data class ServerTarget( override var repositoryId: String? = null, override var branchName: String? = null, override var requestTimeoutSeconds: Int = DEFAULT_REQUEST_TIMEOUT_SECONDS, + val metaProperties: MutableMap = mutableMapOf(), ) : ServerEndpoint { override fun getValidationErrors(): List { val errors = mutableListOf() diff --git a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ImportIntoModelServer.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ImportIntoModelServer.kt index 2a412a773e..2e8f2431b4 100644 --- a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ImportIntoModelServer.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ImportIntoModelServer.kt @@ -20,6 +20,7 @@ import kotlinx.coroutines.runBlocking import org.gradle.api.DefaultTask import org.gradle.api.file.DirectoryProperty import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.MapProperty import org.gradle.api.provider.Property import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.Input @@ -29,6 +30,7 @@ import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction import org.modelix.model.ModelFacade import org.modelix.model.api.INode +import org.modelix.model.api.IProperty import org.modelix.model.api.PNodeAdapter import org.modelix.model.client2.ModelClientV2 import org.modelix.model.client2.runWrite @@ -67,6 +69,9 @@ abstract class ImportIntoModelServer @Inject constructor(of: ObjectFactory) : De @Input val requestTimeoutSeconds: Property = of.property(Int::class.java) + @Input + val metaProperties: MapProperty = of.mapProperty(String::class.java, String::class.java) + @TaskAction fun import() { val inputDir = inputDir.get().asFile @@ -91,6 +96,11 @@ abstract class ImportIntoModelServer @Inject constructor(of: ObjectFactory) : De logger.info("Got root node: {}", rootNode) logger.info("Calculating diff...") ModelImporter(rootNode, continueOnError.get()).importFilesAsRootChildren(files) + + logger.info("Setting meta properties...") + for ((key, value) in metaProperties.get()) { + rootNode.setPropertyValue(IProperty.fromName(key), value) + } } logger.info("Sending diff to server...") } From 1664abccccc7da1fe54ccae9e6360c6bc301b7d1 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Mon, 5 Feb 2024 17:21:49 +0100 Subject: [PATCH 2/6] docs(bulk-model-sync-gradle): add docs for metaProperties configuration option --- .../reference/component-bulk-model-sync-gradle.adoc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/global/modules/core/pages/reference/component-bulk-model-sync-gradle.adoc b/docs/global/modules/core/pages/reference/component-bulk-model-sync-gradle.adoc index 7affb7731f..b030713e4a 100644 --- a/docs/global/modules/core/pages/reference/component-bulk-model-sync-gradle.adoc +++ b/docs/global/modules/core/pages/reference/component-bulk-model-sync-gradle.adoc @@ -137,11 +137,19 @@ If the target branch does not exist on the model-server, it will be created. |`revision` |String -|Source model-server revision. Can be used instead of `repositoryId` and `branchName`. Only available in ServerSource. +|Source model-server revision. Can be used instead of `repositoryId` and `branchName`. +Only available in ServerSource. |`requestTimeoutSeconds` |Integer -|The request timeout measured in seconds to apply when performing HTTP requests towards the model-server. Default: 5 minutes +|The request timeout measured in seconds to apply when performing HTTP requests towards the model-server. +Default: 5 minutes + +|`metaProperties` +|MutableMap +|Custom properties that will be attached to the root node. +The mapping is `propertyName -> propertyValue`. +Only available in ServerTarget. |=== From c134cffede87bbb0ffada3039f741586f610c0f4 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Tue, 6 Feb 2024 12:44:41 +0100 Subject: [PATCH 3/6] ci(bulk-model-sync-gradle): use absolute paths in ci script --- bulk-model-sync-gradle-test/ci.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bulk-model-sync-gradle-test/ci.sh b/bulk-model-sync-gradle-test/ci.sh index 7ea1e0c486..06677e7e04 100755 --- a/bulk-model-sync-gradle-test/ci.sh +++ b/bulk-model-sync-gradle-test/ci.sh @@ -3,7 +3,8 @@ set -e set -x -cd "$(dirname "$0")" +TEST_DIR="$(dirname "$(readlink -f "$0")")" +cd "${TEST_DIR}" ( cd graph-lang-api @@ -20,11 +21,11 @@ if [ "${CI}" != "true" ]; then } fi -cd .. +cd "${TEST_DIR}/.." ./gradlew :model-server:run --console=plain --args="-inmemory -port 28309" & MODEL_SERVER_PID=$! -cd "$(dirname "$0")" +cd "${TEST_DIR}" curl -X GET --retry 30 --retry-connrefused --retry-delay 1 http://localhost:28309/health From ce666cf266fca5ea2798621f0a580b824452beb1 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Tue, 6 Feb 2024 12:09:50 +0100 Subject: [PATCH 4/6] test(bulk-model-sync-gradle): refactor tests Extract side effect from test case into ChangeApplier and avoid companion objects in the tests. --- bulk-model-sync-gradle-test/ci.sh | 1 + .../sync/bulk/gradle/test/ChangeApplier.kt | 74 +++++++++++++++++++ .../model/sync/bulk/gradle/test/PullTest.kt | 17 ++--- .../model/sync/bulk/gradle/test/PushTest.kt | 54 ++------------ 4 files changed, 85 insertions(+), 61 deletions(-) create mode 100644 bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/ChangeApplier.kt diff --git a/bulk-model-sync-gradle-test/ci.sh b/bulk-model-sync-gradle-test/ci.sh index 06677e7e04..9f82788651 100755 --- a/bulk-model-sync-gradle-test/ci.sh +++ b/bulk-model-sync-gradle-test/ci.sh @@ -31,5 +31,6 @@ curl -X GET --retry 30 --retry-connrefused --retry-delay 1 http://localhost:2830 ./gradlew runSyncTestPush --console=plain --stacktrace ./gradlew test --tests 'PushTest' +./gradlew test --tests 'ChangeApplier' ./gradlew runSyncTestPull --console=plain --stacktrace ./gradlew test --tests 'PullTest' diff --git a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/ChangeApplier.kt b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/ChangeApplier.kt new file mode 100644 index 0000000000..ded19b84e9 --- /dev/null +++ b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/ChangeApplier.kt @@ -0,0 +1,74 @@ +/* + * 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.sync.bulk.gradle.test + +import GraphLang.L_GraphLang +import GraphLang.N_Edge +import GraphLang.N_Node +import GraphLang._C_UntypedImpl_Edge +import GraphLang._C_UntypedImpl_Node +import jetbrains.mps.lang.core.L_jetbrains_mps_lang_core +import kotlinx.coroutines.runBlocking +import org.modelix.metamodel.TypedLanguagesRegistry +import org.modelix.metamodel.typed +import org.modelix.model.ModelFacade +import org.modelix.model.api.ConceptReference +import org.modelix.model.api.getDescendants +import org.modelix.model.client2.ModelClientV2PlatformSpecificBuilder +import org.modelix.model.client2.runWrite +import org.modelix.model.lazy.RepositoryId +import kotlin.test.Test + +/** + * Not an actual test. Just a preparation for [PullTest]. + * Marking it as a test makes it easily callable from the ci script. + */ +class ChangeApplier { + + @Test + fun applyChangesForPullTest() { + val url = "http://0.0.0.0:28309/v2" + val branchRef = ModelFacade.createBranchReference(RepositoryId("ci-test"), "master") + val client = ModelClientV2PlatformSpecificBuilder().url(url).build().apply { runBlocking { init() } } + + TypedLanguagesRegistry.register(L_GraphLang) + TypedLanguagesRegistry.register(L_jetbrains_mps_lang_core) + + runBlocking { + client.runWrite(branchRef) { rootNode -> + val graphNodes = rootNode + .getDescendants(false) + .filter { it.getConceptReference() == ConceptReference(_C_UntypedImpl_Node.getUID()) } + .map { it.typed() } + .toList() + + graphNodes[0].name = "X" + graphNodes[1].name = "Y" + graphNodes[2].name = "Z" + + val edges = rootNode + .getDescendants(false) + .filter { it.getConceptReference() == ConceptReference(_C_UntypedImpl_Edge.getUID()) } + .map { it.typed() } + .toList() + + edges[0].source = graphNodes[1] + edges[0].target = graphNodes[3] + } + } + } +} diff --git a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PullTest.kt b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PullTest.kt index a5f08bbae5..6ca3e1a2de 100644 --- a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PullTest.kt +++ b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PullTest.kt @@ -16,25 +16,18 @@ package org.modelix.model.sync.bulk.gradle.test -import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance import org.xmlunit.builder.Input import org.xmlunit.xpath.JAXPXPathEngine import java.io.File -import javax.xml.transform.Source import kotlin.test.assertContentEquals +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class PullTest { - companion object { - private lateinit var source: Source - - @JvmStatic - @BeforeAll - fun initSource() { - val localModel = File("build/test-repo/solutions/GraphSolution/models/GraphSolution.example.mps").readText() - source = Input.fromString(localModel).build() - } - } + + private val localModel = File("build/test-repo/solutions/GraphSolution/models/GraphSolution.example.mps").readText() + private val source = Input.fromString(localModel).build() @Test fun `properties were synced to local`() { diff --git a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PushTest.kt b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PushTest.kt index bac56c11c1..2f51e3daba 100644 --- a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PushTest.kt +++ b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PushTest.kt @@ -1,32 +1,24 @@ package org.modelix.model.sync.bulk.gradle.test -import GraphLang.L_GraphLang -import GraphLang.N_Edge -import GraphLang.N_Node -import GraphLang._C_UntypedImpl_Edge -import GraphLang._C_UntypedImpl_Node -import jetbrains.mps.lang.core.L_jetbrains_mps_lang_core import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Test -import org.modelix.metamodel.TypedLanguagesRegistry -import org.modelix.metamodel.typed +import org.junit.jupiter.api.TestInstance import org.modelix.model.ModelFacade -import org.modelix.model.api.ConceptReference -import org.modelix.model.api.getDescendants import org.modelix.model.api.getRootNode -import org.modelix.model.client2.IModelClientV2 import org.modelix.model.client2.ModelClientV2PlatformSpecificBuilder import org.modelix.model.client2.getReplicatedModel -import org.modelix.model.client2.runWrite import org.modelix.model.data.ModelData import org.modelix.model.data.NodeData -import org.modelix.model.lazy.BranchReference import org.modelix.model.lazy.RepositoryId import org.modelix.model.sync.bulk.asExported import java.io.File import kotlin.test.assertContentEquals +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class PushTest { + private val url = "http://0.0.0.0:28309/v2" + private val branchRef = ModelFacade.createBranchReference(RepositoryId("ci-test"), "master") + private val client = ModelClientV2PlatformSpecificBuilder().url(url).build().apply { runBlocking { init() } } @Test fun `nodes were synced to server`() { @@ -36,15 +28,6 @@ class PushTest { val modules = files.map { ModelData.fromJson(it.readText()) } val inputModel = ModelData(root = NodeData(children = modules.map { it.root })) - TypedLanguagesRegistry.register(L_GraphLang) - TypedLanguagesRegistry.register(L_jetbrains_mps_lang_core) - - val repoId = RepositoryId("ci-test") - val branchName = "master" - val url = "http://0.0.0.0:28309/v2" - - val branchRef = ModelFacade.createBranchReference(repoId, branchName) - val client = ModelClientV2PlatformSpecificBuilder().url(url).build().apply { runBlocking { init() } } val replicatedModel = client.getReplicatedModel(branchRef) val branch = runBlocking { replicatedModel.start() } @@ -52,32 +35,5 @@ class PushTest { assertContentEquals(inputModel.root.children, branch.getRootNode().allChildren.map { it.asExported() }) } replicatedModel.dispose() - - applyChangesForPullTest(client, branchRef) - } - - private fun applyChangesForPullTest(client: IModelClientV2, branchRef: BranchReference) { - runBlocking { - client.runWrite(branchRef) { rootNode -> - val graphNodes = rootNode - .getDescendants(false) - .filter { it.getConceptReference() == ConceptReference(_C_UntypedImpl_Node.getUID()) } - .map { it.typed() } - .toList() - - graphNodes[0].name = "X" - graphNodes[1].name = "Y" - graphNodes[2].name = "Z" - - val edges = rootNode - .getDescendants(false) - .filter { it.getConceptReference() == ConceptReference(_C_UntypedImpl_Edge.getUID()) } - .map { it.typed() } - .toList() - - edges[0].source = graphNodes[1] - edges[0].target = graphNodes[3] - } - } } } From 390a9ca52edf2cca060c6d1cf76c228e667dc668 Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Tue, 6 Feb 2024 12:58:59 +0100 Subject: [PATCH 5/6] test(bulk-model-sync-gradle): test for meta properties --- bulk-model-sync-gradle-test/build.gradle.kts | 2 ++ .../model/sync/bulk/gradle/test/PushTest.kt | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/bulk-model-sync-gradle-test/build.gradle.kts b/bulk-model-sync-gradle-test/build.gradle.kts index fe78848735..9eb3d7c3b0 100644 --- a/bulk-model-sync-gradle-test/build.gradle.kts +++ b/bulk-model-sync-gradle-test/build.gradle.kts @@ -94,6 +94,8 @@ modelSync { url = "http://localhost:28309/v2" repositoryId = "ci-test" branchName = "master" + metaProperties["metaKey1"] = "metaValue1" + metaProperties["metaKey2"] = "metaValue2" } } direction("testPull") { diff --git a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PushTest.kt b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PushTest.kt index 2f51e3daba..d497be665d 100644 --- a/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PushTest.kt +++ b/bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PushTest.kt @@ -4,6 +4,7 @@ import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance import org.modelix.model.ModelFacade +import org.modelix.model.api.IProperty import org.modelix.model.api.getRootNode import org.modelix.model.client2.ModelClientV2PlatformSpecificBuilder import org.modelix.model.client2.getReplicatedModel @@ -13,6 +14,7 @@ import org.modelix.model.lazy.RepositoryId import org.modelix.model.sync.bulk.asExported import java.io.File import kotlin.test.assertContentEquals +import kotlin.test.assertEquals @TestInstance(TestInstance.Lifecycle.PER_CLASS) class PushTest { @@ -36,4 +38,18 @@ class PushTest { } replicatedModel.dispose() } + + @Test + fun `meta properties were applied to root node`() { + val replicatedModel = client.getReplicatedModel(branchRef) + val branch = runBlocking { replicatedModel.start() } + branch.runRead { + val actual1 = branch.getRootNode().getPropertyValue(IProperty.fromName("metaKey1")) + val actual2 = branch.getRootNode().getPropertyValue(IProperty.fromName("metaKey2")) + + assertEquals("metaValue1", actual1) + assertEquals("metaValue2", actual2) + } + replicatedModel.dispose() + } } From 8edc5c4c06f4f2cf11f2a07473108288fcf8d47f Mon Sep 17 00:00:00 2001 From: Michael Huster Date: Tue, 6 Feb 2024 16:21:29 +0100 Subject: [PATCH 6/6] fix(bulk-model-sync-gradle): avoid overwriting existing properties --- .../model/sync/bulk/gradle/tasks/ImportIntoModelServer.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ImportIntoModelServer.kt b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ImportIntoModelServer.kt index 2e8f2431b4..de70cc19af 100644 --- a/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ImportIntoModelServer.kt +++ b/bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ImportIntoModelServer.kt @@ -99,7 +99,10 @@ abstract class ImportIntoModelServer @Inject constructor(of: ObjectFactory) : De logger.info("Setting meta properties...") for ((key, value) in metaProperties.get()) { - rootNode.setPropertyValue(IProperty.fromName(key), value) + val property = IProperty.fromName(key) + if (rootNode.getPropertyValue(property) == null) { + rootNode.setPropertyValue(property, value) + } } } logger.info("Sending diff to server...")