1use crate::codec::app::{
6 from_wire_get_session_catalog_response, from_wire_operation_result,
7 from_wire_provider_metadata, from_wire_resolve_http_subject_response,
8 from_wire_start_provider_response, to_wire_app_invoke_graphql_request,
9 to_wire_app_invoke_request, to_wire_execute_request, to_wire_get_session_catalog_request,
10 to_wire_resolve_http_subject_request, to_wire_start_provider_request,
11};
12use crate::codec::host_service::{HostServiceChannel, connect_host_service, plain_channel};
13use crate::generated::v1;
14use crate::invoke_support::{InvokeError, decode_app_result};
15use crate::rpc_support::GestaltError;
16
17pub type ConnectionMode = i32;
19
20pub mod connection_mode {
24 pub const CONNECTION_MODE_UNSPECIFIED: i32 = 0;
26 pub const CONNECTION_MODE_NONE: i32 = 1;
28 pub const CONNECTION_MODE_SUBJECT: i32 = 2;
30}
31
32#[derive(Clone, Debug, Default, PartialEq)]
36pub struct AccessContext {
37 pub policy: String,
39 pub role: String,
41}
42
43#[derive(Clone, Debug, Default, PartialEq)]
45pub struct AgentInvocationContext {
46 pub provider_name: String,
48 pub session_id: String,
50 pub turn_id: String,
52}
53
54#[derive(Clone, Debug, Default, PartialEq)]
56pub struct AgentToolRef {
57 pub app: String,
59 pub operation: String,
61 pub connection: String,
63 pub instance: String,
65 pub title: String,
67 pub description: String,
69 pub credential_mode: String,
71 pub system: String,
73 pub run_as: Option<SubjectContext>,
75}
76
77#[derive(Clone, Debug, Default, PartialEq)]
82pub struct AppInvokeGraphQLRequest {
83 pub app: String,
85 pub document: String,
87 pub variables: Option<serde_json::Map<String, serde_json::Value>>,
89 pub connection: String,
91 pub instance: String,
93 pub idempotency_key: String,
95 pub context: Option<RequestContext>,
97}
98
99#[derive(Clone, Debug, Default, PartialEq)]
103pub struct AppInvokeRequest {
104 pub app: String,
106 pub operation: String,
108 pub params: Option<serde_json::Map<String, serde_json::Value>>,
110 pub connection: String,
112 pub instance: String,
114 pub idempotency_key: String,
116 pub credential_mode: String,
118 pub context: Option<RequestContext>,
120}
121
122#[derive(Clone, Debug, Default, PartialEq)]
127pub struct Catalog {
128 pub name: String,
130 pub display_name: String,
132 pub description: String,
134 pub icon_svg: String,
136 pub operations: Vec<CatalogOperation>,
138}
139
140#[derive(Clone, Debug, Default, PartialEq)]
145pub struct CatalogOperation {
146 pub id: String,
148 pub method: String,
150 pub title: String,
152 pub description: String,
154 pub input_schema: String,
156 pub output_schema: String,
158 pub annotations: Option<OperationAnnotations>,
160 pub parameters: Vec<CatalogParameter>,
162 pub required_scopes: Vec<String>,
164 pub tags: Vec<String>,
166 pub read_only: bool,
168 pub visible: Option<bool>,
170 pub transport: String,
172 pub allowed_roles: Vec<String>,
174}
175
176#[derive(Clone, Debug, Default, PartialEq)]
181pub struct CatalogParameter {
182 pub name: String,
184 pub r#type: String,
186 pub description: String,
188 pub required: bool,
190 pub default: Option<serde_json::Value>,
192}
193
194#[derive(Clone, Debug, Default, PartialEq)]
198pub struct ConnectionParamDef {
199 pub required: bool,
201 pub description: String,
203 pub default_value: String,
205 pub from: String,
207 pub field: String,
209}
210
211#[derive(Clone, Debug, Default, PartialEq)]
215pub struct CredentialContext {
216 pub mode: String,
218 pub subject_id: String,
220 pub connection: String,
222 pub instance: String,
224}
225
226#[derive(Clone, Debug, Default, PartialEq)]
230pub struct ExecuteRequest {
231 pub operation: String,
233 pub params: Option<serde_json::Map<String, serde_json::Value>>,
235 pub token: String,
237 pub connection_params: std::collections::BTreeMap<String, String>,
239 pub invocation_id: String,
241 pub context: Option<RequestContext>,
243 pub idempotency_key: String,
245}
246
247#[derive(Clone, Debug, Default, PartialEq)]
252pub struct GetSessionCatalogRequest {
253 pub token: String,
255 pub connection_params: std::collections::BTreeMap<String, String>,
257 pub invocation_id: String,
259 pub context: Option<RequestContext>,
261}
262
263#[derive(Clone, Debug, Default, PartialEq)]
267pub struct GetSessionCatalogResponse {
268 pub catalog: Option<Catalog>,
270}
271
272#[derive(Clone, Debug, Default, PartialEq)]
277pub struct HTTPSubjectRequest {
278 pub binding: String,
280 pub method: String,
282 pub path: String,
284 pub content_type: String,
286 pub headers: std::collections::BTreeMap<String, StringList>,
288 pub query: std::collections::BTreeMap<String, StringList>,
290 pub params: Option<serde_json::Map<String, serde_json::Value>>,
292 pub raw_body: Vec<u8>,
294 pub security_scheme: String,
296 pub verified_subject: String,
298 pub verified_claims: std::collections::BTreeMap<String, String>,
300}
301
302#[derive(Clone, Debug, Default, PartialEq)]
306pub struct HostContext {
307 pub public_base_url: String,
309}
310
311#[derive(Clone, Debug, Default, PartialEq)]
313pub struct InvocationContext {
314 pub request_id: String,
316 pub depth: i32,
318 pub call_chain: Vec<String>,
320 pub surface: String,
322 pub internal_connection_access: bool,
324 pub connection: String,
326}
327
328#[derive(Clone, Debug, Default, PartialEq)]
333pub struct OperationAnnotations {
334 pub read_only_hint: Option<bool>,
336 pub idempotent_hint: Option<bool>,
338 pub destructive_hint: Option<bool>,
340 pub open_world_hint: Option<bool>,
342}
343
344#[derive(Clone, Debug, Default, PartialEq)]
348pub struct OperationResult {
349 pub status: i32,
351 pub body: Vec<u8>,
353 pub headers: std::collections::BTreeMap<String, StringList>,
355}
356
357#[derive(Clone, Debug, Default, PartialEq)]
362pub struct ProviderContext {
363 pub kind: String,
365 pub name: String,
367}
368
369#[derive(Clone, Debug, Default, PartialEq)]
373pub struct ProviderMetadata {
374 pub name: String,
376 pub display_name: String,
378 pub description: String,
380 pub connection_mode: ConnectionMode,
382 pub auth_types: Vec<String>,
384 pub connection_params: std::collections::BTreeMap<String, ConnectionParamDef>,
386 pub static_catalog: Option<Catalog>,
388 pub supports_session_catalog: bool,
390 pub min_protocol_version: i32,
392 pub max_protocol_version: i32,
394}
395
396#[derive(Clone, Debug, Default, PartialEq)]
401pub struct RequestContext {
402 pub subject: Option<SubjectContext>,
404 pub credential: Option<CredentialContext>,
406 pub access: Option<AccessContext>,
408 pub workflow: Option<serde_json::Map<String, serde_json::Value>>,
410 pub host: Option<HostContext>,
412 pub agent_subject: Option<SubjectContext>,
416 pub caller: Option<ProviderContext>,
418 pub invocation: Option<InvocationContext>,
420 pub tool_refs: Vec<AgentToolRef>,
425 pub tool_refs_set: bool,
430 pub request_meta: Option<RequestMetaContext>,
432 pub agent: Option<AgentInvocationContext>,
434}
435
436#[derive(Clone, Debug, Default, PartialEq)]
438pub struct RequestMetaContext {
439 pub client_ip: String,
441 pub remote_addr: String,
443 pub user_agent: String,
445}
446
447#[derive(Clone, Debug, Default, PartialEq)]
453pub struct ResolveHTTPSubjectRequest {
454 pub request: Option<HTTPSubjectRequest>,
456 pub context: Option<RequestContext>,
458}
459
460#[derive(Clone, Debug, Default, PartialEq)]
467pub struct ResolveHTTPSubjectResponse {
468 pub subject: Option<SubjectContext>,
470 pub reject_status: i32,
472 pub reject_message: String,
474}
475
476#[derive(Clone, Debug, Default, PartialEq)]
481pub struct StartProviderRequest {
482 pub name: String,
484 pub config: Option<serde_json::Map<String, serde_json::Value>>,
486 pub protocol_version: i32,
488}
489
490#[derive(Clone, Debug, Default, PartialEq)]
494pub struct StartProviderResponse {
495 pub protocol_version: i32,
497}
498
499#[derive(Clone, Debug, Default, PartialEq)]
503pub struct StringList {
504 pub values: Vec<String>,
506}
507
508#[derive(Clone, Debug, Default, PartialEq)]
512pub struct SubjectContext {
513 pub id: String,
515 pub credential_subject_id: String,
517 pub email: String,
519 pub display_name: String,
521 pub scopes: Vec<String>,
523 pub permissions: Vec<SubjectPermissionContext>,
525}
526
527#[derive(Clone, Debug, Default, PartialEq)]
529pub struct SubjectPermissionContext {
530 pub app: String,
532 pub operations: Vec<String>,
534 pub all_operations: bool,
536}
537
538pub struct App {
540 inner: v1::app_client::AppClient<HostServiceChannel>,
541 timeout: Option<std::time::Duration>,
542 context: Option<RequestContext>,
543}
544
545impl App {
546 pub fn new(channel: tonic::transport::Channel) -> Self {
548 Self {
549 inner: v1::app_client::AppClient::new(plain_channel(channel)),
550 timeout: None,
551 context: None,
552 }
553 }
554
555 pub fn with_timeout(mut self, timeout: std::time::Duration) -> Self {
558 self.timeout = Some(timeout);
559 self
560 }
561
562 pub fn with_context(mut self, context: RequestContext) -> Self {
565 self.context = Some(context);
566 self
567 }
568
569 pub async fn connect() -> Result<Self, GestaltError> {
571 Self::connect_named("").await
572 }
573
574 pub async fn connect_named(name: &str) -> Result<Self, GestaltError> {
576 Ok(Self {
577 inner: v1::app_client::AppClient::new(connect_host_service("app", name).await?),
578 timeout: None,
579 context: None,
580 })
581 }
582
583 pub async fn invoke(
587 &mut self,
588 app: String,
589 operation: String,
590 params: Option<serde_json::Map<String, serde_json::Value>>,
591 options: AppInvokeOptions,
592 ) -> Result<serde_json::Value, InvokeError> {
593 let request = AppInvokeRequest {
594 app,
595 operation,
596 params,
597 connection: options.connection,
598 instance: options.instance,
599 idempotency_key: options.idempotency_key,
600 credential_mode: options.credential_mode,
601 context: self.context.clone(),
602 };
603 let invoke_context_app = request.app.clone();
604 let invoke_context_operation = request.operation.clone();
605 let mut tonic_request = tonic::Request::new(to_wire_app_invoke_request(request));
606 if let Some(timeout) = self.timeout {
607 tonic_request.set_timeout(timeout);
608 }
609 let response = from_wire_operation_result(
610 self.inner
611 .invoke(tonic_request)
612 .await
613 .map_err(GestaltError::from)?
614 .into_inner(),
615 );
616 Ok(decode_app_result(
617 &invoke_context_app,
618 &invoke_context_operation,
619 response.status,
620 &response.body,
621 )?)
622 }
623
624 pub async fn invoke_raw(
626 &mut self,
627 request: AppInvokeRequest,
628 ) -> Result<OperationResult, GestaltError> {
629 let mut request = request;
630 if request.context.is_none() {
631 request.context = self.context.clone();
632 }
633 let mut tonic_request = tonic::Request::new(to_wire_app_invoke_request(request));
634 if let Some(timeout) = self.timeout {
635 tonic_request.set_timeout(timeout);
636 }
637 let response = self.inner.invoke(tonic_request).await?;
638 Ok(from_wire_operation_result(response.into_inner()))
639 }
640
641 pub async fn invoke_graphql(
643 &mut self,
644 app: String,
645 document: String,
646 options: AppInvokeGraphQLOptions,
647 ) -> Result<OperationResult, GestaltError> {
648 let request = AppInvokeGraphQLRequest {
649 app,
650 document,
651 connection: options.connection,
652 instance: options.instance,
653 idempotency_key: options.idempotency_key,
654 variables: options.variables,
655 context: self.context.clone(),
656 };
657 let mut tonic_request = tonic::Request::new(to_wire_app_invoke_graphql_request(request));
658 if let Some(timeout) = self.timeout {
659 tonic_request.set_timeout(timeout);
660 }
661 let response = self.inner.invoke_graph_ql(tonic_request).await?;
662 Ok(from_wire_operation_result(response.into_inner()))
663 }
664
665 pub async fn invoke_graphql_raw(
667 &mut self,
668 request: AppInvokeGraphQLRequest,
669 ) -> Result<OperationResult, GestaltError> {
670 let mut request = request;
671 if request.context.is_none() {
672 request.context = self.context.clone();
673 }
674 let mut tonic_request = tonic::Request::new(to_wire_app_invoke_graphql_request(request));
675 if let Some(timeout) = self.timeout {
676 tonic_request.set_timeout(timeout);
677 }
678 let response = self.inner.invoke_graph_ql(tonic_request).await?;
679 Ok(from_wire_operation_result(response.into_inner()))
680 }
681}
682
683#[derive(Clone, Debug, Default)]
686pub struct AppInvokeOptions {
687 pub connection: String,
689 pub instance: String,
691 pub idempotency_key: String,
693 pub credential_mode: String,
695}
696
697#[derive(Clone, Debug, Default)]
700pub struct AppInvokeGraphQLOptions {
701 pub connection: String,
703 pub instance: String,
705 pub idempotency_key: String,
707 pub variables: Option<serde_json::Map<String, serde_json::Value>>,
709}
710
711pub struct AppProvider {
715 inner: v1::app_provider_client::AppProviderClient<tonic::transport::Channel>,
716 timeout: Option<std::time::Duration>,
717 context: Option<RequestContext>,
718}
719
720impl AppProvider {
721 pub fn new(channel: tonic::transport::Channel) -> Self {
723 Self {
724 inner: v1::app_provider_client::AppProviderClient::new(channel),
725 timeout: None,
726 context: None,
727 }
728 }
729
730 pub fn with_timeout(mut self, timeout: std::time::Duration) -> Self {
733 self.timeout = Some(timeout);
734 self
735 }
736
737 pub fn with_context(mut self, context: RequestContext) -> Self {
740 self.context = Some(context);
741 self
742 }
743
744 pub async fn get_metadata(&mut self) -> Result<ProviderMetadata, GestaltError> {
746 let mut tonic_request = tonic::Request::new(());
747 if let Some(timeout) = self.timeout {
748 tonic_request.set_timeout(timeout);
749 }
750 let response = self.inner.get_metadata(tonic_request).await?;
751 Ok(from_wire_provider_metadata(response.into_inner()))
752 }
753
754 pub async fn start_provider(
756 &mut self,
757 name: String,
758 protocol_version: i32,
759 config: Option<serde_json::Map<String, serde_json::Value>>,
760 ) -> Result<StartProviderResponse, GestaltError> {
761 let request = StartProviderRequest {
762 name,
763 protocol_version,
764 config,
765 };
766 let mut tonic_request = tonic::Request::new(to_wire_start_provider_request(request));
767 if let Some(timeout) = self.timeout {
768 tonic_request.set_timeout(timeout);
769 }
770 let response = self.inner.start_provider(tonic_request).await?;
771 Ok(from_wire_start_provider_response(response.into_inner()))
772 }
773
774 pub async fn start_provider_raw(
776 &mut self,
777 request: StartProviderRequest,
778 ) -> Result<StartProviderResponse, GestaltError> {
779 let mut tonic_request = tonic::Request::new(to_wire_start_provider_request(request));
780 if let Some(timeout) = self.timeout {
781 tonic_request.set_timeout(timeout);
782 }
783 let response = self.inner.start_provider(tonic_request).await?;
784 Ok(from_wire_start_provider_response(response.into_inner()))
785 }
786
787 pub async fn execute(
789 &mut self,
790 operation: String,
791 token: String,
792 invocation_id: String,
793 idempotency_key: String,
794 params: Option<serde_json::Map<String, serde_json::Value>>,
795 ) -> Result<OperationResult, GestaltError> {
796 let request = ExecuteRequest {
797 operation,
798 token,
799 invocation_id,
800 idempotency_key,
801 params,
802 context: self.context.clone(),
803 ..Default::default()
804 };
805 let mut tonic_request = tonic::Request::new(to_wire_execute_request(request));
806 if let Some(timeout) = self.timeout {
807 tonic_request.set_timeout(timeout);
808 }
809 let response = self.inner.execute(tonic_request).await?;
810 Ok(from_wire_operation_result(response.into_inner()))
811 }
812
813 pub async fn execute_raw(
815 &mut self,
816 request: ExecuteRequest,
817 ) -> Result<OperationResult, GestaltError> {
818 let mut request = request;
819 if request.context.is_none() {
820 request.context = self.context.clone();
821 }
822 let mut tonic_request = tonic::Request::new(to_wire_execute_request(request));
823 if let Some(timeout) = self.timeout {
824 tonic_request.set_timeout(timeout);
825 }
826 let response = self.inner.execute(tonic_request).await?;
827 Ok(from_wire_operation_result(response.into_inner()))
828 }
829
830 pub async fn resolve_http_subject(
832 &mut self,
833 request: ResolveHTTPSubjectRequest,
834 ) -> Result<ResolveHTTPSubjectResponse, GestaltError> {
835 let mut request = request;
836 if request.context.is_none() {
837 request.context = self.context.clone();
838 }
839 let mut tonic_request = tonic::Request::new(to_wire_resolve_http_subject_request(request));
840 if let Some(timeout) = self.timeout {
841 tonic_request.set_timeout(timeout);
842 }
843 let response = self.inner.resolve_http_subject(tonic_request).await?;
844 Ok(from_wire_resolve_http_subject_response(
845 response.into_inner(),
846 ))
847 }
848
849 pub async fn get_session_catalog(
851 &mut self,
852 token: String,
853 invocation_id: String,
854 ) -> Result<GetSessionCatalogResponse, GestaltError> {
855 let request = GetSessionCatalogRequest {
856 token,
857 invocation_id,
858 context: self.context.clone(),
859 ..Default::default()
860 };
861 let mut tonic_request = tonic::Request::new(to_wire_get_session_catalog_request(request));
862 if let Some(timeout) = self.timeout {
863 tonic_request.set_timeout(timeout);
864 }
865 let response = self.inner.get_session_catalog(tonic_request).await?;
866 Ok(from_wire_get_session_catalog_response(
867 response.into_inner(),
868 ))
869 }
870
871 pub async fn get_session_catalog_raw(
873 &mut self,
874 request: GetSessionCatalogRequest,
875 ) -> Result<GetSessionCatalogResponse, GestaltError> {
876 let mut request = request;
877 if request.context.is_none() {
878 request.context = self.context.clone();
879 }
880 let mut tonic_request = tonic::Request::new(to_wire_get_session_catalog_request(request));
881 if let Some(timeout) = self.timeout {
882 tonic_request.set_timeout(timeout);
883 }
884 let response = self.inner.get_session_catalog(tonic_request).await?;
885 Ok(from_wire_get_session_catalog_response(
886 response.into_inner(),
887 ))
888 }
889}