1use std::collections::BTreeMap;
4use std::convert::{TryFrom, TryInto};
5
6use greentic_types as types;
7use semver::Version;
8use time::OffsetDateTime;
9
10use crate::bindings;
11
12type MapperResult<T> = Result<T, types::GreenticError>;
13
14fn invalid_input(msg: impl Into<String>) -> types::GreenticError {
15 types::GreenticError::new(types::ErrorCode::InvalidInput, msg)
16}
17
18fn i128_to_i64(value: i128) -> MapperResult<i64> {
19 value
20 .try_into()
21 .map_err(|_| invalid_input("numeric overflow converting deadline"))
22}
23
24fn timestamp_ms_to_offset(ms: i64) -> MapperResult<OffsetDateTime> {
25 let nanos = (ms as i128)
26 .checked_mul(1_000_000)
27 .ok_or_else(|| invalid_input("timestamp overflow"))?;
28 OffsetDateTime::from_unix_timestamp_nanos(nanos)
29 .map_err(|err| invalid_input(format!("invalid timestamp: {err}")))
30}
31
32fn offset_to_timestamp_ms(dt: &OffsetDateTime) -> MapperResult<i64> {
33 let nanos = dt.unix_timestamp_nanos();
34 let ms = nanos
35 .checked_div(1_000_000)
36 .ok_or_else(|| invalid_input("timestamp division overflow"))?;
37 ms.try_into()
38 .map_err(|_| invalid_input("timestamp overflow converting to milliseconds"))
39}
40
41type WitTenantCtx = bindings::greentic::interfaces_types::types::TenantCtx;
42type WitImpersonation = bindings::greentic::interfaces_types::types::Impersonation;
43type WitSessionCursor = bindings::greentic::interfaces_types::types::SessionCursor;
44type WitOutcome = bindings::greentic::interfaces_types::types::Outcome;
45type WitOutcomePending = bindings::greentic::interfaces_types::types::OutcomePending;
46type WitOutcomeError = bindings::greentic::interfaces_types::types::OutcomeError;
47type WitErrorCode = bindings::greentic::interfaces_types::types::ErrorCode;
48type WitAllowList = bindings::greentic::interfaces_types::types::AllowList;
49type WitProtocol = bindings::greentic::interfaces_types::types::Protocol;
50type WitNetworkPolicy = bindings::greentic::interfaces_types::types::NetworkPolicy;
51type WitSpanContext = bindings::greentic::interfaces_types::types::SpanContext;
52type WitPackRef = bindings::greentic::interfaces_types::types::PackRef;
53type WitSignature = bindings::greentic::interfaces_types::types::Signature;
54type WitSignatureAlgorithm = bindings::greentic::interfaces_types::types::SignatureAlgorithm;
55type WitCommonFlowKind =
56 bindings::greentic_common_types_0_1_0_common::exports::greentic::common_types::types::FlowKind;
57type WitCommonTenantCtx =
58 bindings::greentic_common_types_0_1_0_common::exports::greentic::common_types::types::TenantCtx;
59type WitOutcomeStatus = bindings::greentic_common_types_0_1_0_common::exports::greentic::common_types::types::OutcomeStatus;
60type WitComponentOutcome = bindings::greentic_common_types_0_1_0_common::exports::greentic::common_types::types::ComponentOutcome;
61type WitPackKind = bindings::greentic_pack_export_v1_0_1_0_pack_host::exports::greentic::pack_export_v1::pack_api::PackKind;
62type WitPackDescriptor =
63 bindings::greentic_pack_export_v1_0_1_0_pack_host::exports::greentic::pack_export_v1::pack_api::PackDescriptor;
64type WitFlowDescriptor =
65 bindings::greentic_pack_export_v1_0_1_0_pack_host::exports::greentic::pack_export_v1::pack_api::FlowDescriptor;
66type WitPackFlowKind =
67 bindings::greentic_pack_export_v1_0_1_0_pack_host::greentic::common_types::types::FlowKind;
68
69#[derive(Clone, Debug, PartialEq, Eq)]
71pub enum ComponentOutcomeStatus {
72 Done,
74 Pending,
76 Error,
78}
79
80#[derive(Clone, Debug, PartialEq, Eq)]
82pub struct ComponentOutcome {
83 pub status: ComponentOutcomeStatus,
85 pub code: Option<String>,
87 pub payload: String,
89 pub metadata: Option<String>,
91}
92
93#[derive(Clone, Debug, PartialEq, Eq)]
95pub struct PackDescriptor {
96 pub pack_id: types::PackId,
98 pub version: Version,
100 pub kind: types::PackKind,
102 pub publisher: String,
104}
105
106#[derive(Clone, Debug, PartialEq, Eq)]
108pub struct FlowDescriptor {
109 pub id: types::FlowId,
111 pub kind: types::FlowKind,
113 pub tags: Vec<String>,
115 pub entrypoints: Vec<String>,
117}
118
119pub fn tenant_ctx_from_wit(ctx: WitTenantCtx) -> MapperResult<types::TenantCtx> {
121 types::TenantCtx::try_from(ctx)
122}
123
124pub fn tenant_ctx_to_wit(ctx: types::TenantCtx) -> MapperResult<WitTenantCtx> {
126 WitTenantCtx::try_from(ctx)
127}
128
129impl TryFrom<WitImpersonation> for types::Impersonation {
130 type Error = types::GreenticError;
131
132 fn try_from(value: WitImpersonation) -> MapperResult<Self> {
133 Ok(Self {
134 actor_id: value.actor_id.try_into()?,
135 reason: value.reason,
136 })
137 }
138}
139
140impl From<types::Impersonation> for WitImpersonation {
141 fn from(value: types::Impersonation) -> Self {
142 Self {
143 actor_id: value.actor_id.into(),
144 reason: value.reason,
145 }
146 }
147}
148
149impl TryFrom<WitTenantCtx> for types::TenantCtx {
150 type Error = types::GreenticError;
151
152 fn try_from(value: WitTenantCtx) -> MapperResult<Self> {
153 let WitTenantCtx {
154 env,
155 tenant,
156 tenant_id,
157 team,
158 team_id,
159 user,
160 user_id,
161 trace_id,
162 correlation_id,
163 session_id,
164 flow_id,
165 node_id,
166 provider_id,
167 deadline_ms,
168 attempt,
169 idempotency_key,
170 impersonation,
171 attributes,
172 } = value;
173
174 let deadline =
175 deadline_ms.map(|ms| types::InvocationDeadline::from_unix_millis(ms as i128));
176
177 let env = env.try_into()?;
178 let tenant = tenant.try_into()?;
179 let tenant_id = tenant_id.try_into()?;
180 let team = team.map(|item| item.try_into()).transpose()?;
181 let team_id = team_id.map(|item| item.try_into()).transpose()?;
182 let user = user.map(|item| item.try_into()).transpose()?;
183 let user_id = user_id.map(|item| item.try_into()).transpose()?;
184 let impersonation = impersonation
185 .map(types::Impersonation::try_from)
186 .transpose()?;
187 let attributes: BTreeMap<String, String> = attributes.into_iter().collect();
188
189 Ok(Self {
190 env,
191 tenant,
192 tenant_id,
193 team,
194 team_id,
195 user,
196 user_id,
197 session_id,
198 flow_id,
199 node_id,
200 provider_id,
201 trace_id,
202 correlation_id,
203 attributes,
204 deadline,
205 attempt,
206 idempotency_key,
207 impersonation,
208 })
209 }
210}
211
212impl TryFrom<types::TenantCtx> for WitTenantCtx {
213 type Error = types::GreenticError;
214
215 fn try_from(value: types::TenantCtx) -> MapperResult<Self> {
216 let deadline_ms = match value.deadline {
217 Some(deadline) => Some(i128_to_i64(deadline.unix_millis())?),
218 None => None,
219 };
220 let attributes: Vec<(String, String)> = value.attributes.into_iter().collect();
221
222 Ok(Self {
223 env: value.env.into(),
224 tenant: value.tenant.into(),
225 tenant_id: value.tenant_id.into(),
226 team: value.team.map(Into::into),
227 team_id: value.team_id.map(Into::into),
228 user: value.user.map(Into::into),
229 user_id: value.user_id.map(Into::into),
230 session_id: value.session_id,
231 flow_id: value.flow_id,
232 node_id: value.node_id,
233 provider_id: value.provider_id,
234 trace_id: value.trace_id,
235 correlation_id: value.correlation_id,
236 attributes,
237 deadline_ms,
238 attempt: value.attempt,
239 idempotency_key: value.idempotency_key,
240 impersonation: value.impersonation.map(Into::into),
241 })
242 }
243}
244
245impl From<WitSessionCursor> for types::SessionCursor {
246 fn from(value: WitSessionCursor) -> Self {
247 Self {
248 node_pointer: value.node_pointer,
249 wait_reason: value.wait_reason,
250 outbox_marker: value.outbox_marker,
251 }
252 }
253}
254
255impl From<types::SessionCursor> for WitSessionCursor {
256 fn from(value: types::SessionCursor) -> Self {
257 Self {
258 node_pointer: value.node_pointer,
259 wait_reason: value.wait_reason,
260 outbox_marker: value.outbox_marker,
261 }
262 }
263}
264
265impl From<WitErrorCode> for types::ErrorCode {
266 fn from(value: WitErrorCode) -> Self {
267 match value {
268 WitErrorCode::Unknown => Self::Unknown,
269 WitErrorCode::InvalidInput => Self::InvalidInput,
270 WitErrorCode::NotFound => Self::NotFound,
271 WitErrorCode::Conflict => Self::Conflict,
272 WitErrorCode::Timeout => Self::Timeout,
273 WitErrorCode::Unauthenticated => Self::Unauthenticated,
274 WitErrorCode::PermissionDenied => Self::PermissionDenied,
275 WitErrorCode::RateLimited => Self::RateLimited,
276 WitErrorCode::Unavailable => Self::Unavailable,
277 WitErrorCode::Internal => Self::Internal,
278 }
279 }
280}
281
282impl From<types::ErrorCode> for WitErrorCode {
283 fn from(value: types::ErrorCode) -> Self {
284 match value {
285 types::ErrorCode::Unknown => Self::Unknown,
286 types::ErrorCode::InvalidInput => Self::InvalidInput,
287 types::ErrorCode::NotFound => Self::NotFound,
288 types::ErrorCode::Conflict => Self::Conflict,
289 types::ErrorCode::Timeout => Self::Timeout,
290 types::ErrorCode::Unauthenticated => Self::Unauthenticated,
291 types::ErrorCode::PermissionDenied => Self::PermissionDenied,
292 types::ErrorCode::RateLimited => Self::RateLimited,
293 types::ErrorCode::Unavailable => Self::Unavailable,
294 types::ErrorCode::Internal => Self::Internal,
295 }
296 }
297}
298
299impl From<WitOutcome> for types::Outcome<String> {
300 fn from(value: WitOutcome) -> Self {
301 match value {
302 WitOutcome::Done(val) => Self::Done(val),
303 WitOutcome::Pending(payload) => Self::Pending {
304 reason: payload.reason,
305 expected_input: payload.expected_input,
306 },
307 WitOutcome::Error(payload) => Self::Error {
308 code: payload.code.into(),
309 message: payload.message,
310 },
311 }
312 }
313}
314
315impl From<types::Outcome<String>> for WitOutcome {
316 fn from(value: types::Outcome<String>) -> Self {
317 match value {
318 types::Outcome::Done(val) => Self::Done(val),
319 types::Outcome::Pending {
320 reason,
321 expected_input,
322 } => Self::Pending(WitOutcomePending {
323 reason,
324 expected_input,
325 }),
326 types::Outcome::Error { code, message } => Self::Error(WitOutcomeError {
327 code: code.into(),
328 message,
329 }),
330 }
331 }
332}
333
334impl From<WitProtocol> for types::Protocol {
335 fn from(value: WitProtocol) -> Self {
336 match value {
337 WitProtocol::Http => Self::Http,
338 WitProtocol::Https => Self::Https,
339 WitProtocol::Tcp => Self::Tcp,
340 WitProtocol::Udp => Self::Udp,
341 WitProtocol::Grpc => Self::Grpc,
342 WitProtocol::Custom(v) => Self::Custom(v),
343 }
344 }
345}
346
347impl From<types::Protocol> for WitProtocol {
348 fn from(value: types::Protocol) -> Self {
349 match value {
350 types::Protocol::Http => Self::Http,
351 types::Protocol::Https => Self::Https,
352 types::Protocol::Tcp => Self::Tcp,
353 types::Protocol::Udp => Self::Udp,
354 types::Protocol::Grpc => Self::Grpc,
355 types::Protocol::Custom(v) => Self::Custom(v),
356 }
357 }
358}
359
360impl From<WitAllowList> for types::AllowList {
361 fn from(value: WitAllowList) -> Self {
362 Self {
363 domains: value.domains,
364 ports: value.ports,
365 protocols: value.protocols.into_iter().map(Into::into).collect(),
366 }
367 }
368}
369
370impl From<types::AllowList> for WitAllowList {
371 fn from(value: types::AllowList) -> Self {
372 Self {
373 domains: value.domains,
374 ports: value.ports,
375 protocols: value.protocols.into_iter().map(Into::into).collect(),
376 }
377 }
378}
379
380impl From<WitNetworkPolicy> for types::NetworkPolicy {
381 fn from(value: WitNetworkPolicy) -> Self {
382 Self {
383 egress: value.egress.into(),
384 deny_on_miss: value.deny_on_miss,
385 }
386 }
387}
388
389impl From<types::NetworkPolicy> for WitNetworkPolicy {
390 fn from(value: types::NetworkPolicy) -> Self {
391 Self {
392 egress: value.egress.into(),
393 deny_on_miss: value.deny_on_miss,
394 }
395 }
396}
397
398impl TryFrom<WitSpanContext> for types::SpanContext {
399 type Error = types::GreenticError;
400
401 fn try_from(value: WitSpanContext) -> MapperResult<Self> {
402 let WitSpanContext {
403 tenant,
404 session_id,
405 flow_id,
406 node_id,
407 provider,
408 start_ms,
409 end_ms,
410 } = value;
411
412 let start = start_ms.map(timestamp_ms_to_offset).transpose()?;
413 let end = end_ms.map(timestamp_ms_to_offset).transpose()?;
414 let tenant = tenant.try_into()?;
415
416 Ok(Self {
417 tenant,
418 session_id: session_id.map(types::SessionKey::from),
419 flow_id,
420 node_id,
421 provider,
422 start,
423 end,
424 })
425 }
426}
427
428impl TryFrom<types::SpanContext> for WitSpanContext {
429 type Error = types::GreenticError;
430
431 fn try_from(value: types::SpanContext) -> MapperResult<Self> {
432 let start_ms = value
433 .start
434 .as_ref()
435 .map(offset_to_timestamp_ms)
436 .transpose()?;
437 let end_ms = value.end.as_ref().map(offset_to_timestamp_ms).transpose()?;
438
439 Ok(Self {
440 tenant: value.tenant.into(),
441 session_id: value.session_id.map(|key| key.to_string()),
442 flow_id: value.flow_id,
443 node_id: value.node_id,
444 provider: value.provider,
445 start_ms,
446 end_ms,
447 })
448 }
449}
450
451impl From<WitSignatureAlgorithm> for types::SignatureAlgorithm {
452 fn from(value: WitSignatureAlgorithm) -> Self {
453 match value {
454 WitSignatureAlgorithm::Ed25519 => Self::Ed25519,
455 WitSignatureAlgorithm::Other(v) => Self::Other(v),
456 }
457 }
458}
459
460impl From<types::SignatureAlgorithm> for WitSignatureAlgorithm {
461 fn from(value: types::SignatureAlgorithm) -> Self {
462 match value {
463 types::SignatureAlgorithm::Ed25519 => Self::Ed25519,
464 types::SignatureAlgorithm::Other(v) => Self::Other(v),
465 }
466 }
467}
468
469impl From<WitSignature> for types::Signature {
470 fn from(value: WitSignature) -> Self {
471 Self {
472 key_id: value.key_id,
473 algorithm: value.algorithm.into(),
474 signature: value.signature,
475 }
476 }
477}
478
479impl From<types::Signature> for WitSignature {
480 fn from(value: types::Signature) -> Self {
481 Self {
482 key_id: value.key_id,
483 algorithm: value.algorithm.into(),
484 signature: value.signature,
485 }
486 }
487}
488
489impl TryFrom<WitPackRef> for types::PackRef {
490 type Error = types::GreenticError;
491
492 fn try_from(value: WitPackRef) -> MapperResult<Self> {
493 let version = Version::parse(&value.version)
494 .map_err(|err| invalid_input(format!("invalid version: {err}")))?;
495 Ok(Self {
496 oci_url: value.oci_url,
497 version,
498 digest: value.digest,
499 signatures: value.signatures.into_iter().map(Into::into).collect(),
500 })
501 }
502}
503
504impl From<types::PackRef> for WitPackRef {
505 fn from(value: types::PackRef) -> Self {
506 Self {
507 oci_url: value.oci_url,
508 version: value.version.to_string(),
509 digest: value.digest,
510 signatures: value.signatures.into_iter().map(Into::into).collect(),
511 }
512 }
513}
514
515pub fn flow_kind_to_wit(kind: types::FlowKind) -> WitCommonFlowKind {
517 match kind {
518 types::FlowKind::Messaging => WitCommonFlowKind::Messaging,
519 types::FlowKind::Event => WitCommonFlowKind::Event,
520 types::FlowKind::ComponentConfig => WitCommonFlowKind::ComponentConfig,
521 types::FlowKind::Job => WitCommonFlowKind::Job,
522 types::FlowKind::Http => WitCommonFlowKind::Http,
523 }
524}
525
526pub fn flow_kind_from_wit(kind: WitCommonFlowKind) -> types::FlowKind {
528 match kind {
529 WitCommonFlowKind::Messaging => types::FlowKind::Messaging,
530 WitCommonFlowKind::Event => types::FlowKind::Event,
531 WitCommonFlowKind::ComponentConfig => types::FlowKind::ComponentConfig,
532 WitCommonFlowKind::Job => types::FlowKind::Job,
533 WitCommonFlowKind::Http => types::FlowKind::Http,
534 }
535}
536
537fn flow_kind_from_pack_wit(kind: WitPackFlowKind) -> types::FlowKind {
538 match kind {
539 WitPackFlowKind::Messaging => types::FlowKind::Messaging,
540 WitPackFlowKind::Event => types::FlowKind::Event,
541 WitPackFlowKind::ComponentConfig => types::FlowKind::ComponentConfig,
542 WitPackFlowKind::Job => types::FlowKind::Job,
543 WitPackFlowKind::Http => types::FlowKind::Http,
544 }
545}
546
547fn flow_kind_to_pack_wit(kind: types::FlowKind) -> WitPackFlowKind {
548 match kind {
549 types::FlowKind::Messaging => WitPackFlowKind::Messaging,
550 types::FlowKind::Event => WitPackFlowKind::Event,
551 types::FlowKind::ComponentConfig => WitPackFlowKind::ComponentConfig,
552 types::FlowKind::Job => WitPackFlowKind::Job,
553 types::FlowKind::Http => WitPackFlowKind::Http,
554 }
555}
556
557pub fn pack_kind_to_wit(kind: types::PackKind) -> WitPackKind {
559 match kind {
560 types::PackKind::Application => WitPackKind::Application,
561 types::PackKind::Provider => WitPackKind::Provider,
562 types::PackKind::Infrastructure => WitPackKind::Infrastructure,
563 types::PackKind::Library => WitPackKind::Library,
564 }
565}
566
567pub fn pack_kind_from_wit(kind: WitPackKind) -> types::PackKind {
569 match kind {
570 WitPackKind::Application => types::PackKind::Application,
571 WitPackKind::Provider => types::PackKind::Provider,
572 WitPackKind::Infrastructure => types::PackKind::Infrastructure,
573 WitPackKind::Library => types::PackKind::Library,
574 }
575}
576
577pub fn tenant_ctx_from_common(ctx: WitCommonTenantCtx) -> MapperResult<types::TenantCtx> {
579 let WitCommonTenantCtx {
580 env,
581 tenant_id,
582 team_id,
583 user_id,
584 session_id,
585 flow_id,
586 node_id,
587 } = ctx;
588
589 Ok(types::TenantCtx {
590 env: env.try_into()?,
591 tenant: tenant_id.clone().try_into()?,
592 tenant_id: tenant_id.try_into()?,
593 team: team_id.clone().map(|id| id.try_into()).transpose()?,
594 team_id: team_id.map(|id| id.try_into()).transpose()?,
595 user: user_id.clone().map(|id| id.try_into()).transpose()?,
596 user_id: user_id.map(|id| id.try_into()).transpose()?,
597 session_id,
598 flow_id,
599 node_id,
600 provider_id: None,
601 trace_id: None,
602 correlation_id: None,
603 attributes: BTreeMap::new(),
604 deadline: None,
605 attempt: 0,
606 idempotency_key: None,
607 impersonation: None,
608 })
609}
610
611pub fn tenant_ctx_to_common(ctx: types::TenantCtx) -> MapperResult<WitCommonTenantCtx> {
613 Ok(WitCommonTenantCtx {
614 env: ctx.env.into(),
615 tenant_id: ctx.tenant_id.into(),
616 team_id: ctx.team_id.map(Into::into),
617 user_id: ctx.user_id.map(Into::into),
618 session_id: ctx.session_id,
619 flow_id: ctx.flow_id,
620 node_id: ctx.node_id,
621 })
622}
623
624pub fn component_outcome_from_wit(outcome: WitComponentOutcome) -> ComponentOutcome {
626 let status = match outcome.status {
627 WitOutcomeStatus::Done => ComponentOutcomeStatus::Done,
628 WitOutcomeStatus::Pending => ComponentOutcomeStatus::Pending,
629 WitOutcomeStatus::Error => ComponentOutcomeStatus::Error,
630 };
631
632 ComponentOutcome {
633 status,
634 code: outcome.code,
635 payload: outcome.payload_json,
636 metadata: outcome.metadata_json,
637 }
638}
639
640pub fn component_outcome_to_wit(outcome: ComponentOutcome) -> WitComponentOutcome {
642 let status = match outcome.status {
643 ComponentOutcomeStatus::Done => WitOutcomeStatus::Done,
644 ComponentOutcomeStatus::Pending => WitOutcomeStatus::Pending,
645 ComponentOutcomeStatus::Error => WitOutcomeStatus::Error,
646 };
647
648 WitComponentOutcome {
649 status,
650 code: outcome.code,
651 payload_json: outcome.payload,
652 metadata_json: outcome.metadata,
653 }
654}
655
656pub fn pack_descriptor_from_wit(desc: WitPackDescriptor) -> MapperResult<PackDescriptor> {
658 Ok(PackDescriptor {
659 pack_id: desc.pack_id.try_into()?,
660 version: Version::parse(&desc.version)
661 .map_err(|err| invalid_input(format!("invalid version: {err}")))?,
662 kind: pack_kind_from_wit(desc.kind),
663 publisher: desc.publisher,
664 })
665}
666
667pub fn pack_descriptor_to_wit(desc: PackDescriptor) -> WitPackDescriptor {
669 WitPackDescriptor {
670 pack_id: desc.pack_id.into(),
671 version: desc.version.to_string(),
672 kind: pack_kind_to_wit(desc.kind),
673 publisher: desc.publisher,
674 }
675}
676
677pub fn flow_descriptor_from_wit(desc: WitFlowDescriptor) -> MapperResult<FlowDescriptor> {
679 Ok(FlowDescriptor {
680 id: desc.id.try_into()?,
681 kind: flow_kind_from_pack_wit(desc.kind),
682 tags: desc.tags,
683 entrypoints: desc.entrypoints,
684 })
685}
686
687pub fn flow_descriptor_to_wit(desc: FlowDescriptor) -> WitFlowDescriptor {
689 WitFlowDescriptor {
690 id: desc.id.into(),
691 kind: flow_kind_to_pack_wit(desc.kind),
692 tags: desc.tags,
693 entrypoints: desc.entrypoints,
694 }
695}
696
697#[cfg(test)]
698mod tests {
699 use super::*;
700 use std::convert::TryFrom;
701
702 fn fixture_id<T>(value: &str) -> T
703 where
704 T: TryFrom<String, Error = types::GreenticError>,
705 {
706 T::try_from(value.to_owned())
707 .unwrap_or_else(|err| panic!("invalid fixture identifier '{value}': {err}"))
708 }
709
710 fn sample_tenant_ctx() -> types::TenantCtx {
711 types::TenantCtx {
712 env: fixture_id("prod"),
713 tenant: fixture_id("tenant-1"),
714 tenant_id: fixture_id("tenant-1"),
715 team: Some(fixture_id("team-42")),
716 team_id: Some(fixture_id("team-42")),
717 user: Some(fixture_id("user-7")),
718 user_id: Some(fixture_id("user-7")),
719 attributes: BTreeMap::new(),
720 session_id: Some("sess-42".into()),
721 flow_id: Some("flow-42".into()),
722 node_id: Some("node-42".into()),
723 provider_id: Some("provider-42".into()),
724 trace_id: Some("trace".into()),
725 correlation_id: Some("corr".into()),
726 deadline: Some(types::InvocationDeadline::from_unix_millis(
727 1_700_000_000_000,
728 )),
729 attempt: 2,
730 idempotency_key: Some("idem".into()),
731 impersonation: Some(types::Impersonation {
732 actor_id: fixture_id("actor"),
733 reason: Some("maintenance".into()),
734 }),
735 }
736 }
737
738 #[test]
739 fn tenant_ctx_roundtrip() {
740 let ctx = sample_tenant_ctx();
741 let wit = match WitTenantCtx::try_from(ctx.clone()) {
742 Ok(value) => value,
743 Err(err) => panic!("failed to map to wit: {err}"),
744 };
745 let back = match types::TenantCtx::try_from(wit) {
746 Ok(value) => value,
747 Err(err) => panic!("failed to map from wit: {err}"),
748 };
749 assert_eq!(back.env.as_str(), ctx.env.as_str());
750 assert_eq!(back.tenant.as_str(), ctx.tenant.as_str());
751 assert!(back.impersonation.is_some());
752 assert!(ctx.impersonation.is_some());
753 assert_eq!(
754 back.impersonation.as_ref().map(|imp| imp.actor_id.as_str()),
755 ctx.impersonation.as_ref().map(|imp| imp.actor_id.as_str())
756 );
757 assert_eq!(back.session_id, ctx.session_id);
758 assert_eq!(back.flow_id, ctx.flow_id);
759 assert_eq!(back.node_id, ctx.node_id);
760 assert_eq!(back.provider_id, ctx.provider_id);
761 }
762
763 #[test]
764 fn outcome_roundtrip() {
765 let pending = types::Outcome::Pending {
766 reason: "waiting".into(),
767 expected_input: Some(vec!["input".into()]),
768 };
769 let wit = WitOutcome::from(pending.clone());
770 let back = types::Outcome::from(wit);
771 match back {
772 types::Outcome::Pending { reason, .. } => {
773 assert_eq!(reason, "waiting");
774 }
775 _ => panic!("expected pending"),
776 }
777 }
778}