Skip to content

Commit 274c292

Browse files
authored
Merge pull request #86 from ergon/feature/dope-180-seperation-between-collection-and-scope
DOPE-180: seperation between collection and scope
2 parents 650e96f + 20839c2 commit 274c292

32 files changed

Lines changed: 467 additions & 171 deletions

File tree

.github/workflows/kotlin_ci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,10 @@ jobs:
3434
run: ./gradlew ktlintCheck
3535
- name: Run unit tests
3636
run: ./gradlew test
37+
- name: Docker diagnostics
38+
run: |
39+
docker version
40+
docker info
41+
ls -l /var/run/docker.sock
3742
- name: Run integration tests
3843
run: ./gradlew integrationTest

core/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ repositories {
3434

3535
dependencies {
3636
testImplementation(kotlin("test"))
37-
testImplementation("org.testcontainers:couchbase:1.20.3")
37+
testImplementation("org.testcontainers:couchbase:1.21.4")
3838
implementation(kotlin("reflect"))
3939
}
4040

core/src/main/kotlin/ch/ergon/dope/resolvable/bucket/Bucket.kt

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,50 @@ import ch.ergon.dope.resolvable.Updatable
99
import ch.ergon.dope.resolvable.expression.SingleExpression
1010
import ch.ergon.dope.validtype.ObjectType
1111

12-
sealed class Bucket(open val name: String) : Fromable, Joinable, Nestable, Deletable, Updatable, SingleExpression<ObjectType>
12+
sealed interface Bucket : Fromable, Joinable, Nestable, Deletable, Updatable, SingleExpression<ObjectType> {
13+
val name: String
14+
val scope: BucketScope?
15+
}
16+
17+
data class BucketScope(val name: String, val collection: ScopeCollection? = null) {
18+
fun withCollection(collectionName: String) = copy(collection = ScopeCollection(collectionName))
19+
20+
fun withCollection(collection: ScopeCollection) = copy(collection = collection)
21+
}
22+
23+
data class ScopeCollection(val name: String)
24+
25+
data class UnaliasedBucket(
26+
override val name: String,
27+
override val scope: BucketScope? = null,
28+
) : Bucket {
29+
30+
fun alias(alias: String) = AliasedBucket(name, alias, scope)
31+
32+
fun withScope(scopeName: String) = copy(scope = BucketScope(scopeName))
1333

14-
data class UnaliasedBucket(override val name: String) : Bucket(name) {
15-
fun alias(alias: String) = AliasedBucket(name, alias)
34+
fun withScope(scope: BucketScope) = copy(scope = scope)
35+
fun withScopeAndCollection(scopeName: String, collectionName: String) =
36+
copy(scope = BucketScope(scopeName, ScopeCollection(collectionName)))
1637
}
1738

18-
data class AliasedBucket(override val name: String, val alias: String) : Bucket(name) {
19-
fun asBucketDefinition() = AliasedBucketDefinition(name, alias)
39+
data class AliasedBucket(
40+
override val name: String,
41+
val alias: String,
42+
override val scope: BucketScope? = null,
43+
) : Bucket {
44+
fun asBucketDefinition(): AliasedBucketDefinition =
45+
AliasedBucketDefinition(
46+
bucketName = name,
47+
scopeName = scope?.name,
48+
collectionName = scope?.collection?.name,
49+
alias = alias,
50+
)
2051
}
2152

22-
data class AliasedBucketDefinition(val name: String, val alias: String) : Resolvable
53+
data class AliasedBucketDefinition(
54+
val bucketName: String,
55+
val scopeName: String? = null,
56+
val collectionName: String? = null,
57+
val alias: String,
58+
) : Resolvable

core/src/main/kotlin/ch/ergon/dope/resolvable/bucket/UseKeys.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import ch.ergon.dope.resolvable.expression.type.toDopeType
1111
import ch.ergon.dope.validtype.ArrayType
1212
import ch.ergon.dope.validtype.StringType
1313
import ch.ergon.dope.validtype.ValidType
14+
import kotlin.collections.Collection
1415

1516
data class UseKeysClass private constructor(
1617
val useKeys: TypeExpression<out ValidType>,
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package ch.ergon.dope.resolvable.expression.type
22

3+
import ch.ergon.dope.resolvable.bucket.Bucket
34
import ch.ergon.dope.validtype.ValidType
45

56
interface IField<T : ValidType> : TypeExpression<T> {
67
val name: String
7-
val path: String
8+
val bucket: Bucket?
89
}
910

10-
data class Field<T : ValidType>(override val name: String, override val path: String) : IField<T>
11+
data class Field<T : ValidType>(override val name: String, override val bucket: Bucket? = null) : IField<T>

core/src/test/kotlin/ch/ergon/dope/resolvable/clause/NestDslTest.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class NestDslTest {
3838
@Test
3939
fun `should support standard nest with keys`() {
4040
val nestable = UnaliasedBucket("nest")
41-
val keys = Field<ArrayType<StringType>>("keys", "")
41+
val keys = Field<ArrayType<StringType>>("keys")
4242
val parentClause = parentClause()
4343
val expected = StandardNestOnKeysClause(nestable, keys = keys, parentClause = parentClause)
4444

@@ -62,7 +62,7 @@ class NestDslTest {
6262
@Test
6363
fun `should support standard nest with key`() {
6464
val nestable = UnaliasedBucket("nest")
65-
val key = Field<StringType>("key", "")
65+
val key = Field<StringType>("key")
6666
val parentClause = parentClause()
6767
val expected = StandardNestOnKeyClause(nestable, key = key, bucket = null, parentClause = parentClause)
6868

@@ -86,7 +86,7 @@ class NestDslTest {
8686
fun `should support standard nest with key for bucket`() {
8787
val nestable = UnaliasedBucket("nest")
8888
val bucket = UnaliasedBucket("bucket")
89-
val key = Field<StringType>("key", "")
89+
val key = Field<StringType>("key")
9090
val parentClause = parentClause()
9191
val expected = StandardNestOnKeyClause(nestable, key = key, bucket = bucket, parentClause = parentClause)
9292

@@ -109,7 +109,7 @@ class NestDslTest {
109109
@Test
110110
fun `should support inner nest with keys`() {
111111
val nestable = UnaliasedBucket("nest")
112-
val keys = Field<ArrayType<StringType>>("keys", "")
112+
val keys = Field<ArrayType<StringType>>("keys")
113113
val parentClause = parentClause()
114114
val expected = InnerNestOnKeysClause(nestable, keys = keys, parentClause = parentClause)
115115

@@ -133,7 +133,7 @@ class NestDslTest {
133133
@Test
134134
fun `should support inner nest with key`() {
135135
val nestable = UnaliasedBucket("nest")
136-
val key = Field<StringType>("key", "")
136+
val key = Field<StringType>("key")
137137
val parentClause = parentClause()
138138
val expected = InnerNestOnKeyClause(nestable, key = key, bucket = null, parentClause = parentClause)
139139

@@ -157,7 +157,7 @@ class NestDslTest {
157157
fun `should support inner nest with key for bucket`() {
158158
val nestable = UnaliasedBucket("nest")
159159
val bucket = UnaliasedBucket("bucket")
160-
val key = Field<StringType>("key", "")
160+
val key = Field<StringType>("key")
161161
val parentClause = parentClause()
162162
val expected = InnerNestOnKeyClause(nestable, key = key, bucket = bucket, parentClause = parentClause)
163163

@@ -180,7 +180,7 @@ class NestDslTest {
180180
@Test
181181
fun `should support left nest with keys`() {
182182
val nestable = UnaliasedBucket("nest")
183-
val keys = Field<ArrayType<StringType>>("keys", "")
183+
val keys = Field<ArrayType<StringType>>("keys")
184184
val parentClause = parentClause()
185185
val expected = LeftNestOnKeysClause(nestable, keys = keys, parentClause = parentClause)
186186

@@ -204,7 +204,7 @@ class NestDslTest {
204204
@Test
205205
fun `should support left nest with key`() {
206206
val nestable = UnaliasedBucket("nest")
207-
val key = Field<StringType>("key", "")
207+
val key = Field<StringType>("key")
208208
val parentClause = parentClause()
209209
val expected = LeftNestOnKeyClause(nestable, key = key, bucket = null, parentClause = parentClause)
210210

@@ -228,7 +228,7 @@ class NestDslTest {
228228
fun `should support left nest with key for bucket`() {
229229
val nestable = UnaliasedBucket("nest")
230230
val bucket = UnaliasedBucket("bucket")
231-
val key = Field<StringType>("key", "")
231+
val key = Field<StringType>("key")
232232
val parentClause = parentClause()
233233
val expected = LeftNestOnKeyClause(nestable, key = key, bucket = bucket, parentClause = parentClause)
234234

couchbase/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ dependencies {
3636
implementation(project(":core"))
3737
testImplementation(kotlin("test"))
3838
testImplementation("com.couchbase.client:kotlin-client:1.5.0")
39-
testImplementation("org.testcontainers:couchbase:1.20.3")
39+
testImplementation("org.testcontainers:couchbase:1.21.4")
4040
implementation(kotlin("reflect"))
4141
}
4242

couchbase/src/integrationTest/kotlin/ch/ergon/dope/clauses/FromIntegrationTest.kt

Lines changed: 59 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import ch.ergon.dope.QueryBuilder
44
import ch.ergon.dope.couchbase.resolvable.expression.type.meta
55
import ch.ergon.dope.couchbase.resolver.CouchbaseResolver
66
import ch.ergon.dope.integrationTest.BaseIntegrationTest
7+
import ch.ergon.dope.integrationTest.TestCouchbaseDatabase.testAppOrderAuditBucket
78
import ch.ergon.dope.integrationTest.TestCouchbaseDatabase.testBucket
89
import ch.ergon.dope.integrationTest.toMapValues
910
import ch.ergon.dope.integrationTest.tryUntil
@@ -27,9 +28,9 @@ class FromIntegrationTest : BaseIntegrationTest() {
2728
fun `join all orders on employee id`() {
2829
val employeeAlias = testBucket.alias("e")
2930
val orderAlias = testBucket.alias("o")
30-
val employeeNameField = Field<StringType>("name", employeeAlias.alias)
31-
val orderNumberField = Field<StringType>("orderNumber", orderAlias.alias)
32-
val orderEmployeeIdField = Field<StringType>("employee", orderAlias.alias)
31+
val employeeNameField = Field<StringType>("name", employeeAlias)
32+
val orderNumberField = Field<StringType>("orderNumber", orderAlias)
33+
val orderEmployeeIdField = Field<StringType>("employee", orderAlias)
3334
val dopeQuery = QueryBuilder
3435
.select(
3536
employeeNameField,
@@ -58,16 +59,54 @@ class FromIntegrationTest : BaseIntegrationTest() {
5859
}
5960
}
6061

62+
@Test
63+
fun `join default orders with order_audit collection`() {
64+
val orderAlias = testBucket.withScopeAndCollection("_default", "_default").alias("o")
65+
val orderNumberField = Field<StringType>("orderNumber", orderAlias)
66+
67+
val auditAlias = testAppOrderAuditBucket.alias("a")
68+
val auditOrderRefField = Field<StringType>("orderRef", auditAlias)
69+
val auditStatusField = Field<StringType>("status", auditAlias)
70+
71+
val dopeQuery = QueryBuilder
72+
.select(
73+
orderNumberField,
74+
auditStatusField,
75+
)
76+
.from(orderAlias)
77+
.join(
78+
auditAlias,
79+
auditOrderRefField.isEqualTo(meta(orderAlias).id),
80+
)
81+
.orderBy(orderNumberField, ASC)
82+
.build(CouchbaseResolver())
83+
84+
println(dopeQuery)
85+
86+
tryUntil {
87+
val queryResult = queryWithoutParameters(dopeQuery)
88+
println(queryResult.rows)
89+
90+
assertEquals(5, queryResult.rows.size)
91+
92+
assertEquals(mapOf("orderNumber" to "order1", "status" to "CREATED"), queryResult.toMapValues(rowNumber = 0))
93+
assertEquals(mapOf("orderNumber" to "order2", "status" to "CREATED"), queryResult.toMapValues(rowNumber = 1))
94+
assertEquals(mapOf("orderNumber" to "order3", "status" to "CREATED"), queryResult.toMapValues(rowNumber = 2))
95+
assertEquals(mapOf("orderNumber" to "order4", "status" to "CREATED"), queryResult.toMapValues(rowNumber = 3))
96+
assertEquals(mapOf("orderNumber" to "order5", "status" to "CREATED"), queryResult.toMapValues(rowNumber = 4))
97+
}
98+
}
99+
61100
@Test
62101
fun `join documents with multiple joins`() {
63102
val employeeAlias = testBucket.alias("e")
64103
val clientAlias = testBucket.alias("c")
65104
val orderAlias = testBucket.alias("o")
66-
val employeeNameField = Field<StringType>("name", employeeAlias.alias)
67-
val clientNameField = Field<StringType>("name", clientAlias.alias)
68-
val orderNumberField = Field<StringType>("orderNumber", orderAlias.alias)
69-
val orderEmployeeIdField = Field<StringType>("employee", orderAlias.alias)
70-
val orderClientIdField = Field<StringType>("client", orderAlias.alias)
105+
val employeeNameField = Field<StringType>("name", employeeAlias)
106+
val clientNameField = Field<StringType>("name", clientAlias)
107+
val orderNumberField = Field<StringType>("orderNumber", orderAlias)
108+
val orderEmployeeIdField = Field<StringType>("employee", orderAlias)
109+
val orderClientIdField = Field<StringType>("client", orderAlias)
71110
val dopeQuery = QueryBuilder
72111
.select(
73112
orderNumberField,
@@ -122,21 +161,21 @@ class FromIntegrationTest : BaseIntegrationTest() {
122161
@Test
123162
fun `use multiple from terms`() {
124163
val e = testBucket.alias("e")
125-
val eIdField = Field<NumberType>("id", e.alias)
126-
val eTypeField = Field<StringType>("type", e.alias)
127-
val eIsActiveField = Field<BooleanType>("isActive", e.alias)
164+
val eIdField = Field<NumberType>("id", e)
165+
val eTypeField = Field<StringType>("type", e)
166+
val eIsActiveField = Field<BooleanType>("isActive", e)
128167
val c = testBucket.alias("c")
129-
val cIdField = Field<NumberType>("id", c.alias)
130-
val cTypeField = Field<StringType>("type", c.alias)
131-
val cIsActiveField = Field<BooleanType>("isActive", c.alias)
168+
val cIdField = Field<NumberType>("id", c)
169+
val cTypeField = Field<StringType>("type", c)
170+
val cIsActiveField = Field<BooleanType>("isActive", c)
132171
val o = testBucket.alias("o")
133-
val oClientField = Field<StringType>("client", o.alias)
134-
val oTypeField = Field<StringType>("type", o.alias)
135-
val oOrderNumberField = Field<NumberType>("orderNumber", o.alias)
136-
val oQuantitiesField = Field<ArrayType<NumberType>>("quantities", o.alias)
172+
val oClientField = Field<StringType>("client", o)
173+
val oTypeField = Field<StringType>("type", o)
174+
val oOrderNumberField = Field<NumberType>("orderNumber", o)
175+
val oQuantitiesField = Field<ArrayType<NumberType>>("quantities", o)
137176
val nestedOrders = testBucket.alias("nestedOrders")
138-
val nestedOrdersEmployeeField = Field<StringType>("employee", nestedOrders.alias)
139-
val nestedOrdersTypeField = Field<StringType>("type", nestedOrders.alias)
177+
val nestedOrdersEmployeeField = Field<StringType>("employee", nestedOrders)
178+
val nestedOrdersTypeField = Field<StringType>("type", nestedOrders)
140179

141180
val dopeQuery = QueryBuilder
142181
.selectDistinct(

couchbase/src/integrationTest/kotlin/ch/ergon/dope/clauses/UpdateIntegrationTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ class UpdateIntegrationTest : BaseIntegrationTest() {
2727

2828
@Test
2929
fun `update to set and unset single attribute`() {
30-
val newFieldName = Field<StringType>("newFieldName", testBucket.name)
31-
val newNullField = Field<NumberType>("nullField", testBucket.name)
30+
val newFieldName = Field<StringType>("newFieldName", testBucket)
31+
val newNullField = Field<NumberType>("nullField", testBucket)
3232
val dopeQuery = QueryBuilder
3333
.update(
3434
testBucket.useKeys("client:1"),

couchbase/src/integrationTest/kotlin/ch/ergon/dope/functions/ConditionalFunctionsIntegrationTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class ConditionalFunctionsIntegrationTest : BaseIntegrationTest() {
4747

4848
@Test
4949
fun `return first non null, non missing value`() {
50-
val missingField = Field<StringType>("nonexistent", testBucket.name)
50+
val missingField = Field<StringType>("nonexistent", testBucket)
5151
val dopeQuery = QueryBuilder
5252
.select(
5353
ifMissingOrNull(

0 commit comments

Comments
 (0)