@@ -2,8 +2,12 @@ package streams.utils
22
33import com.fasterxml.jackson.core.JsonGenerator
44import com.fasterxml.jackson.core.JsonParseException
5+ import com.fasterxml.jackson.core.JsonParser
56import com.fasterxml.jackson.core.JsonProcessingException
7+ import com.fasterxml.jackson.databind.DeserializationContext
68import com.fasterxml.jackson.databind.DeserializationFeature
9+ import com.fasterxml.jackson.databind.JsonDeserializer
10+ import com.fasterxml.jackson.databind.JsonNode
711import com.fasterxml.jackson.databind.JsonSerializer
812import com.fasterxml.jackson.databind.ObjectMapper
913import com.fasterxml.jackson.databind.SerializationFeature
@@ -13,10 +17,18 @@ import com.fasterxml.jackson.module.kotlin.convertValue
1317import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
1418import com.fasterxml.jackson.module.kotlin.readValue
1519import org.neo4j.driver.Value
20+ import org.neo4j.driver.Values
1621import org.neo4j.driver.internal.value.PointValue
1722import org.neo4j.driver.types.Node
1823import org.neo4j.driver.types.Point
1924import org.neo4j.driver.types.Relationship
25+ import streams.events.EntityType
26+ import streams.events.Meta
27+ import streams.events.NodePayload
28+ import streams.events.Payload
29+ import streams.events.RecordChange
30+ import streams.events.RelationshipPayload
31+ import streams.events.Schema
2032import streams.events.StreamsTransactionEvent
2133import streams.events.StreamsTransactionNodeEvent
2234import streams.events.StreamsTransactionRelationshipEvent
@@ -94,6 +106,125 @@ class DriverRelationshipSerializer : JsonSerializer<Relationship>() {
94106 }
95107}
96108
109+ class StreamsTransactionRelationshipEventDeserializer : StreamsTransactionEventDeserializer <StreamsTransactionRelationshipEvent , RelationshipPayload >() {
110+ override fun createEvent (meta : Meta , payload : RelationshipPayload , schema : Schema ): StreamsTransactionRelationshipEvent {
111+ return StreamsTransactionRelationshipEvent (meta, payload, schema)
112+ }
113+
114+ override fun convertPayload (payloadMap : JsonNode ): RelationshipPayload {
115+ return JSONUtils .convertValue<RelationshipPayload >(payloadMap)
116+ }
117+
118+ override fun fillPayload (payload : RelationshipPayload ,
119+ beforeProps : Map <String , Any >? ,
120+ afterProps : Map <String , Any >? ): RelationshipPayload {
121+ return payload.copy(
122+ before = payload.before?.copy(properties = beforeProps),
123+ after = payload.after?.copy(properties = afterProps)
124+ )
125+ }
126+
127+ override fun deserialize (parser : JsonParser , context : DeserializationContext ): StreamsTransactionRelationshipEvent {
128+ val deserialized = super .deserialize(parser, context)
129+ if (deserialized.payload.type == EntityType .node) {
130+ throw IllegalArgumentException (" Relationship event expected, but node type found" )
131+ }
132+ return deserialized
133+ }
134+
135+ }
136+
137+ class StreamsTransactionNodeEventDeserializer : StreamsTransactionEventDeserializer <StreamsTransactionNodeEvent , NodePayload >() {
138+ override fun createEvent (meta : Meta , payload : NodePayload , schema : Schema ): StreamsTransactionNodeEvent {
139+ return StreamsTransactionNodeEvent (meta, payload, schema)
140+ }
141+
142+ override fun convertPayload (payloadMap : JsonNode ): NodePayload {
143+ return JSONUtils .convertValue<NodePayload >(payloadMap)
144+ }
145+
146+ override fun fillPayload (payload : NodePayload ,
147+ beforeProps : Map <String , Any >? ,
148+ afterProps : Map <String , Any >? ): NodePayload {
149+ return payload.copy(
150+ before = payload.before?.copy(properties = beforeProps),
151+ after = payload.after?.copy(properties = afterProps)
152+ )
153+ }
154+
155+ override fun deserialize (parser : JsonParser , context : DeserializationContext ): StreamsTransactionNodeEvent {
156+ val deserialized = super .deserialize(parser, context)
157+ if (deserialized.payload.type == EntityType .relationship) {
158+ throw IllegalArgumentException (" Node event expected, but relationship type found" )
159+ }
160+ return deserialized
161+ }
162+
163+ }
164+
165+ abstract class StreamsTransactionEventDeserializer <EVENT , PAYLOAD : Payload > : JsonDeserializer <EVENT >() {
166+
167+ abstract fun createEvent (meta : Meta , payload : PAYLOAD , schema : Schema ): EVENT
168+ abstract fun convertPayload (payloadMap : JsonNode ): PAYLOAD
169+ abstract fun fillPayload (payload : PAYLOAD ,
170+ beforeProps : Map <String , Any >? ,
171+ afterProps : Map <String , Any >? ): PAYLOAD
172+
173+ @Throws(IOException ::class , JsonProcessingException ::class )
174+ override fun deserialize (parser : JsonParser , context : DeserializationContext ): EVENT {
175+ val root: JsonNode = parser.codec.readTree(parser)
176+ val meta = JSONUtils .convertValue<Meta >(root[" meta" ])
177+ val schema = JSONUtils .convertValue<Schema >(root[" schema" ])
178+ val points = schema.properties.filterValues { it == " PointValue" }.keys
179+ var payload = convertPayload(root[" payload" ])
180+ if (points.isNotEmpty()) {
181+ val beforeProps = convertPoints(payload.before, points)
182+ val afterProps = convertPoints(payload.after, points)
183+ payload = fillPayload(payload, beforeProps, afterProps)
184+ }
185+ return createEvent(meta, payload, schema)
186+ }
187+
188+ private fun convertPoints (
189+ recordChange : RecordChange ? ,
190+ points : Set <String >
191+ ) = recordChange
192+ ?.properties
193+ ?.mapValues {
194+ if (points.contains(it.key)) {
195+ val pointMap = it.value as Map <String , Any >
196+ when (pointMap[" crs" ]) {
197+ " cartesian" -> Values .point(
198+ 7203 ,
199+ pointMap[" x" ].toString().toDouble(),
200+ pointMap[" y" ].toString().toDouble()
201+ )
202+ " cartesian-3d" -> Values .point(
203+ 9157 ,
204+ pointMap[" x" ].toString().toDouble(),
205+ pointMap[" y" ].toString().toDouble(),
206+ pointMap[" z" ].toString().toDouble()
207+ )
208+ " wgs-84" -> Values .point(
209+ 4326 ,
210+ pointMap[" longitude" ].toString().toDouble(),
211+ pointMap[" latitude" ].toString().toDouble()
212+ )
213+ " wgs-84-3d" -> Values .point(
214+ 4979 ,
215+ pointMap[" longitude" ].toString().toDouble(),
216+ pointMap[" latitude" ].toString().toDouble(),
217+ pointMap[" height" ].toString().toDouble()
218+ )
219+ else -> throw IllegalArgumentException (" CRS value: ${pointMap[" crs" ]} not found" )
220+ }
221+ } else {
222+ it.value
223+ }
224+ }
225+
226+ }
227+
97228object JSONUtils {
98229
99230 private val OBJECT_MAPPER : ObjectMapper = jacksonObjectMapper()
@@ -106,10 +237,13 @@ object JSONUtils {
106237 module.addSerializer(PointValue ::class .java, PointValueSerializer ())
107238 module.addSerializer(Node ::class .java, DriverNodeSerializer ())
108239 module.addSerializer(Relationship ::class .java, DriverRelationshipSerializer ())
240+ module.addDeserializer(StreamsTransactionRelationshipEvent ::class .java, StreamsTransactionRelationshipEventDeserializer ())
241+ module.addDeserializer(StreamsTransactionNodeEvent ::class .java, StreamsTransactionNodeEventDeserializer ())
109242 module.addSerializer(TemporalAccessor ::class .java, TemporalAccessorSerializer ())
110243 OBJECT_MAPPER .registerModule(module)
111244 OBJECT_MAPPER .disable(SerializationFeature .FAIL_ON_EMPTY_BEANS )
112245 OBJECT_MAPPER .disable(DeserializationFeature .FAIL_ON_UNKNOWN_PROPERTIES )
246+ STRICT_OBJECT_MAPPER .registerModule(module)
113247 }
114248
115249 fun getObjectMapper (): ObjectMapper = OBJECT_MAPPER
0 commit comments