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 i18n_id,
588 session_id,
589 flow_id,
590 node_id,
591 } = ctx;
592
593 Ok(types::TenantCtx {
594 env: env.try_into()?,
595 tenant: tenant_id.clone().try_into()?,
596 tenant_id: tenant_id.try_into()?,
597 team: team_id.clone().map(|id| id.try_into()).transpose()?,
598 team_id: team_id.map(|id| id.try_into()).transpose()?,
599 user: user_id.clone().map(|id| id.try_into()).transpose()?,
600 user_id: user_id.map(|id| id.try_into()).transpose()?,
601 session_id,
602 flow_id,
603 node_id,
604 provider_id: None,
605 trace_id: None,
606 i18n_id: i18n_id.map(|id| id.to_string()),
607 correlation_id: None,
608 attributes: BTreeMap::new(),
609 deadline: None,
610 attempt: 0,
611 idempotency_key: None,
612 impersonation: None,
613 })
614}
615
616pub fn tenant_ctx_to_common(ctx: types::TenantCtx) -> MapperResult<WitCommonTenantCtx> {
618 Ok(WitCommonTenantCtx {
619 env: ctx.env.into(),
620 tenant_id: ctx.tenant_id.into(),
621 team_id: ctx.team_id.map(Into::into),
622 user_id: ctx.user_id.map(Into::into),
623 i18n_id: ctx.i18n_id.clone(),
624 session_id: ctx.session_id,
625 flow_id: ctx.flow_id,
626 node_id: ctx.node_id,
627 })
628}
629
630pub fn component_outcome_from_wit(outcome: WitComponentOutcome) -> ComponentOutcome {
632 let status = match outcome.status {
633 WitOutcomeStatus::Done => ComponentOutcomeStatus::Done,
634 WitOutcomeStatus::Pending => ComponentOutcomeStatus::Pending,
635 WitOutcomeStatus::Error => ComponentOutcomeStatus::Error,
636 };
637
638 ComponentOutcome {
639 status,
640 code: outcome.code,
641 payload: outcome.payload_json,
642 metadata: outcome.metadata_json,
643 }
644}
645
646pub fn component_outcome_to_wit(outcome: ComponentOutcome) -> WitComponentOutcome {
648 let status = match outcome.status {
649 ComponentOutcomeStatus::Done => WitOutcomeStatus::Done,
650 ComponentOutcomeStatus::Pending => WitOutcomeStatus::Pending,
651 ComponentOutcomeStatus::Error => WitOutcomeStatus::Error,
652 };
653
654 WitComponentOutcome {
655 status,
656 code: outcome.code,
657 payload_json: outcome.payload,
658 metadata_json: outcome.metadata,
659 }
660}
661
662pub fn pack_descriptor_from_wit(desc: WitPackDescriptor) -> MapperResult<PackDescriptor> {
664 Ok(PackDescriptor {
665 pack_id: desc.pack_id.try_into()?,
666 version: Version::parse(&desc.version)
667 .map_err(|err| invalid_input(format!("invalid version: {err}")))?,
668 kind: pack_kind_from_wit(desc.kind),
669 publisher: desc.publisher,
670 })
671}
672
673pub fn pack_descriptor_to_wit(desc: PackDescriptor) -> WitPackDescriptor {
675 WitPackDescriptor {
676 pack_id: desc.pack_id.into(),
677 version: desc.version.to_string(),
678 kind: pack_kind_to_wit(desc.kind),
679 publisher: desc.publisher,
680 }
681}
682
683pub fn flow_descriptor_from_wit(desc: WitFlowDescriptor) -> MapperResult<FlowDescriptor> {
685 Ok(FlowDescriptor {
686 id: desc.id.try_into()?,
687 kind: flow_kind_from_pack_wit(desc.kind),
688 tags: desc.tags,
689 entrypoints: desc.entrypoints,
690 })
691}
692
693pub fn flow_descriptor_to_wit(desc: FlowDescriptor) -> WitFlowDescriptor {
695 WitFlowDescriptor {
696 id: desc.id.into(),
697 kind: flow_kind_to_pack_wit(desc.kind),
698 tags: desc.tags,
699 entrypoints: desc.entrypoints,
700 }
701}
702
703#[cfg(test)]
704mod tests {
705 use super::*;
706 use std::convert::TryFrom;
707
708 fn fixture_id<T>(value: &str) -> T
709 where
710 T: TryFrom<String, Error = types::GreenticError>,
711 {
712 T::try_from(value.to_owned())
713 .unwrap_or_else(|err| panic!("invalid fixture identifier '{value}': {err}"))
714 }
715
716 fn sample_tenant_ctx() -> types::TenantCtx {
717 types::TenantCtx {
718 env: fixture_id("prod"),
719 tenant: fixture_id("tenant-1"),
720 tenant_id: fixture_id("tenant-1"),
721 team: Some(fixture_id("team-42")),
722 team_id: Some(fixture_id("team-42")),
723 user: Some(fixture_id("user-7")),
724 user_id: Some(fixture_id("user-7")),
725 attributes: BTreeMap::new(),
726 session_id: Some("sess-42".into()),
727 flow_id: Some("flow-42".into()),
728 node_id: Some("node-42".into()),
729 provider_id: Some("provider-42".into()),
730 trace_id: Some("trace".into()),
731 i18n_id: Some("en-US".into()),
732 correlation_id: Some("corr".into()),
733 deadline: Some(types::InvocationDeadline::from_unix_millis(
734 1_700_000_000_000,
735 )),
736 attempt: 2,
737 idempotency_key: Some("idem".into()),
738 impersonation: Some(types::Impersonation {
739 actor_id: fixture_id("actor"),
740 reason: Some("maintenance".into()),
741 }),
742 }
743 }
744
745 #[test]
746 fn tenant_ctx_roundtrip() {
747 let ctx = sample_tenant_ctx();
748 let wit = match WitTenantCtx::try_from(ctx.clone()) {
749 Ok(value) => value,
750 Err(err) => panic!("failed to map to wit: {err}"),
751 };
752 let back = match types::TenantCtx::try_from(wit) {
753 Ok(value) => value,
754 Err(err) => panic!("failed to map from wit: {err}"),
755 };
756 assert_eq!(back.env.as_str(), ctx.env.as_str());
757 assert_eq!(back.tenant.as_str(), ctx.tenant.as_str());
758 assert!(back.impersonation.is_some());
759 assert!(ctx.impersonation.is_some());
760 assert_eq!(
761 back.impersonation.as_ref().map(|imp| imp.actor_id.as_str()),
762 ctx.impersonation.as_ref().map(|imp| imp.actor_id.as_str())
763 );
764 assert_eq!(back.session_id, ctx.session_id);
765 assert_eq!(back.flow_id, ctx.flow_id);
766 assert_eq!(back.node_id, ctx.node_id);
767 assert_eq!(back.provider_id, ctx.provider_id);
768 }
769
770 #[test]
771 fn outcome_roundtrip() {
772 let pending = types::Outcome::Pending {
773 reason: "waiting".into(),
774 expected_input: Some(vec!["input".into()]),
775 };
776 let wit = WitOutcome::from(pending.clone());
777 let back = types::Outcome::from(wit);
778 match back {
779 types::Outcome::Pending { reason, .. } => {
780 assert_eq!(reason, "waiting");
781 }
782 _ => panic!("expected pending"),
783 }
784 }
785}