11package streams.procedures
22
3+ import org.apache.commons.lang3.exception.ExceptionUtils
4+ import org.neo4j.graphdb.GraphDatabaseService
5+ import org.neo4j.kernel.internal.GraphDatabaseAPI
36import org.neo4j.logging.Log
4- import org.neo4j.procedure.*
7+ import org.neo4j.procedure.Context
8+ import org.neo4j.procedure.Description
9+ import org.neo4j.procedure.Mode
10+ import org.neo4j.procedure.Name
11+ import org.neo4j.procedure.Procedure
512import streams.StreamsEventConsumer
613import streams.StreamsEventConsumerFactory
14+ import streams.StreamsEventSink
715import streams.StreamsEventSinkConfigMapper
816import streams.StreamsSinkConfiguration
917import streams.config.StreamsConfig
18+ import streams.events.StreamsPluginStatus
19+ import streams.utils.Neo4jUtils
20+ import java.util.stream.Collectors
1021import java.util.stream.Stream
1122
1223class StreamResult (@JvmField val event : Map <String , * >)
24+ class KeyValueResult (@JvmField val name : String , @JvmField val value : Any? )
1325
1426class StreamsSinkProcedures {
1527
1628 @JvmField @Context
1729 var log: Log ? = null
1830
31+ @JvmField @Context
32+ var db: GraphDatabaseService ? = null
33+
1934 @Procedure(mode = Mode .READ , name = " streams.consume" )
2035 @Description(" streams.consume(topic, {timeout: <long value>, from: <string>, groupId: <string>, commit: <boolean>, partitions:[{partition: <number>, offset: <number>}]}) " +
2136 " YIELD event - Allows to consume custom topics" )
@@ -35,6 +50,71 @@ class StreamsSinkProcedures {
3550 return data.map { StreamResult (mapOf (" data" to it)) }.stream()
3651 }
3752
53+ @Procedure(" streams.sink.start" )
54+ fun sinkStart (): Stream <KeyValueResult > {
55+ checkEnabled()
56+ return checkLeader {
57+ try {
58+ streamsEventSink?.start()
59+ sinkStatus()
60+ } catch (e: Exception ) {
61+ log?.error(" Cannot start the Sink because of the following exception" , e)
62+ Stream .concat(sinkStatus(),
63+ Stream .of(KeyValueResult (" exception" , ExceptionUtils .getStackTrace(e))))
64+ }
65+ }
66+ }
67+
68+ @Procedure(" streams.sink.stop" )
69+ fun sinkStop (): Stream <KeyValueResult > {
70+ checkEnabled()
71+ return checkLeader {
72+ try {
73+ streamsEventSink?.stop()
74+ sinkStatus()
75+ } catch (e: Exception ) {
76+ log?.error(" Cannot stopped the Sink because of the following exception" , e)
77+ Stream .concat(sinkStatus(),
78+ Stream .of(KeyValueResult (" exception" , ExceptionUtils .getStackTrace(e))))
79+ }
80+ }
81+ }
82+
83+ @Procedure(" streams.sink.restart" )
84+ fun sinkRestart (): Stream <KeyValueResult > {
85+ val stopped = sinkStop().collect(Collectors .toList())
86+ val hasError = stopped.any { it.name == " exception" }
87+ if (hasError) {
88+ return stopped.stream()
89+ }
90+ return sinkStart()
91+ }
92+
93+ @Procedure(" streams.sink.config" )
94+ fun sinkConfig (): Stream <KeyValueResult > {
95+ checkEnabled()
96+ return checkLeader {
97+ streamsSinkConfiguration.asMap()
98+ .entries.stream()
99+ .map { KeyValueResult (it.key, it.value) }
100+ }
101+ }
102+
103+ @Procedure(" streams.sink.status" )
104+ fun sinkStatus (): Stream <KeyValueResult > {
105+ checkEnabled()
106+ return checkLeader {
107+ val value = (streamsEventSink?.status() ? : StreamsPluginStatus .UNKNOWN ).toString()
108+ Stream .of(KeyValueResult (" status" , value))
109+ }
110+ }
111+
112+ private fun checkLeader (lambda : () -> Stream <KeyValueResult >): Stream <KeyValueResult > = if (Neo4jUtils .isWriteableInstance(db as GraphDatabaseAPI )) {
113+ lambda()
114+ } else {
115+ Stream .of(KeyValueResult (" error" , " You can use this procedure only in the LEADER or in a single instance configuration." ))
116+ }
117+
38118 private fun readData (topic : String , procedureConfig : Map <String , Any >, consumerConfig : Map <String , String >): List <Any > {
39119 val cfg = procedureConfig.mapValues { if (it.key != " partitions" ) it.value else mapOf (topic to it.value) }
40120 val timeout = cfg.getOrDefault(" timeout" , 1000 ).toString().toLong()
@@ -80,6 +160,7 @@ class StreamsSinkProcedures {
80160 private lateinit var streamsSinkConfiguration: StreamsSinkConfiguration
81161 private lateinit var streamsEventConsumerFactory: StreamsEventConsumerFactory
82162 private lateinit var streamsEventSinkConfigMapper: StreamsEventSinkConfigMapper
163+ private var streamsEventSink: StreamsEventSink ? = null
83164
84165 fun registerStreamsEventSinkConfigMapper (streamsEventSinkConfigMapper : StreamsEventSinkConfigMapper ) {
85166 this .streamsEventSinkConfigMapper = streamsEventSinkConfigMapper
@@ -92,5 +173,9 @@ class StreamsSinkProcedures {
92173 fun registerStreamsEventConsumerFactory (streamsEventConsumerFactory : StreamsEventConsumerFactory ) {
93174 this .streamsEventConsumerFactory = streamsEventConsumerFactory
94175 }
176+
177+ fun registerStreamsEventSink (streamsEventSink : StreamsEventSink ) {
178+ this .streamsEventSink = streamsEventSink
179+ }
95180 }
96181}
0 commit comments