1use crate::{
14 core::{
15 db::{
16 control_plane::ControlPlaneRepo,
17 data_flow::DataFlowRepo,
18 tx::{Transaction, TransactionalContext},
19 },
20 error::{DbError, HandlerError},
21 handler::DataFlowHandler,
22 model::{
23 data_address::DataAddress,
24 data_flow::{DataFlow, DataFlowState, DataFlowType, TransitionError},
25 messages::{
26 DataFlowPrepareMessage, DataFlowResumeMessage, DataFlowStartMessage,
27 DataFlowStartedNotificationMessage, DataFlowStatusMessage,
28 DataFlowStatusResponseMessage,
29 },
30 },
31 },
32 error::{SdkError, SdkResult},
33};
34
35pub struct DataPlaneSdkInternal<C>
36where
37 C: TransactionalContext,
38{
39 pub(crate) ctx: C,
40 pub(crate) repo: Box<dyn DataFlowRepo<Transaction = C::Transaction>>,
41 pub(crate) control_plane_repo: Box<dyn ControlPlaneRepo<Transaction = C::Transaction>>,
42 pub(crate) handler: Box<dyn DataFlowHandler<Transaction = C::Transaction>>,
43 pub(crate) client: reqwest::Client,
44}
45
46impl<C> DataPlaneSdkInternal<C>
47where
48 C: TransactionalContext,
49{
50 pub async fn start(
51 &self,
52 participant_context_id: &str,
53 control_plane_id: &str,
54 req: DataFlowStartMessage,
55 ) -> SdkResult<DataFlowStatusMessage> {
56 let mut flow = DataFlow::builder()
57 .id(req.process_id)
58 .counter_party_id(req.counter_party_id)
59 .maybe_data_address(req.data_address)
60 .participant_context_id(participant_context_id)
61 .state(DataFlowState::Initiating)
62 .metadata(req.metadata)
63 .claims(req.claims)
64 .participant_id(req.participant_id)
65 .dataspace_context(req.dataspace_context)
66 .dataset_id(req.dataset_id)
67 .agreement_id(req.agreement_id)
68 .control_plane_id(control_plane_id)
69 .labels(req.labels)
70 .profile(req.profile)
71 .kind(DataFlowType::Provider)
72 .build();
73
74 if self.handler.can_handle(&flow).await? {
75 let mut tx = self.ctx.begin().await?;
76 let response = self.handler.on_start(&mut tx, &flow).await?;
77
78 match response.state {
79 DataFlowState::Starting => flow.transition_to_starting()?,
80 DataFlowState::Started => flow.transition_to_started()?,
81 _ => {
82 return Err(SdkError::Handler(HandlerError::NotSupported(
83 "Handler can only transition to Starting or Started state".to_string(),
84 )));
85 }
86 }
87
88 self.repo.create(&mut tx, &flow).await?;
89 tx.commit().await?;
90
91 Ok(response)
92 } else {
93 Err(SdkError::Handler(HandlerError::NotSupported(
94 "Data flow handler cannot handle this flow".to_string(),
95 )))
96 }
97 }
98
99 pub async fn prepare(
100 &self,
101 participant_context_id: &str,
102 control_plane_id: &str,
103 req: DataFlowPrepareMessage,
104 ) -> SdkResult<DataFlowStatusMessage> {
105 let mut flow = DataFlow::builder()
106 .id(req.process_id)
107 .counter_party_id(req.counter_party_id)
108 .participant_context_id(participant_context_id)
109 .state(DataFlowState::Initiating)
110 .metadata(req.metadata)
111 .claims(req.claims)
112 .participant_id(req.participant_id)
113 .dataspace_context(req.dataspace_context)
114 .dataset_id(req.dataset_id)
115 .agreement_id(req.agreement_id)
116 .control_plane_id(control_plane_id)
117 .labels(req.labels)
118 .profile(req.profile)
119 .kind(DataFlowType::Consumer)
120 .build();
121
122 if self.handler.can_handle(&flow).await? {
123 let mut tx = self.ctx.begin().await?;
124 let response = self.handler.on_prepare(&mut tx, &flow).await?;
125
126 match response.state {
127 DataFlowState::Preparing => flow.transition_to_preparing()?,
128 DataFlowState::Prepared => flow.transition_to_prepared()?,
129 _ => {
130 return Err(SdkError::Handler(HandlerError::NotSupported(format!(
131 "Handler can only transition to Preparing or Prepared state: current state {:?}",
132 response.state
133 ))));
134 }
135 }
136 self.repo.create(&mut tx, &flow).await?;
137 tx.commit().await?;
138
139 Ok(response)
140 } else {
141 Err(SdkError::Handler(HandlerError::NotSupported(
142 "Data flow handler cannot handle this flow".to_string(),
143 )))
144 }
145 }
146
147 pub async fn terminate(
148 &self,
149 _ctx: &str,
150 flow_id: &str,
151 reason: Option<String>,
152 ) -> SdkResult<()> {
153 let mut tx = self.ctx.begin().await?;
154 let mut flow = self
155 .repo
156 .fetch_by_id(&mut tx, flow_id)
157 .await?
158 .ok_or_else(|| DbError::NotFound(flow_id.to_string()))?;
159
160 flow.transition_to_terminated(reason)?;
161 self.repo.update(&mut tx, &flow).await?;
162
163 self.handler.on_terminate(&mut tx, &flow).await?;
164
165 tx.commit().await?;
166 Ok(())
167 }
168
169 pub async fn started(
170 &self,
171 _ctx: &str,
172 flow_id: &str,
173 msg: DataFlowStartedNotificationMessage,
174 ) -> SdkResult<()> {
175 let mut tx = self.ctx.begin().await?;
176 let mut flow = self
177 .repo
178 .fetch_by_id(&mut tx, flow_id)
179 .await?
180 .ok_or_else(|| DbError::NotFound(flow_id.to_string()))?;
181
182 flow.data_address = msg.data_address;
183
184 self.handler.on_started(&mut tx, &flow).await?;
185
186 flow.transition_to_started()?;
187 self.repo.update(&mut tx, &flow).await?;
188
189 tx.commit().await?;
190 Ok(())
191 }
192
193 pub async fn completed(&self, _ctx: &str, flow_id: &str) -> SdkResult<()> {
194 let mut tx = self.ctx.begin().await?;
195 let mut flow = self
196 .repo
197 .fetch_by_id(&mut tx, flow_id)
198 .await?
199 .ok_or_else(|| DbError::NotFound(flow_id.to_string()))?;
200
201 flow.transition_to_completed()?;
202
203 self.handler.on_completed(&mut tx, &flow).await?;
204
205 self.repo.update(&mut tx, &flow).await?;
206
207 tx.commit().await?;
208 Ok(())
209 }
210
211 pub async fn suspend(
212 &self,
213 _ctx: &str,
214 flow_id: &str,
215 reason: Option<String>,
216 ) -> SdkResult<()> {
217 let mut tx = self.ctx.begin().await?;
218
219 let mut flow = self
220 .repo
221 .fetch_by_id(&mut tx, flow_id)
222 .await?
223 .ok_or_else(|| DbError::NotFound(flow_id.to_string()))?;
224
225 flow.transition_to_suspended(reason)?;
226 self.repo.update(&mut tx, &flow).await?;
227
228 self.handler.on_suspend(&mut tx, &flow).await?;
229
230 tx.commit().await?;
231
232 Ok(())
233 }
234
235 pub async fn resume(
236 &self,
237 _ctx: &str,
238 flow_id: &str,
239 msg: DataFlowResumeMessage,
240 ) -> SdkResult<DataFlowStatusMessage> {
241 let mut tx = self.ctx.begin().await?;
242
243 let mut flow = self
244 .repo
245 .fetch_by_id(&mut tx, flow_id)
246 .await?
247 .ok_or_else(|| DbError::NotFound(flow_id.to_string()))?;
248
249 flow.data_address = msg.data_address;
250
251 let response = self.handler.on_resume(&mut tx, &flow).await?;
252
253 match response.state {
254 DataFlowState::Starting => flow.transition_to_starting()?,
255 DataFlowState::Started => flow.transition_to_started()?,
256 _ => {
257 return Err(SdkError::Handler(HandlerError::NotSupported(
258 "Handler can only transition to Starting or Started state".to_string(),
259 )));
260 }
261 }
262
263 self.repo.update(&mut tx, &flow).await?;
264
265 tx.commit().await?;
266
267 Ok(response)
268 }
269 pub async fn status(
270 &self,
271 _ctx: &str,
272 flow_id: &str,
273 ) -> SdkResult<DataFlowStatusResponseMessage> {
274 let mut tx = self.ctx.begin().await?;
275
276 let flow = self
277 .repo
278 .fetch_by_id(&mut tx, flow_id)
279 .await?
280 .ok_or_else(|| DbError::NotFound(flow_id.to_string()))?;
281
282 tx.commit().await?;
283
284 Ok(DataFlowStatusResponseMessage::builder()
285 .data_flow_id(flow.id)
286 .state(flow.state)
287 .build())
288 }
289
290 pub async fn notify_prepared(
295 &self,
296 ctx: &str,
297 flow_id: &str,
298 data_address: Option<DataAddress>,
299 ) -> SdkResult<()> {
300 self.send_callback(
301 ctx,
302 flow_id,
303 "prepared",
304 data_address.clone(),
305 None,
306 move |flow| {
307 flow.data_address = data_address;
308 flow.transition_to_prepared()
309 },
310 )
311 .await
312 }
313
314 pub async fn notify_started(
316 &self,
317 ctx: &str,
318 flow_id: &str,
319 data_address: Option<DataAddress>,
320 ) -> SdkResult<()> {
321 self.send_callback(
322 ctx,
323 flow_id,
324 "started",
325 data_address.clone(),
326 None,
327 |flow| {
328 flow.data_address = data_address;
329 flow.transition_to_started()
330 },
331 )
332 .await
333 }
334
335 pub async fn notify_completed(&self, ctx: &str, flow_id: &str) -> SdkResult<()> {
337 self.send_callback(ctx, flow_id, "completed", None, None, |flow| {
338 flow.transition_to_completed()
339 })
340 .await
341 }
342
343 pub async fn notify_errored(
346 &self,
347 ctx: &str,
348 flow_id: &str,
349 error: Option<String>,
350 ) -> SdkResult<()> {
351 self.send_callback(ctx, flow_id, "errored", None, error.clone(), move |flow| {
352 flow.transition_to_terminated(error)
353 })
354 .await
355 }
356
357 async fn send_callback<CB>(
358 &self,
359 _ctx: &str,
360 flow_id: &str,
361 operation: &str,
362 data_address: Option<DataAddress>,
363 error: Option<String>,
364 op: CB,
365 ) -> SdkResult<()>
366 where
367 CB: FnOnce(&mut DataFlow) -> Result<(), TransitionError>,
368 {
369 let mut tx = self.ctx.begin().await?;
370
371 let mut flow = self
372 .fetch_by_id(&mut tx, flow_id)
373 .await?
374 .ok_or_else(|| DbError::NotFound(flow_id.to_string()))?;
375
376 let control_plane = self
377 .control_plane_repo
378 .fetch_by_id(&mut tx, &flow.control_plane_id)
379 .await?
380 .ok_or_else(|| DbError::NotFound(flow.control_plane_id.clone()))?;
381
382 op(&mut flow)?;
383
384 let msg = DataFlowStatusMessage::builder()
385 .data_flow_id(flow.id.clone())
386 .maybe_data_address(data_address)
387 .state(flow.state.clone())
388 .maybe_error(error)
389 .build();
390
391 let url = format!(
392 "{}/transfers/{}/dataflow/{}",
393 control_plane.url.trim_end_matches('/'),
394 flow.id,
395 operation
396 );
397
398 let resp = self.client.post(&url).json(&msg).send().await?;
399 if !resp.status().is_success() {
400 let status = resp.status().as_u16();
401 let body = resp.text().await.unwrap_or_default();
402 return Err(SdkError::NotificationStatus { status, body });
403 }
404
405 self.repo.update(&mut tx, &flow).await?;
406 tx.commit().await?;
407
408 Ok(())
409 }
410
411 pub async fn fetch_by_id(
412 &self,
413 tx: &mut C::Transaction,
414 flow_id: &str,
415 ) -> SdkResult<Option<DataFlow>> {
416 self.repo.fetch_by_id(tx, flow_id).await.map(Ok)?
417 }
418
419 pub fn ctx(&self) -> &C {
420 &self.ctx
421 }
422}