@@ -2,7 +2,13 @@ use crate::traits::IdentifiableFlow;
22use async_nats:: jetstream:: kv:: Config ;
33use futures_lite:: StreamExt ;
44use prost:: Message ;
5- use tucana:: shared:: { ExecutionFlow , ValidationFlow , Value } ;
5+ use tucana:: shared:: {
6+ ExecutionFlow , Struct , ValidationFlow , Value ,
7+ value:: Kind :: { self , StructValue } ,
8+ } ;
9+
10+ const EMITTER_TOPIC_PREFIX : & str = "runtime.emitter" ;
11+ const EMITTER_WAIT_TIMEOUT_SECONDS : u64 = 30 ;
612
713pub struct AdapterStore {
814 client : async_nats:: Client ,
@@ -15,6 +21,13 @@ pub enum FlowIdentifyResult {
1521 Multiple ( Vec < ValidationFlow > ) ,
1622}
1723
24+ pub enum FlowExecutionResult {
25+ Ongoing ( Value ) ,
26+ Failed ,
27+ FinishedWithoutOngoing ,
28+ TransportError ,
29+ }
30+
1831impl AdapterStore {
1932 pub async fn from_url ( url : String , bucket : String ) -> Self {
2033 let client = match async_nats:: connect ( url) . await {
@@ -118,32 +131,109 @@ impl AdapterStore {
118131 flow : ValidationFlow ,
119132 input_value : Option < Value > ,
120133 ) -> Option < Value > {
134+ match self . execute_flow_with_emitter ( flow, input_value) . await {
135+ FlowExecutionResult :: Ongoing ( value) => Some ( value) ,
136+ FlowExecutionResult :: Failed
137+ | FlowExecutionResult :: FinishedWithoutOngoing
138+ | FlowExecutionResult :: TransportError => None ,
139+ }
140+ }
141+
142+ pub async fn execute_flow_with_emitter (
143+ & self ,
144+ flow : ValidationFlow ,
145+ input_value : Option < Value > ,
146+ ) -> FlowExecutionResult {
121147 // TODO: Replace body vaidation with triangulus when its ready
122- let uuid = uuid:: Uuid :: new_v4 ( ) . to_string ( ) ;
148+ let execution_id = uuid:: Uuid :: new_v4 ( ) . to_string ( ) ;
123149 let flow_id = flow. flow_id ;
124150 let execution_flow: ExecutionFlow =
125151 Self :: convert_validation_flow ( flow, input_value. clone ( ) ) ;
126152 let bytes = execution_flow. encode_to_vec ( ) ;
127- let topic = format ! ( "execution.{}" , uuid) ;
153+ let execution_topic = format ! ( "execution.{}" , execution_id) ;
154+ let emitter_topic = format ! ( "{}.{}" , EMITTER_TOPIC_PREFIX , execution_id) ;
155+
128156 log:: info!(
129157 "Requesting execution of flow {} with execution id {}" ,
130158 flow_id,
131- uuid
159+ execution_id
132160 ) ;
133- log:: debug!( "Flow Input for Execution ({}) is {:?}" , uuid, input_value) ;
134- let result = self . client . request ( topic, bytes. into ( ) ) . await ;
135-
136- match result {
137- Ok ( message) => match Value :: decode ( message. payload ) {
138- Ok ( value) => Some ( value) ,
139- Err ( err) => {
140- log:: error!( "Failed to decode response from NATS server: {:?}" , err) ;
141- None
142- }
143- } ,
161+ log:: debug!(
162+ "Flow Input for Execution ({}) is {:?}" ,
163+ execution_id,
164+ input_value
165+ ) ;
166+
167+ let mut subscriber = match self . client . subscribe ( emitter_topic. clone ( ) ) . await {
168+ Ok ( subscriber) => subscriber,
144169 Err ( err) => {
145- log:: error!( "Failed to send request to NATS server: {:?}" , err) ;
146- None
170+ log:: error!(
171+ "Failed to subscribe to emitter topic '{}' for flow {}: {:?}" ,
172+ emitter_topic,
173+ flow_id,
174+ err
175+ ) ;
176+ return FlowExecutionResult :: TransportError ;
177+ }
178+ } ;
179+
180+ if let Err ( err) = self
181+ . client
182+ . publish ( execution_topic. clone ( ) , bytes. into ( ) )
183+ . await
184+ {
185+ log:: error!(
186+ "Failed to publish flow {} to execution topic '{}': {:?}" ,
187+ flow_id,
188+ execution_topic,
189+ err
190+ ) ;
191+ return FlowExecutionResult :: TransportError ;
192+ }
193+
194+ loop {
195+ let next_message = tokio:: time:: timeout (
196+ std:: time:: Duration :: from_secs ( EMITTER_WAIT_TIMEOUT_SECONDS ) ,
197+ subscriber. next ( ) ,
198+ )
199+ . await ;
200+
201+ let message = match next_message {
202+ Ok ( Some ( message) ) => message,
203+ Ok ( None ) => {
204+ log:: error!(
205+ "Emitter subscription '{}' closed before execution completed" ,
206+ emitter_topic
207+ ) ;
208+ return FlowExecutionResult :: TransportError ;
209+ }
210+ Err ( _) => {
211+ log:: error!(
212+ "Timed out waiting for emitter events on '{}' after {}s" ,
213+ emitter_topic,
214+ EMITTER_WAIT_TIMEOUT_SECONDS
215+ ) ;
216+ return FlowExecutionResult :: TransportError ;
217+ }
218+ } ;
219+
220+ let Some ( ( emit_type, payload) ) = Self :: decode_emit_message ( message. payload . as_ref ( ) )
221+ else {
222+ continue ;
223+ } ;
224+
225+ match emit_type. as_str ( ) {
226+ "starting" => { }
227+ "ongoing" => return FlowExecutionResult :: Ongoing ( payload) ,
228+ "failed" => return FlowExecutionResult :: Failed ,
229+ "finished" => return FlowExecutionResult :: FinishedWithoutOngoing ,
230+ other => {
231+ log:: warn!(
232+ "Received unknown emitter event '{}' on '{}'" ,
233+ other,
234+ emitter_topic
235+ ) ;
236+ }
147237 }
148238 }
149239 }
@@ -174,4 +264,38 @@ impl AdapterStore {
174264 }
175265 true
176266 }
267+
268+ fn decode_emit_message ( bytes : & [ u8 ] ) -> Option < ( String , Value ) > {
269+ let decoded = match Value :: decode ( bytes) {
270+ Ok ( value) => value,
271+ Err ( err) => {
272+ log:: error!( "Failed to decode emitter payload: {:?}" , err) ;
273+ return None ;
274+ }
275+ } ;
276+
277+ let Value {
278+ kind : Some ( StructValue ( Struct { fields } ) ) ,
279+ } = decoded
280+ else {
281+ log:: warn!( "Emitter payload was not a struct value" ) ;
282+ return None ;
283+ } ;
284+
285+ let Some ( emit_type) = fields. get ( "emit_type" ) else {
286+ log:: warn!( "Emitter payload is missing 'emit_type'" ) ;
287+ return None ;
288+ } ;
289+ let Some ( payload) = fields. get ( "payload" ) else {
290+ log:: warn!( "Emitter payload is missing 'payload'" ) ;
291+ return None ;
292+ } ;
293+
294+ let Some ( Kind :: StringValue ( emit_type_str) ) = emit_type. kind . as_ref ( ) else {
295+ log:: warn!( "Emitter payload field 'emit_type' was not a string" ) ;
296+ return None ;
297+ } ;
298+
299+ Some ( ( emit_type_str. clone ( ) , payload. clone ( ) ) )
300+ }
177301}
0 commit comments