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 i18n_id,
163 correlation_id,
164 session_id,
165 flow_id,
166 node_id,
167 provider_id,
168 deadline_ms,
169 attempt,
170 idempotency_key,
171 impersonation,
172 attributes,
173 } = value;
174
175 let deadline =
176 deadline_ms.map(|ms| types::InvocationDeadline::from_unix_millis(ms as i128));
177
178 let env = env.try_into()?;
179 let tenant = tenant.try_into()?;
180 let tenant_id = tenant_id.try_into()?;
181 let team = team.map(|item| item.try_into()).transpose()?;
182 let team_id = team_id.map(|item| item.try_into()).transpose()?;
183 let user = user.map(|item| item.try_into()).transpose()?;
184 let user_id = user_id.map(|item| item.try_into()).transpose()?;
185 let impersonation = impersonation
186 .map(types::Impersonation::try_from)
187 .transpose()?;
188 let attributes: BTreeMap<String, String> = attributes.into_iter().collect();
189
190 Ok(Self {
191 env,
192 tenant,
193 tenant_id,
194 team,
195 team_id,
196 user,
197 user_id,
198 session_id,
199 flow_id,
200 node_id,
201 provider_id,
202 trace_id,
203 i18n_id,
204 correlation_id,
205 attributes,
206 deadline,
207 attempt,
208 idempotency_key,
209 impersonation,
210 })
211 }
212}
213
214impl TryFrom<types::TenantCtx> for WitTenantCtx {
215 type Error = types::GreenticError;
216
217 fn try_from(value: types::TenantCtx) -> MapperResult<Self> {
218 let deadline_ms = match value.deadline {
219 Some(deadline) => Some(i128_to_i64(deadline.unix_millis())?),
220 None => None,
221 };
222 let attributes: Vec<(String, String)> = value.attributes.into_iter().collect();
223
224 Ok(Self {
225 env: value.env.into(),
226 tenant: value.tenant.into(),
227 tenant_id: value.tenant_id.into(),
228 team: value.team.map(Into::into),
229 team_id: value.team_id.map(Into::into),
230 user: value.user.map(Into::into),
231 user_id: value.user_id.map(Into::into),
232 session_id: value.session_id,
233 flow_id: value.flow_id,
234 node_id: value.node_id,
235 provider_id: value.provider_id,
236 trace_id: value.trace_id,
237 i18n_id: value.i18n_id.clone(),
238 correlation_id: value.correlation_id,
239 attributes,
240 deadline_ms,
241 attempt: value.attempt,
242 idempotency_key: value.idempotency_key,
243 impersonation: value.impersonation.map(Into::into),
244 })
245 }
246}
247
248impl From<WitSessionCursor> for types::SessionCursor {
249 fn from(value: WitSessionCursor) -> Self {
250 Self {
251 node_pointer: value.node_pointer,
252 wait_reason: value.wait_reason,
253 outbox_marker: value.outbox_marker,
254 }
255 }
256}
257
258impl From<types::SessionCursor> for WitSessionCursor {
259 fn from(value: types::SessionCursor) -> Self {
260 Self {
261 node_pointer: value.node_pointer,
262 wait_reason: value.wait_reason,
263 outbox_marker: value.outbox_marker,
264 }
265 }
266}
267
268impl From<WitErrorCode> for types::ErrorCode {
269 fn from(value: WitErrorCode) -> Self {
270 match value {
271 WitErrorCode::Unknown => Self::Unknown,
272 WitErrorCode::InvalidInput => Self::InvalidInput,
273 WitErrorCode::NotFound => Self::NotFound,
274 WitErrorCode::Conflict => Self::Conflict,
275 WitErrorCode::Timeout => Self::Timeout,
276 WitErrorCode::Unauthenticated => Self::Unauthenticated,
277 WitErrorCode::PermissionDenied => Self::PermissionDenied,
278 WitErrorCode::RateLimited => Self::RateLimited,
279 WitErrorCode::Unavailable => Self::Unavailable,
280 WitErrorCode::Internal => Self::Internal,
281 }
282 }
283}
284
285impl From<types::ErrorCode> for WitErrorCode {
286 fn from(value: types::ErrorCode) -> Self {
287 match value {
288 types::ErrorCode::Unknown => Self::Unknown,
289 types::ErrorCode::InvalidInput => Self::InvalidInput,
290 types::ErrorCode::NotFound => Self::NotFound,
291 types::ErrorCode::Conflict => Self::Conflict,
292 types::ErrorCode::Timeout => Self::Timeout,
293 types::ErrorCode::Unauthenticated => Self::Unauthenticated,
294 types::ErrorCode::PermissionDenied => Self::PermissionDenied,
295 types::ErrorCode::RateLimited => Self::RateLimited,
296 types::ErrorCode::Unavailable => Self::Unavailable,
297 types::ErrorCode::Internal => Self::Internal,
298 }
299 }
300}
301
302impl From<WitOutcome> for types::Outcome<String> {
303 fn from(value: WitOutcome) -> Self {
304 match value {
305 WitOutcome::Done(val) => Self::Done(val),
306 WitOutcome::Pending(payload) => Self::Pending {
307 reason: payload.reason,
308 expected_input: payload.expected_input,
309 },
310 WitOutcome::Error(payload) => Self::Error {
311 code: payload.code.into(),
312 message: payload.message,
313 },
314 }
315 }
316}
317
318impl From<types::Outcome<String>> for WitOutcome {
319 fn from(value: types::Outcome<String>) -> Self {
320 match value {
321 types::Outcome::Done(val) => Self::Done(val),
322 types::Outcome::Pending {
323 reason,
324 expected_input,
325 } => Self::Pending(WitOutcomePending {
326 reason,
327 expected_input,
328 }),
329 types::Outcome::Error { code, message } => Self::Error(WitOutcomeError {
330 code: code.into(),
331 message,
332 }),
333 }
334 }
335}
336
337impl From<WitProtocol> for types::Protocol {
338 fn from(value: WitProtocol) -> Self {
339 match value {
340 WitProtocol::Http => Self::Http,
341 WitProtocol::Https => Self::Https,
342 WitProtocol::Tcp => Self::Tcp,
343 WitProtocol::Udp => Self::Udp,
344 WitProtocol::Grpc => Self::Grpc,
345 WitProtocol::Custom(v) => Self::Custom(v),
346 }
347 }
348}
349
350impl From<types::Protocol> for WitProtocol {
351 fn from(value: types::Protocol) -> Self {
352 match value {
353 types::Protocol::Http => Self::Http,
354 types::Protocol::Https => Self::Https,
355 types::Protocol::Tcp => Self::Tcp,
356 types::Protocol::Udp => Self::Udp,
357 types::Protocol::Grpc => Self::Grpc,
358 types::Protocol::Custom(v) => Self::Custom(v),
359 }
360 }
361}
362
363impl From<WitAllowList> for types::AllowList {
364 fn from(value: WitAllowList) -> Self {
365 Self {
366 domains: value.domains,
367 ports: value.ports,
368 protocols: value.protocols.into_iter().map(Into::into).collect(),
369 }
370 }
371}
372
373impl From<types::AllowList> for WitAllowList {
374 fn from(value: types::AllowList) -> Self {
375 Self {
376 domains: value.domains,
377 ports: value.ports,
378 protocols: value.protocols.into_iter().map(Into::into).collect(),
379 }
380 }
381}
382
383impl From<WitNetworkPolicy> for types::NetworkPolicy {
384 fn from(value: WitNetworkPolicy) -> Self {
385 Self {
386 egress: value.egress.into(),
387 deny_on_miss: value.deny_on_miss,
388 }
389 }
390}
391
392impl From<types::NetworkPolicy> for WitNetworkPolicy {
393 fn from(value: types::NetworkPolicy) -> Self {
394 Self {
395 egress: value.egress.into(),
396 deny_on_miss: value.deny_on_miss,
397 }
398 }
399}
400
401impl TryFrom<WitSpanContext> for types::SpanContext {
402 type Error = types::GreenticError;
403
404 fn try_from(value: WitSpanContext) -> MapperResult<Self> {
405 let WitSpanContext {
406 tenant,
407 session_id,
408 flow_id,
409 node_id,
410 provider,
411 start_ms,
412 end_ms,
413 } = value;
414
415 let start = start_ms.map(timestamp_ms_to_offset).transpose()?;
416 let end = end_ms.map(timestamp_ms_to_offset).transpose()?;
417 let tenant = tenant.try_into()?;
418
419 Ok(Self {
420 tenant,
421 session_id: session_id.map(types::SessionKey::from),
422 flow_id,
423 node_id,
424 provider,
425 start,
426 end,
427 })
428 }
429}
430
431impl TryFrom<types::SpanContext> for WitSpanContext {
432 type Error = types::GreenticError;
433
434 fn try_from(value: types::SpanContext) -> MapperResult<Self> {
435 let start_ms = value
436 .start
437 .as_ref()
438 .map(offset_to_timestamp_ms)
439 .transpose()?;
440 let end_ms = value.end.as_ref().map(offset_to_timestamp_ms).transpose()?;
441
442 Ok(Self {
443 tenant: value.tenant.into(),
444 session_id: value.session_id.map(|key| key.to_string()),
445 flow_id: value.flow_id,
446 node_id: value.node_id,
447 provider: value.provider,
448 start_ms,
449 end_ms,
450 })
451 }
452}
453
454impl From<WitSignatureAlgorithm> for types::SignatureAlgorithm {
455 fn from(value: WitSignatureAlgorithm) -> Self {
456 match value {
457 WitSignatureAlgorithm::Ed25519 => Self::Ed25519,
458 WitSignatureAlgorithm::Other(v) => Self::Other(v),
459 }
460 }
461}
462
463impl From<types::SignatureAlgorithm> for WitSignatureAlgorithm {
464 fn from(value: types::SignatureAlgorithm) -> Self {
465 match value {
466 types::SignatureAlgorithm::Ed25519 => Self::Ed25519,
467 types::SignatureAlgorithm::Other(v) => Self::Other(v),
468 }
469 }
470}
471
472impl From<WitSignature> for types::Signature {
473 fn from(value: WitSignature) -> Self {
474 Self {
475 key_id: value.key_id,
476 algorithm: value.algorithm.into(),
477 signature: value.signature,
478 }
479 }
480}
481
482impl From<types::Signature> for WitSignature {
483 fn from(value: types::Signature) -> Self {
484 Self {
485 key_id: value.key_id,
486 algorithm: value.algorithm.into(),
487 signature: value.signature,
488 }
489 }
490}
491
492impl TryFrom<WitPackRef> for types::PackRef {
493 type Error = types::GreenticError;
494
495 fn try_from(value: WitPackRef) -> MapperResult<Self> {
496 let version = Version::parse(&value.version)
497 .map_err(|err| invalid_input(format!("invalid version: {err}")))?;
498 Ok(Self {
499 oci_url: value.oci_url,
500 version,
501 digest: value.digest,
502 signatures: value.signatures.into_iter().map(Into::into).collect(),
503 })
504 }
505}
506
507impl From<types::PackRef> for WitPackRef {
508 fn from(value: types::PackRef) -> Self {
509 Self {
510 oci_url: value.oci_url,
511 version: value.version.to_string(),
512 digest: value.digest,
513 signatures: value.signatures.into_iter().map(Into::into).collect(),
514 }
515 }
516}
517
518pub fn flow_kind_to_wit(kind: types::FlowKind) -> WitCommonFlowKind {
520 match kind {
521 types::FlowKind::Messaging => WitCommonFlowKind::Messaging,
522 types::FlowKind::Event => WitCommonFlowKind::Event,
523 types::FlowKind::ComponentConfig => WitCommonFlowKind::ComponentConfig,
524 types::FlowKind::Job => WitCommonFlowKind::Job,
525 types::FlowKind::Http => WitCommonFlowKind::Http,
526 }
527}
528
529pub fn flow_kind_from_wit(kind: WitCommonFlowKind) -> types::FlowKind {
531 match kind {
532 WitCommonFlowKind::Messaging => types::FlowKind::Messaging,
533 WitCommonFlowKind::Event => types::FlowKind::Event,
534 WitCommonFlowKind::ComponentConfig => types::FlowKind::ComponentConfig,
535 WitCommonFlowKind::Job => types::FlowKind::Job,
536 WitCommonFlowKind::Http => types::FlowKind::Http,
537 }
538}
539
540fn flow_kind_from_pack_wit(kind: WitPackFlowKind) -> types::FlowKind {
541 match kind {
542 WitPackFlowKind::Messaging => types::FlowKind::Messaging,
543 WitPackFlowKind::Event => types::FlowKind::Event,
544 WitPackFlowKind::ComponentConfig => types::FlowKind::ComponentConfig,
545 WitPackFlowKind::Job => types::FlowKind::Job,
546 WitPackFlowKind::Http => types::FlowKind::Http,
547 }
548}
549
550fn flow_kind_to_pack_wit(kind: types::FlowKind) -> WitPackFlowKind {
551 match kind {
552 types::FlowKind::Messaging => WitPackFlowKind::Messaging,
553 types::FlowKind::Event => WitPackFlowKind::Event,
554 types::FlowKind::ComponentConfig => WitPackFlowKind::ComponentConfig,
555 types::FlowKind::Job => WitPackFlowKind::Job,
556 types::FlowKind::Http => WitPackFlowKind::Http,
557 }
558}
559
560pub fn pack_kind_to_wit(kind: types::PackKind) -> WitPackKind {
562 match kind {
563 types::PackKind::Application => WitPackKind::Application,
564 types::PackKind::Provider => WitPackKind::Provider,
565 types::PackKind::Infrastructure => WitPackKind::Infrastructure,
566 types::PackKind::Library => WitPackKind::Library,
567 }
568}
569
570pub fn pack_kind_from_wit(kind: WitPackKind) -> types::PackKind {
572 match kind {
573 WitPackKind::Application => types::PackKind::Application,
574 WitPackKind::Provider => types::PackKind::Provider,
575 WitPackKind::Infrastructure => types::PackKind::Infrastructure,
576 WitPackKind::Library => types::PackKind::Library,
577 }
578}
579
580pub fn tenant_ctx_from_common(ctx: WitCommonTenantCtx) -> MapperResult<types::TenantCtx> {
582 let WitCommonTenantCtx {
583 env,
584 tenant_id,
585 team_id,
586 user_id,
587 session_id,
588 flow_id,
589 node_id,
590 } = ctx;
591
592 Ok(types::TenantCtx {
593 env: env.try_into()?,
594 tenant: tenant_id.clone().try_into()?,
595 tenant_id: tenant_id.try_into()?,
596 team: team_id.clone().map(|id| id.try_into()).transpose()?,
597 team_id: team_id.map(|id| id.try_into()).transpose()?,
598 user: user_id.clone().map(|id| id.try_into()).transpose()?,
599 user_id: user_id.map(|id| id.try_into()).transpose()?,
600 session_id,
601 flow_id,
602 node_id,
603 provider_id: None,
604 trace_id: None,
605 i18n_id: None,
606 correlation_id: None,
607 attributes: BTreeMap::new(),
608 deadline: None,
609 attempt: 0,
610 idempotency_key: None,
611 impersonation: None,
612 })
613}
614
615pub fn tenant_ctx_to_common(ctx: types::TenantCtx) -> MapperResult<WitCommonTenantCtx> {
617 Ok(WitCommonTenantCtx {
618 env: ctx.env.into(),
619 tenant_id: ctx.tenant_id.into(),
620 team_id: ctx.team_id.map(Into::into),
621 user_id: ctx.user_id.map(Into::into),
622 session_id: ctx.session_id,
623 flow_id: ctx.flow_id,
624 node_id: ctx.node_id,
625 })
626}
627
628pub fn component_outcome_from_wit(outcome: WitComponentOutcome) -> ComponentOutcome {
630 let status = match outcome.status {
631 WitOutcomeStatus::Done => ComponentOutcomeStatus::Done,
632 WitOutcomeStatus::Pending => ComponentOutcomeStatus::Pending,
633 WitOutcomeStatus::Error => ComponentOutcomeStatus::Error,
634 };
635
636 ComponentOutcome {
637 status,
638 code: outcome.code,
639 payload: outcome.payload_json,
640 metadata: outcome.metadata_json,
641 }
642}
643
644pub fn component_outcome_to_wit(outcome: ComponentOutcome) -> WitComponentOutcome {
646 let status = match outcome.status {
647 ComponentOutcomeStatus::Done => WitOutcomeStatus::Done,
648 ComponentOutcomeStatus::Pending => WitOutcomeStatus::Pending,
649 ComponentOutcomeStatus::Error => WitOutcomeStatus::Error,
650 };
651
652 WitComponentOutcome {
653 status,
654 code: outcome.code,
655 payload_json: outcome.payload,
656 metadata_json: outcome.metadata,
657 }
658}
659
660pub fn pack_descriptor_from_wit(desc: WitPackDescriptor) -> MapperResult<PackDescriptor> {
662 Ok(PackDescriptor {
663 pack_id: desc.pack_id.try_into()?,
664 version: Version::parse(&desc.version)
665 .map_err(|err| invalid_input(format!("invalid version: {err}")))?,
666 kind: pack_kind_from_wit(desc.kind),
667 publisher: desc.publisher,
668 })
669}
670
671pub fn pack_descriptor_to_wit(desc: PackDescriptor) -> WitPackDescriptor {
673 WitPackDescriptor {
674 pack_id: desc.pack_id.into(),
675 version: desc.version.to_string(),
676 kind: pack_kind_to_wit(desc.kind),
677 publisher: desc.publisher,
678 }
679}
680
681pub fn flow_descriptor_from_wit(desc: WitFlowDescriptor) -> MapperResult<FlowDescriptor> {
683 Ok(FlowDescriptor {
684 id: desc.id.try_into()?,
685 kind: flow_kind_from_pack_wit(desc.kind),
686 tags: desc.tags,
687 entrypoints: desc.entrypoints,
688 })
689}
690
691pub fn flow_descriptor_to_wit(desc: FlowDescriptor) -> WitFlowDescriptor {
693 WitFlowDescriptor {
694 id: desc.id.into(),
695 kind: flow_kind_to_pack_wit(desc.kind),
696 tags: desc.tags,
697 entrypoints: desc.entrypoints,
698 }
699}
700
701#[cfg(test)]
702mod tests {
703 use super::*;
704 use std::convert::TryFrom;
705
706 fn fixture_id<T>(value: &str) -> T
707 where
708 T: TryFrom<String, Error = types::GreenticError>,
709 {
710 T::try_from(value.to_owned())
711 .unwrap_or_else(|err| panic!("invalid fixture identifier '{value}': {err}"))
712 }
713
714 fn sample_tenant_ctx() -> types::TenantCtx {
715 types::TenantCtx {
716 env: fixture_id("prod"),
717 tenant: fixture_id("tenant-1"),
718 tenant_id: fixture_id("tenant-1"),
719 team: Some(fixture_id("team-42")),
720 team_id: Some(fixture_id("team-42")),
721 user: Some(fixture_id("user-7")),
722 user_id: Some(fixture_id("user-7")),
723 attributes: BTreeMap::new(),
724 session_id: Some("sess-42".into()),
725 flow_id: Some("flow-42".into()),
726 node_id: Some("node-42".into()),
727 provider_id: Some("provider-42".into()),
728 trace_id: Some("trace".into()),
729 i18n_id: Some("en-US".into()),
730 correlation_id: Some("corr".into()),
731 deadline: Some(types::InvocationDeadline::from_unix_millis(
732 1_700_000_000_000,
733 )),
734 attempt: 2,
735 idempotency_key: Some("idem".into()),
736 impersonation: Some(types::Impersonation {
737 actor_id: fixture_id("actor"),
738 reason: Some("maintenance".into()),
739 }),
740 }
741 }
742
743 #[test]
744 fn tenant_ctx_roundtrip() {
745 let ctx = sample_tenant_ctx();
746 let wit = match WitTenantCtx::try_from(ctx.clone()) {
747 Ok(value) => value,
748 Err(err) => panic!("failed to map to wit: {err}"),
749 };
750 let back = match types::TenantCtx::try_from(wit) {
751 Ok(value) => value,
752 Err(err) => panic!("failed to map from wit: {err}"),
753 };
754 assert_eq!(back.env.as_str(), ctx.env.as_str());
755 assert_eq!(back.tenant.as_str(), ctx.tenant.as_str());
756 assert!(back.impersonation.is_some());
757 assert!(ctx.impersonation.is_some());
758 assert_eq!(
759 back.impersonation.as_ref().map(|imp| imp.actor_id.as_str()),
760 ctx.impersonation.as_ref().map(|imp| imp.actor_id.as_str())
761 );
762 assert_eq!(back.session_id, ctx.session_id);
763 assert_eq!(back.flow_id, ctx.flow_id);
764 assert_eq!(back.node_id, ctx.node_id);
765 assert_eq!(back.provider_id, ctx.provider_id);
766 }
767
768 #[test]
769 fn outcome_roundtrip() {
770 let pending = types::Outcome::Pending {
771 reason: "waiting".into(),
772 expected_input: Some(vec!["input".into()]),
773 };
774 let wit = WitOutcome::from(pending.clone());
775 let back = types::Outcome::from(wit);
776 match back {
777 types::Outcome::Pending { reason, .. } => {
778 assert_eq!(reason, "waiting");
779 }
780 _ => panic!("expected pending"),
781 }
782 }
783}