1use super::task::TaskDefinitionFields;
2use crate::models::authentication::*;
3use crate::models::resource::*;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use std::collections::HashMap;
7
8string_constants! {
9 CallType {
11 ASYNCAPI => "asyncapi",
12 GRPC => "grpc",
13 HTTP => "http",
14 OPENAPI => "openapi",
15 A2A => "a2a",
16 }
17}
18
19string_constants! {
20 AsyncApiProtocol {
22 AMQP => "amqp",
23 AMQP1 => "amqp1",
24 ANYPOINTMQ => "anypointmq",
25 GOOGLE_PUBSUB => "googlepubsub",
26 HTTP => "http",
27 IBMMQ => "ibmmq",
28 JMS => "jms",
29 KAFKA => "kafka",
30 MERCURE => "mercure",
31 MQTT => "mqtt",
32 MQTT5 => "mqtt5",
33 NATS => "nats",
34 PULSAR => "pulsar",
35 REDIS => "redis",
36 SNS => "sns",
37 SOLACE => "solace",
38 SQS => "sqs",
39 STOMP => "stomp",
40 WS => "ws",
41 }
42}
43
44string_constants! {
45 A2AMethod {
47 MESSAGE_SEND => "message/send",
48 MESSAGE_STREAM => "message/stream",
49 TASKS_GET => "tasks/get",
50 TASKS_LIST => "tasks/list",
51 TASKS_CANCEL => "tasks/cancel",
52 TASKS_RESUBSCRIBE => "tasks/resubscribe",
53 TASKS_PUSH_NOTIFICATION_CONFIG_SET => "tasks/pushNotificationConfig/set",
54 TASKS_PUSH_NOTIFICATION_CONFIG_GET => "tasks/pushNotificationConfig/get",
55 TASKS_PUSH_NOTIFICATION_CONFIG_LIST => "tasks/pushNotificationConfig/list",
56 TASKS_PUSH_NOTIFICATION_CONFIG_DELETE => "tasks/pushNotificationConfig/delete",
57 AGENT_GET_AUTHENTICATED_EXTENDED_CARD => "agent/getAuthenticatedExtendedCard",
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Serialize)]
63#[serde(untagged)]
64#[allow(clippy::large_enum_variant)]
65pub enum CallTaskDefinition {
66 AsyncAPI(CallAsyncAPIDefinition),
68 GRPC(Box<CallGRPCDefinition>),
70 HTTP(CallHTTPDefinition),
72 OpenAPI(CallOpenAPIDefinition),
74 A2A(CallA2ADefinition),
76 Function(CallFunctionDefinition),
78}
79
80impl CallTaskDefinition {
81 pub fn common_fields(&self) -> &super::task::TaskDefinitionFields {
83 match self {
84 CallTaskDefinition::HTTP(t) => &t.common,
85 CallTaskDefinition::GRPC(t) => &t.common,
86 CallTaskDefinition::OpenAPI(t) => &t.common,
87 CallTaskDefinition::AsyncAPI(t) => &t.common,
88 CallTaskDefinition::A2A(t) => &t.common,
89 CallTaskDefinition::Function(t) => &t.common,
90 }
91 }
92
93 pub fn call_type_name(&self) -> &'static str {
96 match self {
97 CallTaskDefinition::HTTP(_) => "http",
98 CallTaskDefinition::GRPC(_) => "grpc",
99 CallTaskDefinition::OpenAPI(_) => "openapi",
100 CallTaskDefinition::AsyncAPI(_) => "asyncapi",
101 CallTaskDefinition::A2A(_) => "a2a",
102 CallTaskDefinition::Function(_) => "function",
103 }
104 }
105}
106
107impl<'de> serde::Deserialize<'de> for CallTaskDefinition {
108 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
109 where
110 D: serde::Deserializer<'de>,
111 {
112 let value = Value::deserialize(deserializer)?;
113
114 let call_value = value.get("call").and_then(|v| v.as_str()).unwrap_or("");
115
116 macro_rules! try_call {
117 ($variant:ident, $ty:ty) => {
118 <$ty>::deserialize(value)
119 .map(CallTaskDefinition::$variant)
120 .map_err(serde::de::Error::custom)
121 };
122 }
123 macro_rules! try_call_boxed {
124 ($variant:ident, $ty:ty) => {
125 <$ty>::deserialize(value)
126 .map(|v| CallTaskDefinition::$variant(Box::new(v)))
127 .map_err(serde::de::Error::custom)
128 };
129 }
130
131 match call_value {
132 "asyncapi" => try_call!(AsyncAPI, CallAsyncAPIDefinition),
133 "grpc" => try_call_boxed!(GRPC, CallGRPCDefinition),
134 "http" => try_call!(HTTP, CallHTTPDefinition),
135 "openapi" => try_call!(OpenAPI, CallOpenAPIDefinition),
136 "a2a" => try_call!(A2A, CallA2ADefinition),
137 _ => try_call!(Function, CallFunctionDefinition),
138 }
139 }
140}
141
142#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
144pub struct HTTPArguments {
145 pub method: String,
147
148 pub endpoint: super::resource::OneOfEndpointDefinitionOrUri,
150
151 #[serde(skip_serializing_if = "Option::is_none")]
153 pub headers: Option<OneOfHeadersOrExpression>,
154
155 #[serde(skip_serializing_if = "Option::is_none")]
157 pub body: Option<Value>,
158
159 #[serde(skip_serializing_if = "Option::is_none")]
161 pub query: Option<OneOfQueryOrExpression>,
162
163 #[serde(skip_serializing_if = "Option::is_none")]
165 pub output: Option<String>,
166
167 #[serde(skip_serializing_if = "Option::is_none")]
169 pub redirect: Option<bool>,
170}
171
172#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
174#[serde(untagged)]
175pub enum StringMapOrExpression {
176 Map(HashMap<String, String>),
178 Expression(String),
180}
181
182impl Default for StringMapOrExpression {
183 fn default() -> Self {
184 StringMapOrExpression::Map(HashMap::new())
185 }
186}
187
188pub type OneOfHeadersOrExpression = StringMapOrExpression;
190
191pub type OneOfQueryOrExpression = StringMapOrExpression;
193
194macro_rules! define_call_definition {
196 ($( #[$meta:meta] )* $name:ident, $with_ty:ty) => {
197 $( #[$meta] )*
198 #[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
199 pub struct $name {
200 pub call: String,
202
203 pub with: $with_ty,
205
206 #[serde(flatten)]
208 pub common: TaskDefinitionFields,
209 }
210 };
211}
212
213define_call_definition!(
214 CallHTTPDefinition, HTTPArguments
216);
217
218#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
220pub struct GRPCServiceDefinition {
221 pub name: String,
223
224 pub host: String,
226
227 #[serde(skip_serializing_if = "Option::is_none")]
229 pub port: Option<u16>,
230
231 #[serde(skip_serializing_if = "Option::is_none")]
233 pub authentication: Option<ReferenceableAuthenticationPolicy>,
234}
235
236#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
238pub struct GRPCArguments {
239 pub proto: ExternalResourceDefinition,
241
242 pub service: GRPCServiceDefinition,
244
245 pub method: String,
247
248 #[serde(skip_serializing_if = "Option::is_none")]
250 pub arguments: Option<HashMap<String, Value>>,
251
252 #[serde(skip_serializing_if = "Option::is_none")]
254 pub authentication: Option<ReferenceableAuthenticationPolicy>,
255}
256
257define_call_definition!(
258 CallGRPCDefinition, GRPCArguments
260);
261
262#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
264pub struct OpenAPIArguments {
265 pub document: ExternalResourceDefinition,
267
268 #[serde(rename = "operationId")]
270 pub operation_id: String,
271
272 #[serde(skip_serializing_if = "Option::is_none")]
274 pub parameters: Option<HashMap<String, Value>>,
275
276 #[serde(skip_serializing_if = "Option::is_none")]
278 pub authentication: Option<ReferenceableAuthenticationPolicy>,
279
280 #[serde(skip_serializing_if = "Option::is_none")]
282 pub output: Option<String>,
283
284 #[serde(skip_serializing_if = "Option::is_none")]
286 pub redirect: Option<bool>,
287}
288
289define_call_definition!(
290 CallOpenAPIDefinition, OpenAPIArguments
292);
293
294#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
296pub struct AsyncApiServerDefinition {
297 pub name: String,
299
300 #[serde(skip_serializing_if = "Option::is_none")]
302 pub variables: Option<HashMap<String, Value>>,
303}
304
305#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
307pub struct AsyncApiOutboundMessageDefinition {
308 #[serde(skip_serializing_if = "Option::is_none")]
310 pub payload: Option<Value>,
311
312 #[serde(skip_serializing_if = "Option::is_none")]
314 pub headers: Option<Value>,
315}
316
317#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
319pub struct AsyncApiInboundMessageDefinition {
320 #[serde(skip_serializing_if = "Option::is_none")]
322 pub payload: Option<Value>,
323
324 #[serde(skip_serializing_if = "Option::is_none")]
326 pub headers: Option<Value>,
327
328 #[serde(rename = "correlationId", skip_serializing_if = "Option::is_none")]
330 pub correlation_id: Option<String>,
331}
332
333#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
335#[serde(untagged)]
336pub enum AsyncApiMessageConsumptionPolicy {
337 Amount {
339 amount: u32,
341 },
342 For {
344 #[serde(rename = "for")]
346 for_: super::duration::OneOfDurationOrIso8601Expression,
347 },
348 While {
350 #[serde(rename = "while")]
352 while_: String,
353 },
354 Until {
356 until: String,
358 },
359}
360
361impl Default for AsyncApiMessageConsumptionPolicy {
362 fn default() -> Self {
363 AsyncApiMessageConsumptionPolicy::Amount { amount: 1 }
364 }
365}
366
367#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
369pub struct AsyncApiSubscriptionDefinition {
370 #[serde(skip_serializing_if = "Option::is_none")]
372 pub filter: Option<String>,
373
374 pub consume: AsyncApiMessageConsumptionPolicy,
376
377 #[serde(skip_serializing_if = "Option::is_none")]
379 pub foreach: Option<super::task::SubscriptionIteratorDefinition>,
380}
381
382#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
384pub struct AsyncApiArguments {
385 pub document: ExternalResourceDefinition,
387
388 #[serde(skip_serializing_if = "Option::is_none")]
390 pub channel: Option<String>,
391
392 #[serde(skip_serializing_if = "Option::is_none")]
394 pub operation: Option<String>,
395
396 #[serde(skip_serializing_if = "Option::is_none")]
398 pub server: Option<AsyncApiServerDefinition>,
399
400 #[serde(skip_serializing_if = "Option::is_none")]
402 pub protocol: Option<String>,
403
404 #[serde(skip_serializing_if = "Option::is_none")]
406 pub message: Option<AsyncApiOutboundMessageDefinition>,
407
408 #[serde(skip_serializing_if = "Option::is_none")]
410 pub subscription: Option<AsyncApiSubscriptionDefinition>,
411
412 #[serde(skip_serializing_if = "Option::is_none")]
414 pub authentication: Option<ReferenceableAuthenticationPolicy>,
415}
416
417define_call_definition!(
418 CallAsyncAPIDefinition, AsyncApiArguments
420);
421
422#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
424#[serde(untagged)]
425pub enum OneOfA2AParametersOrExpression {
426 Map(HashMap<String, Value>),
428 Expression(String),
430}
431
432impl Default for OneOfA2AParametersOrExpression {
433 fn default() -> Self {
434 OneOfA2AParametersOrExpression::Map(HashMap::new())
435 }
436}
437
438#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
440pub struct A2AArguments {
441 #[serde(rename = "agentCard", skip_serializing_if = "Option::is_none")]
443 pub agent_card: Option<ExternalResourceDefinition>,
444
445 #[serde(skip_serializing_if = "Option::is_none")]
447 pub server: Option<super::resource::OneOfEndpointDefinitionOrUri>,
448
449 pub method: String,
451
452 #[serde(skip_serializing_if = "Option::is_none")]
454 pub parameters: Option<OneOfA2AParametersOrExpression>,
455}
456
457define_call_definition!(
458 CallA2ADefinition, A2AArguments
460);
461
462#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
464pub struct CallFunctionDefinition {
465 pub call: String,
467
468 #[serde(skip_serializing_if = "Option::is_none")]
470 pub with: Option<HashMap<String, Value>>,
471
472 #[serde(flatten)]
474 pub common: TaskDefinitionFields,
475}
476
477#[cfg(test)]
478mod tests {
479 use super::*;
480
481 #[test]
482 fn test_call_http_deserialize() {
483 let json = r#"{
484 "call": "http",
485 "with": {
486 "method": "GET",
487 "endpoint": "http://example.com/api"
488 }
489 }"#;
490 let http: CallHTTPDefinition = serde_json::from_str(json).unwrap();
491 assert_eq!(http.call, "http");
492 assert_eq!(http.with.method, "GET");
493 match &http.with.endpoint {
494 OneOfEndpointDefinitionOrUri::Uri(uri) => assert_eq!(uri, "http://example.com/api"),
495 _ => panic!("Expected Uri variant"),
496 }
497 }
498
499 #[test]
500 fn test_call_http_with_headers_and_query() {
501 let json = r#"{
502 "call": "http",
503 "with": {
504 "method": "POST",
505 "endpoint": "http://example.com/api",
506 "headers": {"Authorization": "Bearer token"},
507 "query": {"page": "1"},
508 "output": "response",
509 "redirect": true
510 }
511 }"#;
512 let http: CallHTTPDefinition = serde_json::from_str(json).unwrap();
513 assert_eq!(http.with.method, "POST");
514 assert!(http.with.headers.is_some());
515 assert!(http.with.query.is_some());
516 assert_eq!(http.with.output, Some("response".to_string()));
517 assert_eq!(http.with.redirect, Some(true));
518 }
519
520 #[test]
521 fn test_call_http_roundtrip() {
522 let json = r#"{
523 "call": "http",
524 "with": {
525 "method": "GET",
526 "endpoint": "http://example.com/api"
527 }
528 }"#;
529 let http: CallHTTPDefinition = serde_json::from_str(json).unwrap();
530 let serialized = serde_json::to_string(&http).unwrap();
531 let deserialized: CallHTTPDefinition = serde_json::from_str(&serialized).unwrap();
532 assert_eq!(http, deserialized);
533 }
534
535 #[test]
536 fn test_call_http_with_endpoint_config() {
537 let json = r#"{
538 "call": "http",
539 "with": {
540 "method": "GET",
541 "endpoint": {
542 "uri": "http://example.com/{id}",
543 "authentication": {
544 "basic": {"username": "admin", "password": "secret"}
545 }
546 }
547 }
548 }"#;
549 let http: CallHTTPDefinition = serde_json::from_str(json).unwrap();
550 match &http.with.endpoint {
551 OneOfEndpointDefinitionOrUri::Endpoint(ep) => {
552 assert_eq!(ep.uri, "http://example.com/{id}");
553 assert!(ep.authentication.is_some());
554 }
555 _ => panic!("Expected Endpoint variant"),
556 }
557 }
558
559 #[test]
560 fn test_call_grpc_deserialize() {
561 let json = r#"{
562 "call": "grpc",
563 "with": {
564 "proto": {
565 "name": "MyProto",
566 "endpoint": "http://example.com/proto"
567 },
568 "service": {
569 "name": "UserService",
570 "host": "example.com",
571 "port": 50051
572 },
573 "method": "GetUser",
574 "arguments": {"userId": "12345"}
575 }
576 }"#;
577 let grpc: CallGRPCDefinition = serde_json::from_str(json).unwrap();
578 assert_eq!(grpc.call, "grpc");
579 assert_eq!(grpc.with.proto.name, Some("MyProto".to_string()));
580 assert_eq!(grpc.with.service.name, "UserService");
581 assert_eq!(grpc.with.service.host, "example.com");
582 assert_eq!(grpc.with.service.port, Some(50051));
583 assert_eq!(grpc.with.method, "GetUser");
584 }
585
586 #[test]
587 fn test_call_grpc_roundtrip() {
588 let json = r#"{
589 "call": "grpc",
590 "with": {
591 "proto": {
592 "name": "MyProto",
593 "endpoint": "http://example.com/proto"
594 },
595 "service": {
596 "name": "UserService",
597 "host": "example.com",
598 "port": 50051
599 },
600 "method": "GetUser"
601 }
602 }"#;
603 let grpc: CallGRPCDefinition = serde_json::from_str(json).unwrap();
604 let serialized = serde_json::to_string(&grpc).unwrap();
605 let deserialized: CallGRPCDefinition = serde_json::from_str(&serialized).unwrap();
606 assert_eq!(grpc, deserialized);
607 }
608
609 #[test]
610 fn test_call_openapi_deserialize() {
611 let json = r#"{
612 "call": "openapi",
613 "with": {
614 "document": {
615 "name": "MyOpenAPIDoc",
616 "endpoint": "http://example.com/openapi.json"
617 },
618 "operationId": "getUsers",
619 "parameters": {"param1": "value1"},
620 "authentication": {"use": "my-auth"},
621 "output": "content",
622 "redirect": true
623 }
624 }"#;
625 let openapi: CallOpenAPIDefinition = serde_json::from_str(json).unwrap();
626 assert_eq!(openapi.call, "openapi");
627 assert_eq!(openapi.with.operation_id, "getUsers");
628 assert!(openapi.with.parameters.is_some());
629 assert!(openapi.with.authentication.is_some());
630 assert_eq!(openapi.with.output, Some("content".to_string()));
631 assert_eq!(openapi.with.redirect, Some(true));
632 }
633
634 #[test]
635 fn test_call_openapi_roundtrip() {
636 let json = r#"{
637 "call": "openapi",
638 "with": {
639 "document": {
640 "name": "MyOpenAPIDoc",
641 "endpoint": "http://example.com/openapi.json"
642 },
643 "operationId": "getUsers"
644 }
645 }"#;
646 let openapi: CallOpenAPIDefinition = serde_json::from_str(json).unwrap();
647 let serialized = serde_json::to_string(&openapi).unwrap();
648 let deserialized: CallOpenAPIDefinition = serde_json::from_str(&serialized).unwrap();
649 assert_eq!(openapi, deserialized);
650 }
651
652 #[test]
653 fn test_call_asyncapi_deserialize() {
654 let json = r#"{
655 "call": "asyncapi",
656 "with": {
657 "document": {
658 "name": "MyAsyncAPIDoc",
659 "endpoint": "http://example.com/asyncapi.json"
660 },
661 "operation": "user.signup",
662 "server": {"name": "default-server"},
663 "protocol": "http",
664 "message": {
665 "payload": {"userId": "12345"}
666 },
667 "authentication": {"use": "asyncapi-auth"}
668 }
669 }"#;
670 let asyncapi: CallAsyncAPIDefinition = serde_json::from_str(json).unwrap();
671 assert_eq!(asyncapi.call, "asyncapi");
672 assert_eq!(asyncapi.with.operation, Some("user.signup".to_string()));
673 assert!(asyncapi.with.server.is_some());
674 assert_eq!(asyncapi.with.protocol, Some("http".to_string()));
675 assert!(asyncapi.with.message.is_some());
676 assert!(asyncapi.with.authentication.is_some());
677 }
678
679 #[test]
680 fn test_call_asyncapi_roundtrip() {
681 let json = r#"{
682 "call": "asyncapi",
683 "with": {
684 "document": {
685 "name": "MyAsyncAPIDoc",
686 "endpoint": "http://example.com/asyncapi.json"
687 },
688 "operation": "user.signup",
689 "protocol": "http"
690 }
691 }"#;
692 let asyncapi: CallAsyncAPIDefinition = serde_json::from_str(json).unwrap();
693 let serialized = serde_json::to_string(&asyncapi).unwrap();
694 let deserialized: CallAsyncAPIDefinition = serde_json::from_str(&serialized).unwrap();
695 assert_eq!(asyncapi, deserialized);
696 }
697
698 #[test]
699 fn test_call_function_deserialize() {
700 let json = r#"{
701 "call": "myFunction",
702 "with": {
703 "param1": "value1",
704 "param2": 42
705 }
706 }"#;
707 let func: CallFunctionDefinition = serde_json::from_str(json).unwrap();
708 assert_eq!(func.call, "myFunction");
709 assert!(func.with.is_some());
710 let with = func.with.unwrap();
711 assert_eq!(with.get("param1").unwrap(), "value1");
712 }
713
714 #[test]
715 fn test_call_function_roundtrip() {
716 let json = r#"{
717 "call": "myFunction",
718 "with": {"param1": "value1"}
719 }"#;
720 let func: CallFunctionDefinition = serde_json::from_str(json).unwrap();
721 let serialized = serde_json::to_string(&func).unwrap();
722 let deserialized: CallFunctionDefinition = serde_json::from_str(&serialized).unwrap();
723 assert_eq!(func, deserialized);
724 }
725
726 #[test]
727 fn test_call_a2a_deserialize() {
728 let json = r#"{
729 "call": "a2a",
730 "with": {
731 "method": "message/send",
732 "parameters": {"message": "hello"}
733 }
734 }"#;
735 let a2a: CallA2ADefinition = serde_json::from_str(json).unwrap();
736 assert_eq!(a2a.call, "a2a");
737 assert_eq!(a2a.with.method, "message/send");
738 assert!(a2a.with.parameters.is_some());
739 }
740
741 #[test]
742 fn test_call_task_definition_http() {
743 let json =
744 r#"{"call": "http", "with": {"method": "GET", "endpoint": "http://example.com"}}"#;
745 let def: CallTaskDefinition = serde_json::from_str(json).unwrap();
746 match def {
747 CallTaskDefinition::HTTP(http) => assert_eq!(http.call, "http"),
748 _ => panic!("Expected HTTP variant"),
749 }
750 }
751
752 #[test]
753 fn test_call_task_definition_grpc() {
754 let json = r#"{"call": "grpc", "with": {"proto": {"endpoint": "http://example.com/proto"}, "service": {"name": "Svc", "host": "example.com"}, "method": "Get"}}"#;
755 let def: CallTaskDefinition = serde_json::from_str(json).unwrap();
756 match def {
757 CallTaskDefinition::GRPC(grpc) => assert_eq!(grpc.call, "grpc"),
758 _ => panic!("Expected GRPC variant"),
759 }
760 }
761
762 #[test]
763 fn test_call_task_definition_openapi() {
764 let json = r#"{"call": "openapi", "with": {"document": {"endpoint": "http://example.com/openapi.json"}, "operationId": "op1"}}"#;
765 let def: CallTaskDefinition = serde_json::from_str(json).unwrap();
766 match def {
767 CallTaskDefinition::OpenAPI(openapi) => assert_eq!(openapi.call, "openapi"),
768 _ => panic!("Expected OpenAPI variant"),
769 }
770 }
771
772 #[test]
773 fn test_call_task_definition_function() {
774 let json = r#"{"call": "myFunc", "with": {"key": "val"}}"#;
775 let def: CallTaskDefinition = serde_json::from_str(json).unwrap();
776 match def {
777 CallTaskDefinition::Function(func) => assert_eq!(func.call, "myFunc"),
778 _ => panic!("Expected Function variant"),
779 }
780 }
781
782 #[test]
783 fn test_headers_map_vs_expression() {
784 let map_json = r#"{"Authorization": "Bearer token"}"#;
785 let map: OneOfHeadersOrExpression = serde_json::from_str(map_json).unwrap();
786 assert!(matches!(map, OneOfHeadersOrExpression::Map(_)));
787
788 let expr_json = r#""${ .headers }""#;
789 let expr: OneOfHeadersOrExpression = serde_json::from_str(expr_json).unwrap();
790 assert!(matches!(expr, OneOfHeadersOrExpression::Expression(_)));
791 }
792
793 #[test]
794 fn test_query_map_vs_expression() {
795 let map_json = r#"{"page": "1"}"#;
796 let map: OneOfQueryOrExpression = serde_json::from_str(map_json).unwrap();
797 assert!(matches!(map, OneOfQueryOrExpression::Map(_)));
798
799 let expr_json = r#""${ .queryParams }""#;
800 let expr: OneOfQueryOrExpression = serde_json::from_str(expr_json).unwrap();
801 assert!(matches!(expr, OneOfQueryOrExpression::Expression(_)));
802 }
803
804 #[test]
805 fn test_asyncapi_consumption_policy_amount() {
806 let json = r#"{"amount": 5}"#;
807 let policy: AsyncApiMessageConsumptionPolicy = serde_json::from_str(json).unwrap();
808 match policy {
809 AsyncApiMessageConsumptionPolicy::Amount { amount } => assert_eq!(amount, 5),
810 _ => panic!("Expected Amount variant"),
811 }
812 }
813
814 #[test]
815 fn test_asyncapi_consumption_policy_for() {
816 let json = r#"{"for": "PT30S"}"#;
817 let policy: AsyncApiMessageConsumptionPolicy = serde_json::from_str(json).unwrap();
818 match policy {
819 AsyncApiMessageConsumptionPolicy::For { for_ } => {
820 assert!(matches!(
822 for_,
823 crate::models::duration::OneOfDurationOrIso8601Expression::Iso8601Expression(_)
824 ));
825 }
826 _ => panic!("Expected For variant"),
827 }
828 }
829
830 #[test]
831 fn test_asyncapi_consumption_policy_for_duration() {
832 let json = r#"{"for": {"seconds": 30}}"#;
833 let policy: AsyncApiMessageConsumptionPolicy = serde_json::from_str(json).unwrap();
834 match policy {
835 AsyncApiMessageConsumptionPolicy::For { for_ } => {
836 assert!(matches!(
837 for_,
838 crate::models::duration::OneOfDurationOrIso8601Expression::Duration(_)
839 ));
840 }
841 _ => panic!("Expected For variant"),
842 }
843 }
844
845 #[test]
846 fn test_asyncapi_consumption_policy_while() {
847 let json = r#"{"while": "${ .counter < 10 }"}"#;
848 let policy: AsyncApiMessageConsumptionPolicy = serde_json::from_str(json).unwrap();
849 match policy {
850 AsyncApiMessageConsumptionPolicy::While { while_ } => {
851 assert_eq!(while_, "${ .counter < 10 }");
852 }
853 _ => panic!("Expected While variant"),
854 }
855 }
856
857 #[test]
858 fn test_grpc_service_with_authentication() {
859 let json = r#"{
860 "name": "UserService",
861 "host": "example.com",
862 "port": 50051,
863 "authentication": {"use": "grpc-auth"}
864 }"#;
865 let service: GRPCServiceDefinition = serde_json::from_str(json).unwrap();
866 assert_eq!(service.name, "UserService");
867 assert!(service.authentication.is_some());
868 }
869
870 #[test]
873 fn test_call_http_with_common_fields() {
874 let json = r#"{
876 "if": "${condition}",
877 "input": {"from": {"key": "value"}},
878 "output": {"as": {"result": "output"}},
879 "timeout": {"after": "PT10S"},
880 "then": "continue",
881 "metadata": {"meta": "data"},
882 "call": "http",
883 "with": {
884 "method": "GET",
885 "endpoint": "http://example.com",
886 "headers": {"Authorization": "Bearer token"},
887 "query": {"q": "search"},
888 "output": "content",
889 "redirect": true
890 }
891 }"#;
892 let http: CallHTTPDefinition = serde_json::from_str(json).unwrap();
893 assert_eq!(http.call, "http");
894 assert_eq!(http.common.if_, Some("${condition}".to_string()));
895 assert!(http.common.input.is_some());
896 assert!(http.common.output.is_some());
897 assert!(http.common.timeout.is_some());
898 assert_eq!(http.common.then, Some("continue".to_string()));
899 assert!(http.common.metadata.is_some());
900 assert_eq!(http.with.method, "GET");
901 assert_eq!(http.with.output, Some("content".to_string()));
902 assert_eq!(http.with.redirect, Some(true));
903 }
904
905 #[test]
906 fn test_call_http_with_common_fields_roundtrip() {
907 let json = r#"{
908 "if": "${condition}",
909 "input": {"from": {"key": "value"}},
910 "output": {"as": {"result": "output"}},
911 "timeout": {"after": "PT10S"},
912 "then": "continue",
913 "call": "http",
914 "with": {
915 "method": "GET",
916 "endpoint": "http://example.com"
917 }
918 }"#;
919 let http: CallHTTPDefinition = serde_json::from_str(json).unwrap();
920 let serialized = serde_json::to_string(&http).unwrap();
921 let deserialized: CallHTTPDefinition = serde_json::from_str(&serialized).unwrap();
922 assert_eq!(http, deserialized);
923 }
924
925 #[test]
926 fn test_call_openapi_with_authentication() {
927 let json = r#"{
929 "call": "openapi",
930 "with": {
931 "document": {
932 "name": "MyOpenAPIDoc",
933 "endpoint": "http://example.com/openapi.json"
934 },
935 "operationId": "getUsers",
936 "parameters": {"param1": "value1", "param2": "value2"},
937 "authentication": {"use": "my-auth"},
938 "output": "content",
939 "redirect": true
940 }
941 }"#;
942 let openapi: CallOpenAPIDefinition = serde_json::from_str(json).unwrap();
943 assert_eq!(openapi.with.operation_id, "getUsers");
944 assert!(openapi.with.authentication.is_some());
945 assert_eq!(openapi.with.output, Some("content".to_string()));
946 assert_eq!(openapi.with.redirect, Some(true));
947 }
948
949 #[test]
950 fn test_call_grpc_with_full_arguments() {
951 let json = r#"{
953 "call": "grpc",
954 "with": {
955 "proto": {
956 "name": "MyProtoFile",
957 "endpoint": "http://example.com/protofile"
958 },
959 "service": {
960 "name": "UserService",
961 "host": "example.com",
962 "port": 50051
963 },
964 "method": "GetUser",
965 "arguments": {"userId": "12345"}
966 }
967 }"#;
968 let grpc: CallGRPCDefinition = serde_json::from_str(json).unwrap();
969 assert_eq!(grpc.call, "grpc");
970 assert_eq!(grpc.with.proto.name, Some("MyProtoFile".to_string()));
971 assert_eq!(grpc.with.service.name, "UserService");
972 assert_eq!(grpc.with.service.host, "example.com");
973 assert_eq!(grpc.with.service.port, Some(50051));
974 assert_eq!(grpc.with.method, "GetUser");
975 assert!(grpc.with.arguments.is_some());
976 }
977
978 #[test]
979 fn test_call_asyncapi_with_subscription() {
980 let json = r#"{
981 "call": "asyncapi",
982 "with": {
983 "document": {
984 "name": "MyAsyncAPIDoc",
985 "endpoint": "http://example.com/asyncapi.json"
986 },
987 "operation": "user.signup",
988 "server": {"name": "default-server"},
989 "protocol": "http",
990 "subscription": {
991 "filter": "${ .type == \"order\" }",
992 "consume": {"amount": 5}
993 },
994 "authentication": {"use": "asyncapi-auth-policy"}
995 }
996 }"#;
997 let asyncapi: CallAsyncAPIDefinition = serde_json::from_str(json).unwrap();
998 assert_eq!(asyncapi.with.operation, Some("user.signup".to_string()));
999 assert!(asyncapi.with.subscription.is_some());
1000 let sub = asyncapi.with.subscription.as_ref().unwrap();
1001 assert!(sub.filter.is_some());
1002 match &sub.consume {
1003 AsyncApiMessageConsumptionPolicy::Amount { amount } => assert_eq!(*amount, 5),
1004 _ => panic!("Expected Amount variant"),
1005 }
1006 }
1007
1008 #[test]
1009 fn test_call_asyncapi_with_message_and_subscription() {
1010 let json = r#"{
1012 "call": "asyncapi",
1013 "with": {
1014 "document": {"endpoint": "http://example.com/asyncapi.json"},
1015 "operation": "order.process",
1016 "protocol": "kafka",
1017 "message": {"payload": {"orderId": "123"}},
1018 "subscription": {
1019 "consume": {"while": "${ .status != \"completed\" }"}
1020 }
1021 }
1022 }"#;
1023 let asyncapi: CallAsyncAPIDefinition = serde_json::from_str(json).unwrap();
1024 assert!(asyncapi.with.message.is_some());
1025 assert!(asyncapi.with.subscription.is_some());
1026 let sub = asyncapi.with.subscription.as_ref().unwrap();
1027 match &sub.consume {
1028 AsyncApiMessageConsumptionPolicy::While { while_ } => {
1029 assert_eq!(while_, "${ .status != \"completed\" }");
1030 }
1031 _ => panic!("Expected While variant"),
1032 }
1033 }
1034
1035 #[test]
1036 fn test_call_function_with_common_fields_roundtrip() {
1037 let json = r#"{
1038 "call": "myFunction",
1039 "with": {"param1": "value1", "param2": 42}
1040 }"#;
1041 let func: CallFunctionDefinition = serde_json::from_str(json).unwrap();
1042 let serialized = serde_json::to_string(&func).unwrap();
1043 let deserialized: CallFunctionDefinition = serde_json::from_str(&serialized).unwrap();
1044 assert_eq!(func, deserialized);
1045 }
1046
1047 #[test]
1048 fn test_call_a2a_with_agent_card() {
1049 let json = r#"{
1050 "call": "a2a",
1051 "with": {
1052 "agentCard": {
1053 "name": "my-agent",
1054 "endpoint": "http://example.com/agent-card"
1055 },
1056 "server": "http://example.com/a2a-server",
1057 "method": "tasks/get",
1058 "parameters": {"taskId": "123"}
1059 }
1060 }"#;
1061 let a2a: CallA2ADefinition = serde_json::from_str(json).unwrap();
1062 assert_eq!(a2a.call, "a2a");
1063 assert!(a2a.with.agent_card.is_some());
1064 assert!(a2a.with.server.is_some());
1065 assert_eq!(a2a.with.method, "tasks/get");
1066 assert!(a2a.with.parameters.is_some());
1067 }
1068
1069 #[test]
1070 fn test_call_a2a_roundtrip() {
1071 let json = r#"{
1072 "call": "a2a",
1073 "with": {
1074 "method": "message/send",
1075 "parameters": {"message": "hello"}
1076 }
1077 }"#;
1078 let a2a: CallA2ADefinition = serde_json::from_str(json).unwrap();
1079 let serialized = serde_json::to_string(&a2a).unwrap();
1080 let deserialized: CallA2ADefinition = serde_json::from_str(&serialized).unwrap();
1081 assert_eq!(a2a, deserialized);
1082 }
1083
1084 #[test]
1087 fn test_call_grpc_with_common_fields() {
1088 let json = r#"{
1090 "if": "${condition}",
1091 "input": {"from": {"key": "value"}},
1092 "output": {"as": {"result": "output"}},
1093 "timeout": {"after": "PT10S"},
1094 "then": "continue",
1095 "metadata": {"meta": "data"},
1096 "call": "grpc",
1097 "with": {
1098 "proto": {
1099 "name": "MyProtoFile",
1100 "endpoint": "http://example.com/protofile"
1101 },
1102 "service": {
1103 "name": "UserService",
1104 "host": "example.com",
1105 "port": 50051
1106 },
1107 "method": "GetUser",
1108 "arguments": {"userId": "12345"}
1109 }
1110 }"#;
1111 let grpc: CallGRPCDefinition = serde_json::from_str(json).unwrap();
1112 assert_eq!(grpc.call, "grpc");
1113 assert_eq!(grpc.common.if_, Some("${condition}".to_string()));
1114 assert!(grpc.common.input.is_some());
1115 assert!(grpc.common.output.is_some());
1116 assert!(grpc.common.timeout.is_some());
1117 assert_eq!(grpc.common.then, Some("continue".to_string()));
1118 assert!(grpc.common.metadata.is_some());
1119 assert_eq!(grpc.with.service.name, "UserService");
1120 assert_eq!(grpc.with.method, "GetUser");
1121 }
1122
1123 #[test]
1124 fn test_call_grpc_with_common_fields_roundtrip() {
1125 let json = r#"{
1126 "if": "${condition}",
1127 "output": {"as": {"result": "output"}},
1128 "then": "continue",
1129 "call": "grpc",
1130 "with": {
1131 "proto": {"endpoint": "http://example.com/proto"},
1132 "service": {"name": "Svc", "host": "example.com"},
1133 "method": "Get"
1134 }
1135 }"#;
1136 let grpc: CallGRPCDefinition = serde_json::from_str(json).unwrap();
1137 let serialized = serde_json::to_string(&grpc).unwrap();
1138 let deserialized: CallGRPCDefinition = serde_json::from_str(&serialized).unwrap();
1139 assert_eq!(grpc, deserialized);
1140 }
1141
1142 #[test]
1143 fn test_call_openapi_with_common_fields() {
1144 let json = r#"{
1146 "if": "${condition}",
1147 "input": {"from": {"key": "value"}},
1148 "output": {"as": {"result": "output"}},
1149 "timeout": {"after": "PT10S"},
1150 "then": "continue",
1151 "metadata": {"meta": "data"},
1152 "call": "openapi",
1153 "with": {
1154 "document": {
1155 "name": "MyOpenAPIDoc",
1156 "endpoint": "http://example.com/openapi.json"
1157 },
1158 "operationId": "getUsers",
1159 "parameters": {"param1": "value1"},
1160 "authentication": {"use": "my-auth"},
1161 "output": "content",
1162 "redirect": true
1163 }
1164 }"#;
1165 let openapi: CallOpenAPIDefinition = serde_json::from_str(json).unwrap();
1166 assert_eq!(openapi.call, "openapi");
1167 assert_eq!(openapi.common.if_, Some("${condition}".to_string()));
1168 assert!(openapi.common.input.is_some());
1169 assert!(openapi.common.output.is_some());
1170 assert!(openapi.common.timeout.is_some());
1171 assert_eq!(openapi.common.then, Some("continue".to_string()));
1172 assert!(openapi.common.metadata.is_some());
1173 assert_eq!(openapi.with.operation_id, "getUsers");
1174 assert_eq!(openapi.with.output, Some("content".to_string()));
1175 assert_eq!(openapi.with.redirect, Some(true));
1176 }
1177
1178 #[test]
1179 fn test_call_openapi_with_common_fields_roundtrip() {
1180 let json = r#"{
1181 "if": "${condition}",
1182 "output": {"as": {"result": "output"}},
1183 "then": "continue",
1184 "call": "openapi",
1185 "with": {
1186 "document": {"endpoint": "http://example.com/openapi.json"},
1187 "operationId": "op1"
1188 }
1189 }"#;
1190 let openapi: CallOpenAPIDefinition = serde_json::from_str(json).unwrap();
1191 let serialized = serde_json::to_string(&openapi).unwrap();
1192 let deserialized: CallOpenAPIDefinition = serde_json::from_str(&serialized).unwrap();
1193 assert_eq!(openapi, deserialized);
1194 }
1195
1196 #[test]
1197 fn test_call_asyncapi_with_common_fields() {
1198 let json = r#"{
1200 "if": "${condition}",
1201 "input": {"from": {"key": "value"}},
1202 "output": {"as": {"result": "output"}},
1203 "timeout": {"after": "PT10S"},
1204 "then": "continue",
1205 "metadata": {"meta": "data"},
1206 "call": "asyncapi",
1207 "with": {
1208 "document": {
1209 "name": "MyAsyncAPIDoc",
1210 "endpoint": "http://example.com/asyncapi.json"
1211 },
1212 "operation": "user.signup",
1213 "server": {"name": "default-server"},
1214 "protocol": "http",
1215 "message": {
1216 "payload": {"userId": "12345"}
1217 },
1218 "authentication": {"use": "asyncapi-auth-policy"}
1219 }
1220 }"#;
1221 let asyncapi: CallAsyncAPIDefinition = serde_json::from_str(json).unwrap();
1222 assert_eq!(asyncapi.call, "asyncapi");
1223 assert_eq!(asyncapi.common.if_, Some("${condition}".to_string()));
1224 assert!(asyncapi.common.input.is_some());
1225 assert!(asyncapi.common.output.is_some());
1226 assert!(asyncapi.common.timeout.is_some());
1227 assert_eq!(asyncapi.common.then, Some("continue".to_string()));
1228 assert!(asyncapi.common.metadata.is_some());
1229 assert_eq!(asyncapi.with.operation, Some("user.signup".to_string()));
1230 assert_eq!(asyncapi.with.protocol, Some("http".to_string()));
1231 }
1232
1233 #[test]
1234 fn test_call_asyncapi_with_common_fields_roundtrip() {
1235 let json = r#"{
1236 "if": "${condition}",
1237 "output": {"as": {"result": "output"}},
1238 "then": "continue",
1239 "call": "asyncapi",
1240 "with": {
1241 "document": {"endpoint": "http://example.com/asyncapi.json"},
1242 "operation": "user.signup",
1243 "protocol": "http"
1244 }
1245 }"#;
1246 let asyncapi: CallAsyncAPIDefinition = serde_json::from_str(json).unwrap();
1247 let serialized = serde_json::to_string(&asyncapi).unwrap();
1248 let deserialized: CallAsyncAPIDefinition = serde_json::from_str(&serialized).unwrap();
1249 assert_eq!(asyncapi, deserialized);
1250 }
1251
1252 #[test]
1253 fn test_call_function_with_common_fields() {
1254 let json = r#"{
1256 "if": "${condition}",
1257 "input": {"from": {"key": "value"}},
1258 "output": {"as": {"result": "output"}},
1259 "timeout": {"after": "PT10S"},
1260 "then": "continue",
1261 "metadata": {"meta": "data"},
1262 "call": "myFunction",
1263 "with": {"param1": "value1", "param2": 42}
1264 }"#;
1265 let func: CallFunctionDefinition = serde_json::from_str(json).unwrap();
1266 assert_eq!(func.call, "myFunction");
1267 assert_eq!(func.common.if_, Some("${condition}".to_string()));
1268 assert!(func.common.input.is_some());
1269 assert!(func.common.output.is_some());
1270 assert!(func.common.timeout.is_some());
1271 assert_eq!(func.common.then, Some("continue".to_string()));
1272 assert!(func.common.metadata.is_some());
1273 }
1274
1275 #[test]
1276 fn test_call_a2a_with_common_fields() {
1277 let json = r#"{
1278 "if": "${condition}",
1279 "input": {"from": {"key": "value"}},
1280 "output": {"as": {"result": "output"}},
1281 "timeout": {"after": "PT10S"},
1282 "then": "continue",
1283 "call": "a2a",
1284 "with": {
1285 "method": "message/send",
1286 "parameters": {"message": "hello"}
1287 }
1288 }"#;
1289 let a2a: CallA2ADefinition = serde_json::from_str(json).unwrap();
1290 assert_eq!(a2a.call, "a2a");
1291 assert_eq!(a2a.common.if_, Some("${condition}".to_string()));
1292 assert!(a2a.common.input.is_some());
1293 assert!(a2a.common.output.is_some());
1294 assert!(a2a.common.timeout.is_some());
1295 assert_eq!(a2a.common.then, Some("continue".to_string()));
1296 }
1297
1298 #[test]
1299 fn test_call_http_with_body() {
1300 let json = r#"{
1302 "call": "http",
1303 "with": {
1304 "method": "POST",
1305 "endpoint": "http://example.com/api",
1306 "headers": {"Content-Type": "application/json"},
1307 "body": {"name": "test", "value": 42},
1308 "output": "content"
1309 }
1310 }"#;
1311 let http: CallHTTPDefinition = serde_json::from_str(json).unwrap();
1312 assert_eq!(http.with.method, "POST");
1313 assert!(http.with.body.is_some());
1314 let body = http.with.body.unwrap();
1315 assert_eq!(body["name"], "test");
1316 assert_eq!(body["value"], 42);
1317 }
1318
1319 #[test]
1320 fn test_call_http_headers_expression() {
1321 let json = r#"{
1323 "call": "http",
1324 "with": {
1325 "method": "GET",
1326 "endpoint": "http://example.com",
1327 "headers": "${ .requestHeaders }"
1328 }
1329 }"#;
1330 let http: CallHTTPDefinition = serde_json::from_str(json).unwrap();
1331 match &http.with.headers {
1332 Some(OneOfHeadersOrExpression::Expression(expr)) => {
1333 assert_eq!(expr, "${ .requestHeaders }");
1334 }
1335 _ => panic!("Expected Expression variant for headers"),
1336 }
1337 }
1338
1339 #[test]
1340 fn test_call_http_query_expression() {
1341 let json = r#"{
1343 "call": "http",
1344 "with": {
1345 "method": "GET",
1346 "endpoint": "http://example.com",
1347 "query": "${ .queryParams }"
1348 }
1349 }"#;
1350 let http: CallHTTPDefinition = serde_json::from_str(json).unwrap();
1351 match &http.with.query {
1352 Some(OneOfQueryOrExpression::Expression(expr)) => {
1353 assert_eq!(expr, "${ .queryParams }");
1354 }
1355 _ => panic!("Expected Expression variant for query"),
1356 }
1357 }
1358
1359 #[test]
1360 fn test_call_grpc_service_with_authentication_roundtrip() {
1361 let json = r#"{
1362 "call": "grpc",
1363 "with": {
1364 "proto": {"endpoint": "http://example.com/proto"},
1365 "service": {
1366 "name": "Svc",
1367 "host": "example.com",
1368 "authentication": {"use": "grpc-auth"}
1369 },
1370 "method": "Get"
1371 }
1372 }"#;
1373 let grpc: CallGRPCDefinition = serde_json::from_str(json).unwrap();
1374 let serialized = serde_json::to_string(&grpc).unwrap();
1375 let deserialized: CallGRPCDefinition = serde_json::from_str(&serialized).unwrap();
1376 assert_eq!(grpc, deserialized);
1377 }
1378
1379 #[test]
1380 fn test_call_a2a_with_parameters_expression() {
1381 let json = r#"{
1383 "call": "a2a",
1384 "with": {
1385 "method": "tasks/get",
1386 "parameters": "${ .taskParams }"
1387 }
1388 }"#;
1389 let a2a: CallA2ADefinition = serde_json::from_str(json).unwrap();
1390 match &a2a.with.parameters {
1391 Some(OneOfA2AParametersOrExpression::Expression(expr)) => {
1392 assert_eq!(expr, "${ .taskParams }");
1393 }
1394 _ => panic!("Expected Expression variant for parameters"),
1395 }
1396 }
1397
1398 #[test]
1399 fn test_call_grpc_with_authentication() {
1400 let json = r#"{
1402 "call": "grpc",
1403 "with": {
1404 "proto": {"endpoint": "http://example.com/proto"},
1405 "service": {
1406 "name": "UserService",
1407 "host": "example.com"
1408 },
1409 "method": "GetUser",
1410 "arguments": {"userId": "12345"},
1411 "authentication": {"use": "my-auth"}
1412 }
1413 }"#;
1414 let grpc: CallGRPCDefinition = serde_json::from_str(json).unwrap();
1415 assert_eq!(grpc.call, "grpc");
1416 assert!(grpc.with.authentication.is_some());
1417 match grpc.with.authentication.unwrap() {
1418 ReferenceableAuthenticationPolicy::Reference(r) => {
1419 assert_eq!(r.use_, "my-auth");
1420 }
1421 _ => panic!("Expected Reference variant"),
1422 }
1423 }
1424
1425 #[test]
1426 fn test_call_asyncapi_channel() {
1427 let json = r#"{
1429 "call": "asyncapi",
1430 "with": {
1431 "document": {"endpoint": "http://example.com/asyncapi.json"},
1432 "channel": "user-events",
1433 "protocol": "kafka",
1434 "message": {"payload": {"event": "created"}}
1435 }
1436 }"#;
1437 let asyncapi: CallAsyncAPIDefinition = serde_json::from_str(json).unwrap();
1438 assert_eq!(asyncapi.with.channel, Some("user-events".to_string()));
1439 assert!(asyncapi.with.message.is_some());
1440 }
1441}