Skip to content

Commit a03f867

Browse files
authored
Merge pull request #64 from ergon/feature/dope-239-object-funcitons
DOPE-239: implemented all objectFunctions with extensions for cm and added tests
2 parents 2c5f4c6 + 287241f commit a03f867

File tree

135 files changed

+2019
-256
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

135 files changed

+2019
-256
lines changed

core/src/integrationTest/kotlin/ch/ergon/dope/clauses/SelectQueryIntegrationTest.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,10 @@ class SelectQueryIntegrationTest : BaseIntegrationTest() {
257257

258258
@Test
259259
fun `select with star expression`() {
260+
val contacts = listOf(
261+
mapOf("email" to "[email protected]", "name" to "Contact A"),
262+
mapOf("email" to "[email protected]", "name" to "Contact B"),
263+
)
260264
val dopeQuery = QueryBuilder()
261265
.select(
262266
testBucket.asterisk(),
@@ -271,7 +275,13 @@ class SelectQueryIntegrationTest : BaseIntegrationTest() {
271275

272276
val queryResult = queryWithoutParameters(dopeQuery)
273277

274-
assertEquals(mapOf("id" to 2, "isActive" to true, "name" to "client2", "type" to "client"), queryResult.toMapValues(rowNumber = 0))
275-
assertEquals(mapOf("id" to 4, "isActive" to true, "name" to "client4", "type" to "client"), queryResult.toMapValues(rowNumber = 1))
278+
assertEquals(
279+
mapOf("id" to 2, "isActive" to true, "name" to "client2", "type" to "client", "contacts" to contacts),
280+
queryResult.toMapValues(rowNumber = 0),
281+
)
282+
assertEquals(
283+
mapOf("id" to 4, "isActive" to true, "name" to "client4", "type" to "client", "contacts" to contacts),
284+
queryResult.toMapValues(rowNumber = 1),
285+
)
276286
}
277287
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,21 @@ class UpdateIntegrationTest : BaseIntegrationTest() {
2626

2727
@Test
2828
fun `update to set and unset single attribute`() {
29-
val newField = Field<StringType>("newField", testBucket.name)
29+
val newFieldName = Field<StringType>("newFieldName", testBucket.name)
3030
val newNullField = Field<NumberType>("nullField", testBucket.name)
3131
val dopeQuery = QueryBuilder()
3232
.update(
3333
testBucket.useKeys("client:1"),
3434
)
3535
.set(
36-
newField.toNewValue("newName"),
36+
newFieldName.toNewValue("newName"),
3737
newNullField.toNewValue(NULL),
3838
)
3939
.unset(
4040
nameField,
4141
)
4242
.returning(
43-
newField,
43+
newFieldName,
4444
newNullField,
4545
nameField,
4646
)
@@ -50,7 +50,7 @@ class UpdateIntegrationTest : BaseIntegrationTest() {
5050
val queryResult = queryWithoutParameters(dopeQuery)
5151
val result = queryResult.toMapValues()
5252

53-
assertEquals("newName", result["newField"])
53+
assertEquals("newName", result["newFieldName"])
5454
assertNull(result["nullField"])
5555
assertNull(result["nameField"])
5656
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package ch.ergon.dope.functions
2+
3+
import ch.ergon.dope.QueryBuilder
4+
import ch.ergon.dope.integrationTest.BaseIntegrationTest
5+
import ch.ergon.dope.integrationTest.TestCouchbaseDatabase.detailsField
6+
import ch.ergon.dope.integrationTest.TestCouchbaseDatabase.testBucket
7+
import ch.ergon.dope.integrationTest.toMapValues
8+
import ch.ergon.dope.resolvable.expression.type.TRUE
9+
import ch.ergon.dope.resolvable.expression.type.alias
10+
import ch.ergon.dope.resolvable.expression.type.collection.inArray
11+
import ch.ergon.dope.resolvable.expression.type.function.objects.addAttribute
12+
import ch.ergon.dope.resolvable.expression.type.function.objects.concat
13+
import ch.ergon.dope.resolvable.expression.type.function.objects.getInnerPairs
14+
import ch.ergon.dope.resolvable.expression.type.function.objects.getLength
15+
import ch.ergon.dope.resolvable.expression.type.function.objects.removeAttribute
16+
import ch.ergon.dope.resolvable.expression.type.logic.and
17+
import ch.ergon.dope.resolvable.expression.type.meta
18+
import ch.ergon.dope.resolvable.expression.type.relational.isGreaterThan
19+
import ch.ergon.dope.resolvable.expression.type.toDopeType
20+
import kotlin.test.Test
21+
import kotlin.test.assertEquals
22+
23+
class ObjectFunctionsIntegrationTest : BaseIntegrationTest() {
24+
@Test
25+
fun `use nested object functions`() {
26+
val dopeQuery = QueryBuilder()
27+
.selectRaw(
28+
detailsField.concat(mapOf("someField" to 4).toDopeType()).addAttribute("otherField", TRUE).removeAttribute("department")
29+
.alias("result"),
30+
)
31+
.from(testBucket)
32+
.where(
33+
detailsField.getLength().isGreaterThan(2)
34+
.and(mapOf("name" to "email", "val" to "[email protected]").toDopeType().inArray(detailsField.getInnerPairs())),
35+
).orderBy(
36+
meta().id,
37+
)
38+
.build()
39+
40+
val queryResult = queryWithoutParameters(dopeQuery)
41+
42+
assertEquals(
43+
mapOf(
44+
"position" to "Engineer",
45+
"email" to "[email protected]",
46+
"someField" to 4,
47+
"otherField" to true,
48+
),
49+
queryResult.toMapValues(),
50+
)
51+
}
52+
}

core/src/integrationTest/kotlin/ch/ergon/dope/integrationTest/TestCouchbaseDatabase.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import ch.ergon.dope.resolvable.expression.type.Field
77
import ch.ergon.dope.validtype.ArrayType
88
import ch.ergon.dope.validtype.BooleanType
99
import ch.ergon.dope.validtype.NumberType
10+
import ch.ergon.dope.validtype.ObjectType
1011
import ch.ergon.dope.validtype.StringType
1112
import com.couchbase.client.kotlin.Cluster
1213
import com.couchbase.client.kotlin.query.QueryResult
@@ -33,6 +34,7 @@ object TestCouchbaseDatabase {
3334
val orderNumberField = Field<StringType>("orderNumber", testBucket.name)
3435
val deliveryDateField = Field<StringType>("deliveryDate", testBucket.name)
3536
val quantitiesField = Field<ArrayType<NumberType>>("quantities", testBucket.name)
37+
val detailsField = Field<ObjectType>("details", testBucket.name)
3638

3739
init {
3840
initContainer()
@@ -69,6 +71,11 @@ object TestCouchbaseDatabase {
6971
"type" to "employee",
7072
"name" to "employee$i",
7173
"isActive" to true,
74+
"details" to mapOf(
75+
"position" to "Engineer",
76+
"department" to "Engineering",
77+
"email" to "employee$i@company.com",
78+
),
7279
),
7380
)
7481
collection.upsert(
@@ -78,6 +85,10 @@ object TestCouchbaseDatabase {
7885
"type" to "client",
7986
"name" to "client$i",
8087
"isActive" to (i % 2 == 0), // clients with even numbers are active
88+
"contacts" to listOf(
89+
mapOf("name" to "Contact A", "email" to "[email protected]"),
90+
mapOf("name" to "Contact B", "email" to "[email protected]"),
91+
),
8192
),
8293
)
8394
collection.upsert(

core/src/main/kotlin/ch/ergon/dope/resolvable/expression/type/Primitive.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ class ObjectPrimitive(
7676
)
7777

7878
class ObjectEntryPrimitive<T : ValidType>(
79-
private val key: TypeExpression<StringType>,
80-
private val value: TypeExpression<T>,
79+
val key: TypeExpression<StringType>,
80+
val value: TypeExpression<T>,
8181
) : Resolvable {
8282
override fun toDopeQuery(manager: DopeQueryManager): DopeQuery {
8383
val keyQuery = key.toDopeQuery(manager)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package ch.ergon.dope.resolvable.expression.type.function.objects
2+
3+
import ch.ergon.dope.resolvable.expression.type.ObjectEntryPrimitive
4+
import ch.ergon.dope.resolvable.expression.type.TypeExpression
5+
import ch.ergon.dope.resolvable.expression.type.function.FunctionExpression
6+
import ch.ergon.dope.resolvable.expression.type.toDopeType
7+
import ch.ergon.dope.resolvable.expression.type.toObjectEntry
8+
import ch.ergon.dope.validtype.ObjectType
9+
import ch.ergon.dope.validtype.StringType
10+
import ch.ergon.dope.validtype.ValidType
11+
12+
class ObjectAddExpression(
13+
objectExpression: TypeExpression<ObjectType>,
14+
objectEntryPrimitive: ObjectEntryPrimitive<out ValidType>,
15+
) : FunctionExpression<ObjectType>("OBJECT_ADD", objectExpression, objectEntryPrimitive.key, objectEntryPrimitive.value)
16+
17+
fun TypeExpression<ObjectType>.addAttribute(objectEntryPrimitive: ObjectEntryPrimitive<out ValidType>) =
18+
ObjectAddExpression(this, objectEntryPrimitive)
19+
20+
fun TypeExpression<ObjectType>.addAttribute(key: TypeExpression<StringType>, value: TypeExpression<out ValidType>) =
21+
addAttribute(key.toObjectEntry(value))
22+
23+
fun TypeExpression<ObjectType>.addAttribute(key: String, value: TypeExpression<out ValidType>) =
24+
addAttribute(key.toDopeType(), value)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package ch.ergon.dope.resolvable.expression.type.function.objects
2+
3+
import ch.ergon.dope.resolvable.expression.type.TypeExpression
4+
import ch.ergon.dope.resolvable.expression.type.function.FunctionExpression
5+
import ch.ergon.dope.validtype.ObjectType
6+
7+
class ObjectConcatExpression(
8+
firstObjectExpression: TypeExpression<ObjectType>,
9+
secondObjectExpression: TypeExpression<ObjectType>,
10+
vararg additionalObjectExpression: TypeExpression<ObjectType>,
11+
) : FunctionExpression<ObjectType>("OBJECT_CONCAT", firstObjectExpression, secondObjectExpression, *additionalObjectExpression)
12+
13+
fun TypeExpression<ObjectType>.concat(
14+
secondObjectExpression: TypeExpression<ObjectType>,
15+
vararg additionalObjectExpression: TypeExpression<ObjectType>,
16+
) = ObjectConcatExpression(this, secondObjectExpression, *additionalObjectExpression)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package ch.ergon.dope.resolvable.expression.type.function.objects
2+
3+
import ch.ergon.dope.resolvable.expression.type.TypeExpression
4+
import ch.ergon.dope.resolvable.expression.type.function.FunctionExpression
5+
import ch.ergon.dope.resolvable.expression.type.toDopeType
6+
import ch.ergon.dope.validtype.ObjectType
7+
import ch.ergon.dope.validtype.StringType
8+
import ch.ergon.dope.validtype.ValidType
9+
10+
class ObjectFieldExpression(
11+
objectExpression: TypeExpression<ObjectType>,
12+
attributeKey: TypeExpression<StringType>,
13+
) : FunctionExpression<ValidType>("OBJECT_FIELD", objectExpression, attributeKey)
14+
15+
fun TypeExpression<ObjectType>.getField(key: TypeExpression<StringType>) =
16+
ObjectFieldExpression(this, key)
17+
18+
fun TypeExpression<ObjectType>.getField(key: String) = getField(key.toDopeType())
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package ch.ergon.dope.resolvable.expression.type.function.objects
2+
3+
import ch.ergon.dope.resolvable.expression.type.TypeExpression
4+
import ch.ergon.dope.resolvable.expression.type.function.FunctionExpression
5+
import ch.ergon.dope.validtype.ArrayType
6+
import ch.ergon.dope.validtype.ObjectType
7+
8+
class ObjectInnerPairsExpression(
9+
objectExpression: TypeExpression<ObjectType>,
10+
) : FunctionExpression<ArrayType<ObjectType>>("OBJECT_INNER_PAIRS", objectExpression)
11+
12+
fun TypeExpression<ObjectType>.getInnerPairs() = ObjectInnerPairsExpression(this)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package ch.ergon.dope.resolvable.expression.type.function.objects
2+
3+
import ch.ergon.dope.resolvable.expression.type.TypeExpression
4+
import ch.ergon.dope.resolvable.expression.type.function.FunctionExpression
5+
import ch.ergon.dope.validtype.ArrayType
6+
import ch.ergon.dope.validtype.ObjectType
7+
import ch.ergon.dope.validtype.ValidType
8+
9+
class ObjectInnerValuesExpression(
10+
objectExpression: TypeExpression<ObjectType>,
11+
) : FunctionExpression<ArrayType<ValidType>>("OBJECT_INNER_VALUES", objectExpression)
12+
13+
fun TypeExpression<ObjectType>.getInnerValues() = ObjectInnerValuesExpression(this)

0 commit comments

Comments
 (0)