Skip to content

Commit

Permalink
Add an example for Kotlins inline classes.
Browse files Browse the repository at this point in the history
Kotlin provides so called „Inline classes“ (https://kotlinlang.org/docs/reference/inline-classes.html), wrapping standard types into a dedicated type. This makes them a nice fit for IDs etc.

The object mapping process works ootb, the parameter mapping needs some extra work: One has to provide custom serializers for all data classes being used (see `IdTypes`). Those serializers can be registered with a dedicated `SimpleModule`. This module needs to be added to the `ObjectMapper` instance used by OGM to convert parameters:

```
ObjectMapperFactory.objectMapper()
        .registerModule(KotlinModule())
        .registerModule(IdTypesModule())
```

The kotlin jackson module may detect them at some point in the future itself (See FasterXML/jackson-module-kotlin#199), but until than that work is necessary.

This closes #822.
  • Loading branch information
michael-simons committed Aug 21, 2020
1 parent 7425b15 commit 5e1f481
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 1 deletion.
5 changes: 5 additions & 0 deletions neo4j-ogm-tests/neo4j-ogm-integration-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-kotlin</artifactId>
</dependency>

<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2002-2020 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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.neo4j.ogm.domain.gh822

import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.ser.std.StdSerializer
import java.io.IOException
import java.io.Serializable

inline class StringID(val value: String) : Serializable

private class StringIDSerializer : StdSerializer<StringID>(StringID::class.java) {
@Throws(IOException::class)
override fun serialize(s: StringID, jsonGenerator: JsonGenerator,
serializerProvider: SerializerProvider) {
jsonGenerator.writeObject(s.value)
}
}

class IdTypesModule : com.fasterxml.jackson.databind.module.SimpleModule() {

init {
addSerializer(StringIDSerializer())
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (c) 2002-2020 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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.neo4j.ogm.domain.gh822

import org.neo4j.ogm.annotation.Id
import org.neo4j.ogm.annotation.NodeEntity

@NodeEntity
data class User(
@Id var userId: StringID? = null,
val name: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package org.neo4j.ogm.kotlin

import com.fasterxml.jackson.module.kotlin.KotlinModule
import org.assertj.core.api.Assertions.assertThat
import org.junit.*
import org.neo4j.driver.AuthTokens
Expand All @@ -27,6 +28,7 @@ import org.neo4j.driver.Values
import org.neo4j.harness.ServerControls
import org.neo4j.harness.TestServerBuilders
import org.neo4j.ogm.config.Configuration
import org.neo4j.ogm.config.ObjectMapperFactory
import org.neo4j.ogm.cypher.ComparisonOperator
import org.neo4j.ogm.cypher.Filter
import org.neo4j.ogm.cypher.Filters
Expand All @@ -38,6 +40,7 @@ import org.neo4j.ogm.domain.delegation.KotlinAImpl
import org.neo4j.ogm.domain.gh696.Lion
import org.neo4j.ogm.domain.gh696.Zebra
import org.neo4j.ogm.domain.gh696.ZooKotlin
import org.neo4j.ogm.domain.gh822.*
import org.neo4j.ogm.session.*
import kotlin.test.assertNotNull

Expand Down Expand Up @@ -67,8 +70,13 @@ class KotlinInteropTest {
sessionFactory = SessionFactory(ogmConfiguration,
MyNode::class.java.`package`.name,
KotlinAImpl::class.java.`package`.name,
ZooKotlin::class.java.`package`.name
ZooKotlin::class.java.`package`.name,
User::class.java.`package`.name
)

ObjectMapperFactory.objectMapper()
.registerModule(KotlinModule())
.registerModule(IdTypesModule())
}

@AfterClass
Expand Down Expand Up @@ -228,4 +236,20 @@ class KotlinInteropTest {
val numberOfMyNodes = sessionFactory.openSession().count<MyNode>(listOf(Filter("name", ComparisonOperator.EQUALS, "Brian")))
assertThat(numberOfMyNodes).isEqualTo(1)
}

@Test // GH-822
fun `inline classes should work with dedicated serializers`() {

val userId = StringID("aUserId")
val userToSave = User(userId, "Danger Dan");

sessionFactory.openSession().save(userToSave);

val nameFilter = Filter("userId", ComparisonOperator.EQUALS, userId).ignoreCase()
val loadedUsers = sessionFactory.openSession().loadAll(User::class.java, nameFilter);
assertThat(loadedUsers).hasSize(1).extracting<StringID>(User::userId).containsOnly(userId)

val loadedUser = sessionFactory.openSession().queryForObject<User>("MATCH (u:User) RETURN u", mapOf(Pair("userId", userId)))!!
assertThat(loadedUser).isNotNull().extracting(User::userId).isEqualTo(userId)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,13 @@ class KotlinMetaDataTest {
assertThat(kotlinZoo.getFieldInfo("animals").typeDescriptor).isEqualTo("org.neo4j.ogm.domain.gh696.Animal")
assertThat(kotlinZoo.getFieldInfo("zookeepers").typeDescriptor).isEqualTo("org.neo4j.ogm.domain.gh696.Zookeeper")
}

@Test // GH-822
fun `OGM should detect the inlines class underlying value type`() {
val metaData = MetaData("org.neo4j.ogm.domain.gh822")
val userClassInfo = metaData.classInfo("User")

assertThat(userClassInfo).isNotNull
assertThat(userClassInfo.getFieldInfo("userId").typeDescriptor).isEqualTo("java.lang.String")
}
}

0 comments on commit 5e1f481

Please sign in to comment.