Skip to content

Commit bb1a04b

Browse files
author
Oleksandr Dzhychko
authored
Merge pull request #715 from modelix/fix/initial-branch-created-through-v2-api-is-visible-in-v1-api
fix(model-server): make initial branch created through V2 API visible in V1 API
2 parents c935a45 + 0679147 commit bb1a04b

File tree

2 files changed

+130
-20
lines changed

2 files changed

+130
-20
lines changed

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

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ class RepositoriesManager(val client: LocalModelClient) : IRepositoriesManager {
6969

7070
fun doMigrations() {
7171
store.runTransaction {
72-
val infoVersionHash = client[RepositoryId("info").getBranchReference().getKey()] ?: return@runTransaction
72+
val v1BranchKey = RepositoryId("info").getBranchReference().getKey()
73+
val infoVersionHash = client[v1BranchKey] ?: return@runTransaction
7374
val infoVersion = CLVersion(infoVersionHash, client.storeCache)
7475
val infoBranch: IBranch = PBranch(infoVersion.getTree(), IdGeneratorDummy())
7576

@@ -124,25 +125,26 @@ class RepositoriesManager(val client: LocalModelClient) : IRepositoriesManager {
124125

125126
private fun repositoryExists(repositoryId: RepositoryId) = getRepositories().contains(repositoryId)
126127

127-
override suspend fun createRepository(repositoryId: RepositoryId, userName: String?, useRoleIds: Boolean): CLVersion {
128-
var initialVersion: CLVersion? = null
129-
store.runTransactionSuspendable {
130-
val masterBranch = repositoryId.getBranchReference()
131-
if (repositoryExists(repositoryId)) throw RepositoryAlreadyExistsException(repositoryId.id)
132-
val existingRepositories = getRepositories()
133-
store.put(REPOSITORIES_LIST_KEY, (existingRepositories + repositoryId).joinToString("\n") { it.id }, false)
134-
store.put(branchListKey(repositoryId), masterBranch.branchName, false)
135-
initialVersion = CLVersion.createRegularVersion(
136-
id = client.idGenerator.generate(),
137-
time = Clock.System.now().epochSeconds.toString(),
138-
author = userName,
139-
tree = CLTree(null, null, client.storeCache, useRoleIds = useRoleIds),
140-
baseVersion = null,
141-
operations = emptyArray(),
142-
)
143-
store.put(branchKey(masterBranch), initialVersion!!.hash, false)
144-
}
145-
return initialVersion!!
128+
override suspend fun createRepository(
129+
repositoryId: RepositoryId,
130+
userName: String?,
131+
useRoleIds: Boolean,
132+
): CLVersion = store.runTransactionSuspendable {
133+
val masterBranch = repositoryId.getBranchReference()
134+
if (repositoryExists(repositoryId)) throw RepositoryAlreadyExistsException(repositoryId.id)
135+
val existingRepositories = getRepositories()
136+
store.put(REPOSITORIES_LIST_KEY, (existingRepositories + repositoryId).joinToString("\n") { it.id }, false)
137+
store.put(branchListKey(repositoryId), masterBranch.branchName, false)
138+
val initialVersion = CLVersion.createRegularVersion(
139+
id = client.idGenerator.generate(),
140+
time = Clock.System.now().epochSeconds.toString(),
141+
author = userName,
142+
tree = CLTree(null, null, client.storeCache, useRoleIds = useRoleIds),
143+
baseVersion = null,
144+
operations = emptyArray(),
145+
)
146+
putVersionHash(masterBranch, initialVersion.hash)
147+
initialVersion
146148
}
147149

148150
fun getBranchNames(repositoryId: RepositoryId): Set<String> {
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing,
9+
* software distributed under the License is distributed on an
10+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11+
* KIND, either express or implied. See the License for the
12+
* specific language governing permissions and limitations
13+
* under the License.
14+
*/
15+
16+
package org.modelix.model.server.handlers
17+
18+
import io.ktor.server.testing.ApplicationTestBuilder
19+
import io.ktor.server.testing.testApplication
20+
import kotlinx.coroutines.CoroutineScope
21+
import kotlinx.coroutines.coroutineScope
22+
import org.modelix.authorization.installAuthentication
23+
import org.modelix.model.InMemoryModels
24+
import org.modelix.model.client.RestWebModelClient
25+
import org.modelix.model.client2.ModelClientV2
26+
import org.modelix.model.lazy.RepositoryId
27+
import org.modelix.model.server.installDefaultServerPlugins
28+
import org.modelix.model.server.store.InMemoryStoreClient
29+
import org.modelix.model.server.store.LocalModelClient
30+
import kotlin.test.Test
31+
import kotlin.test.assertEquals
32+
import kotlin.test.assertNull
33+
34+
class ModelReplicationServerBackwardsCompatibilityTest {
35+
36+
private fun runWithTestModelServer(
37+
block: suspend ApplicationTestBuilder.(scope: CoroutineScope) -> Unit,
38+
) = testApplication {
39+
val storeClient = InMemoryStoreClient()
40+
val modelClient = LocalModelClient(storeClient)
41+
val repositoriesManager = RepositoriesManager(modelClient)
42+
val modelReplicationServer = ModelReplicationServer(repositoriesManager, modelClient, InMemoryModels())
43+
val keyValueLikeModelServer = KeyValueLikeModelServer(repositoriesManager, storeClient, InMemoryModels())
44+
application {
45+
installAuthentication(unitTestMode = true)
46+
installDefaultServerPlugins()
47+
modelReplicationServer.init(this)
48+
keyValueLikeModelServer.init(this)
49+
}
50+
51+
coroutineScope {
52+
block(this)
53+
}
54+
}
55+
56+
@Test
57+
fun `master branch of repository initialized by V2 API is visible through V1 API`() {
58+
val urlV1 = "http://localhost"
59+
val urlV2 = "http://localhost/v2"
60+
val repositoryId = RepositoryId("repo1")
61+
val defaultBranchRef = repositoryId.getBranchReference("master")
62+
63+
runWithTestModelServer {
64+
val modelClientV2 = ModelClientV2
65+
.builder()
66+
.url(urlV2)
67+
.client(client)
68+
.build()
69+
modelClientV2.init()
70+
val modelClientV1 = RestWebModelClient(baseUrl = urlV1, providedClient = client)
71+
72+
val initialVersion = modelClientV2.initRepository(repositoryId)
73+
74+
val branchVersionVisibleInV1 = modelClientV1.getA(defaultBranchRef.getKey())
75+
assertEquals(initialVersion.getContentHash(), branchVersionVisibleInV1)
76+
}
77+
}
78+
79+
@Test
80+
fun `branch is deleted through the V2 API is deleted in the V1 API`() {
81+
val urlV1 = "http://localhost"
82+
val urlV2 = "http://localhost/v2"
83+
val repositoryId = RepositoryId("repo1")
84+
val branchRef = repositoryId.getBranchReference("master")
85+
86+
runWithTestModelServer {
87+
val modelClientV2 = ModelClientV2
88+
.builder()
89+
.url(urlV2)
90+
.client(client)
91+
.build()
92+
modelClientV2.init()
93+
val modelClientV1 = RestWebModelClient(baseUrl = urlV1, providedClient = client)
94+
val initialVersion = modelClientV2.initRepository(repositoryId)
95+
val branchVersionVisibleInV1BeforeDelete = modelClientV1.getA(branchRef.getKey())
96+
assertEquals(
97+
initialVersion.getContentHash(),
98+
branchVersionVisibleInV1BeforeDelete,
99+
"Test setup should create branch in a way that make it visible in the V1 API.",
100+
)
101+
102+
modelClientV2.deleteBranch(branchRef)
103+
104+
val branchVersionVisibleInV1AfterDelete = modelClientV1.getA(branchRef.getKey())
105+
assertNull(branchVersionVisibleInV1AfterDelete)
106+
}
107+
}
108+
}

0 commit comments

Comments
 (0)