Skip to content

Commit e9e12f1

Browse files
authored
Merge pull request #488 from modelix/feature/bulk-sync-git-metadata
MODELIX-718 Store Git metadata when syncing
2 parents a5721c8 + 8edc5c4 commit e9e12f1

File tree

9 files changed

+128
-62
lines changed

9 files changed

+128
-62
lines changed

bulk-model-sync-gradle-test/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ modelSync {
9494
url = "http://localhost:28309/v2"
9595
repositoryId = "ci-test"
9696
branchName = "master"
97+
metaProperties["metaKey1"] = "metaValue1"
98+
metaProperties["metaKey2"] = "metaValue2"
9799
}
98100
}
99101
direction("testPull") {

bulk-model-sync-gradle-test/ci.sh

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
set -e
44
set -x
55

6-
cd "$(dirname "$0")"
6+
TEST_DIR="$(dirname "$(readlink -f "$0")")"
7+
cd "${TEST_DIR}"
78

89
(
910
cd graph-lang-api
@@ -20,15 +21,16 @@ if [ "${CI}" != "true" ]; then
2021
}
2122
fi
2223

23-
cd ..
24+
cd "${TEST_DIR}/.."
2425
./gradlew :model-server:run --console=plain --args="-inmemory -port 28309" &
2526
MODEL_SERVER_PID=$!
2627

27-
cd "$(dirname "$0")"
28+
cd "${TEST_DIR}"
2829

2930
curl -X GET --retry 30 --retry-connrefused --retry-delay 1 http://localhost:28309/health
3031

3132
./gradlew runSyncTestPush --console=plain --stacktrace
3233
./gradlew test --tests 'PushTest'
34+
./gradlew test --tests 'ChangeApplier'
3335
./gradlew runSyncTestPull --console=plain --stacktrace
3436
./gradlew test --tests 'PullTest'
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright (c) 2024.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.modelix.model.sync.bulk.gradle.test
18+
19+
import GraphLang.L_GraphLang
20+
import GraphLang.N_Edge
21+
import GraphLang.N_Node
22+
import GraphLang._C_UntypedImpl_Edge
23+
import GraphLang._C_UntypedImpl_Node
24+
import jetbrains.mps.lang.core.L_jetbrains_mps_lang_core
25+
import kotlinx.coroutines.runBlocking
26+
import org.modelix.metamodel.TypedLanguagesRegistry
27+
import org.modelix.metamodel.typed
28+
import org.modelix.model.ModelFacade
29+
import org.modelix.model.api.ConceptReference
30+
import org.modelix.model.api.getDescendants
31+
import org.modelix.model.client2.ModelClientV2PlatformSpecificBuilder
32+
import org.modelix.model.client2.runWrite
33+
import org.modelix.model.lazy.RepositoryId
34+
import kotlin.test.Test
35+
36+
/**
37+
* Not an actual test. Just a preparation for [PullTest].
38+
* Marking it as a test makes it easily callable from the ci script.
39+
*/
40+
class ChangeApplier {
41+
42+
@Test
43+
fun applyChangesForPullTest() {
44+
val url = "http://0.0.0.0:28309/v2"
45+
val branchRef = ModelFacade.createBranchReference(RepositoryId("ci-test"), "master")
46+
val client = ModelClientV2PlatformSpecificBuilder().url(url).build().apply { runBlocking { init() } }
47+
48+
TypedLanguagesRegistry.register(L_GraphLang)
49+
TypedLanguagesRegistry.register(L_jetbrains_mps_lang_core)
50+
51+
runBlocking {
52+
client.runWrite(branchRef) { rootNode ->
53+
val graphNodes = rootNode
54+
.getDescendants(false)
55+
.filter { it.getConceptReference() == ConceptReference(_C_UntypedImpl_Node.getUID()) }
56+
.map { it.typed<N_Node>() }
57+
.toList()
58+
59+
graphNodes[0].name = "X"
60+
graphNodes[1].name = "Y"
61+
graphNodes[2].name = "Z"
62+
63+
val edges = rootNode
64+
.getDescendants(false)
65+
.filter { it.getConceptReference() == ConceptReference(_C_UntypedImpl_Edge.getUID()) }
66+
.map { it.typed<N_Edge>() }
67+
.toList()
68+
69+
edges[0].source = graphNodes[1]
70+
edges[0].target = graphNodes[3]
71+
}
72+
}
73+
}
74+
}

bulk-model-sync-gradle-test/src/test/kotlin/org/modelix/model/sync/bulk/gradle/test/PullTest.kt

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,18 @@
1616

1717
package org.modelix.model.sync.bulk.gradle.test
1818

19-
import org.junit.jupiter.api.BeforeAll
2019
import org.junit.jupiter.api.Test
20+
import org.junit.jupiter.api.TestInstance
2121
import org.xmlunit.builder.Input
2222
import org.xmlunit.xpath.JAXPXPathEngine
2323
import java.io.File
24-
import javax.xml.transform.Source
2524
import kotlin.test.assertContentEquals
2625

26+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
2727
class PullTest {
28-
companion object {
29-
private lateinit var source: Source
30-
31-
@JvmStatic
32-
@BeforeAll
33-
fun initSource() {
34-
val localModel = File("build/test-repo/solutions/GraphSolution/models/GraphSolution.example.mps").readText()
35-
source = Input.fromString(localModel).build()
36-
}
37-
}
28+
29+
private val localModel = File("build/test-repo/solutions/GraphSolution/models/GraphSolution.example.mps").readText()
30+
private val source = Input.fromString(localModel).build()
3831

3932
@Test
4033
fun `properties were synced to local`() {
Lines changed: 17 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,26 @@
11
package org.modelix.model.sync.bulk.gradle.test
22

3-
import GraphLang.L_GraphLang
4-
import GraphLang.N_Edge
5-
import GraphLang.N_Node
6-
import GraphLang._C_UntypedImpl_Edge
7-
import GraphLang._C_UntypedImpl_Node
8-
import jetbrains.mps.lang.core.L_jetbrains_mps_lang_core
93
import kotlinx.coroutines.runBlocking
104
import org.junit.jupiter.api.Test
11-
import org.modelix.metamodel.TypedLanguagesRegistry
12-
import org.modelix.metamodel.typed
5+
import org.junit.jupiter.api.TestInstance
136
import org.modelix.model.ModelFacade
14-
import org.modelix.model.api.ConceptReference
15-
import org.modelix.model.api.getDescendants
7+
import org.modelix.model.api.IProperty
168
import org.modelix.model.api.getRootNode
17-
import org.modelix.model.client2.IModelClientV2
189
import org.modelix.model.client2.ModelClientV2PlatformSpecificBuilder
1910
import org.modelix.model.client2.getReplicatedModel
20-
import org.modelix.model.client2.runWrite
2111
import org.modelix.model.data.ModelData
2212
import org.modelix.model.data.NodeData
23-
import org.modelix.model.lazy.BranchReference
2413
import org.modelix.model.lazy.RepositoryId
2514
import org.modelix.model.sync.bulk.asExported
2615
import java.io.File
2716
import kotlin.test.assertContentEquals
17+
import kotlin.test.assertEquals
2818

19+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
2920
class PushTest {
21+
private val url = "http://0.0.0.0:28309/v2"
22+
private val branchRef = ModelFacade.createBranchReference(RepositoryId("ci-test"), "master")
23+
private val client = ModelClientV2PlatformSpecificBuilder().url(url).build().apply { runBlocking { init() } }
3024

3125
@Test
3226
fun `nodes were synced to server`() {
@@ -36,48 +30,26 @@ class PushTest {
3630
val modules = files.map { ModelData.fromJson(it.readText()) }
3731
val inputModel = ModelData(root = NodeData(children = modules.map { it.root }))
3832

39-
TypedLanguagesRegistry.register(L_GraphLang)
40-
TypedLanguagesRegistry.register(L_jetbrains_mps_lang_core)
41-
42-
val repoId = RepositoryId("ci-test")
43-
val branchName = "master"
44-
val url = "http://0.0.0.0:28309/v2"
45-
46-
val branchRef = ModelFacade.createBranchReference(repoId, branchName)
47-
val client = ModelClientV2PlatformSpecificBuilder().url(url).build().apply { runBlocking { init() } }
4833
val replicatedModel = client.getReplicatedModel(branchRef)
4934
val branch = runBlocking { replicatedModel.start() }
5035

5136
branch.runRead {
5237
assertContentEquals(inputModel.root.children, branch.getRootNode().allChildren.map { it.asExported() })
5338
}
5439
replicatedModel.dispose()
55-
56-
applyChangesForPullTest(client, branchRef)
5740
}
5841

59-
private fun applyChangesForPullTest(client: IModelClientV2, branchRef: BranchReference) {
60-
runBlocking {
61-
client.runWrite(branchRef) { rootNode ->
62-
val graphNodes = rootNode
63-
.getDescendants(false)
64-
.filter { it.getConceptReference() == ConceptReference(_C_UntypedImpl_Node.getUID()) }
65-
.map { it.typed<N_Node>() }
66-
.toList()
67-
68-
graphNodes[0].name = "X"
69-
graphNodes[1].name = "Y"
70-
graphNodes[2].name = "Z"
71-
72-
val edges = rootNode
73-
.getDescendants(false)
74-
.filter { it.getConceptReference() == ConceptReference(_C_UntypedImpl_Edge.getUID()) }
75-
.map { it.typed<N_Edge>() }
76-
.toList()
42+
@Test
43+
fun `meta properties were applied to root node`() {
44+
val replicatedModel = client.getReplicatedModel(branchRef)
45+
val branch = runBlocking { replicatedModel.start() }
46+
branch.runRead {
47+
val actual1 = branch.getRootNode().getPropertyValue(IProperty.fromName("metaKey1"))
48+
val actual2 = branch.getRootNode().getPropertyValue(IProperty.fromName("metaKey2"))
7749

78-
edges[0].source = graphNodes[1]
79-
edges[0].target = graphNodes[3]
80-
}
50+
assertEquals("metaValue1", actual1)
51+
assertEquals("metaValue2", actual2)
8152
}
53+
replicatedModel.dispose()
8254
}
8355
}

bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/ModelSyncGradlePlugin.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ class ModelSyncGradlePlugin : Plugin<Project> {
161161
it.includedModulePrefixes.set(syncDirection.includedModulePrefixes)
162162
it.continueOnError.set(syncDirection.continueOnError)
163163
it.requestTimeoutSeconds.set(serverTarget.requestTimeoutSeconds)
164+
it.metaProperties.set(serverTarget.metaProperties)
164165
}
165166

166167
project.tasks.register("runSync${syncDirection.name.replaceFirstChar { it.uppercaseChar() }}") {

bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/config/ModelSyncGradleSettings.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ data class ServerTarget(
184184
override var repositoryId: String? = null,
185185
override var branchName: String? = null,
186186
override var requestTimeoutSeconds: Int = DEFAULT_REQUEST_TIMEOUT_SECONDS,
187+
val metaProperties: MutableMap<String, String> = mutableMapOf(),
187188
) : ServerEndpoint {
188189
override fun getValidationErrors(): List<String> {
189190
val errors = mutableListOf<String>()

bulk-model-sync-gradle/src/main/kotlin/org/modelix/model/sync/bulk/gradle/tasks/ImportIntoModelServer.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import kotlinx.coroutines.runBlocking
2020
import org.gradle.api.DefaultTask
2121
import org.gradle.api.file.DirectoryProperty
2222
import org.gradle.api.model.ObjectFactory
23+
import org.gradle.api.provider.MapProperty
2324
import org.gradle.api.provider.Property
2425
import org.gradle.api.provider.SetProperty
2526
import org.gradle.api.tasks.Input
@@ -29,6 +30,7 @@ import org.gradle.api.tasks.PathSensitivity
2930
import org.gradle.api.tasks.TaskAction
3031
import org.modelix.model.ModelFacade
3132
import org.modelix.model.api.INode
33+
import org.modelix.model.api.IProperty
3234
import org.modelix.model.api.PNodeAdapter
3335
import org.modelix.model.client2.ModelClientV2
3436
import org.modelix.model.client2.runWrite
@@ -67,6 +69,9 @@ abstract class ImportIntoModelServer @Inject constructor(of: ObjectFactory) : De
6769
@Input
6870
val requestTimeoutSeconds: Property<Int> = of.property(Int::class.java)
6971

72+
@Input
73+
val metaProperties: MapProperty<String, String> = of.mapProperty(String::class.java, String::class.java)
74+
7075
@TaskAction
7176
fun import() {
7277
val inputDir = inputDir.get().asFile
@@ -91,6 +96,14 @@ abstract class ImportIntoModelServer @Inject constructor(of: ObjectFactory) : De
9196
logger.info("Got root node: {}", rootNode)
9297
logger.info("Calculating diff...")
9398
ModelImporter(rootNode, continueOnError.get()).importFilesAsRootChildren(files)
99+
100+
logger.info("Setting meta properties...")
101+
for ((key, value) in metaProperties.get()) {
102+
val property = IProperty.fromName(key)
103+
if (rootNode.getPropertyValue(property) == null) {
104+
rootNode.setPropertyValue(property, value)
105+
}
106+
}
94107
}
95108
logger.info("Sending diff to server...")
96109
}

docs/global/modules/core/pages/reference/component-bulk-model-sync-gradle.adoc

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,19 @@ If the target branch does not exist on the model-server, it will be created.
137137

138138
|`revision`
139139
|String
140-
|Source model-server revision. Can be used instead of `repositoryId` and `branchName`. Only available in ServerSource.
140+
|Source model-server revision. Can be used instead of `repositoryId` and `branchName`.
141+
Only available in ServerSource.
141142

142143
|`requestTimeoutSeconds`
143144
|Integer
144-
|The request timeout measured in seconds to apply when performing HTTP requests towards the model-server. Default: 5 minutes
145+
|The request timeout measured in seconds to apply when performing HTTP requests towards the model-server.
146+
Default: 5 minutes
147+
148+
|`metaProperties`
149+
|MutableMap<String, String>
150+
|Custom properties that will be attached to the root node.
151+
The mapping is `propertyName -> propertyValue`.
152+
Only available in ServerTarget.
145153

146154
|===
147155

0 commit comments

Comments
 (0)