use crate::codec::app::{
from_wire_get_session_catalog_response, from_wire_operation_result,
from_wire_provider_metadata, from_wire_resolve_http_subject_response,
from_wire_start_provider_response, to_wire_app_invoke_graphql_request,
to_wire_app_invoke_request, to_wire_execute_request, to_wire_get_session_catalog_request,
to_wire_resolve_http_subject_request, to_wire_start_provider_request,
};
use crate::codec::host_service::{HostServiceChannel, connect_host_service, plain_channel};
use crate::generated::v1;
use crate::invoke_support::{InvokeError, decode_app_result};
use crate::rpc_support::GestaltError;
pub type ConnectionMode = i32;
pub mod connection_mode {
pub const CONNECTION_MODE_UNSPECIFIED: i32 = 0;
pub const CONNECTION_MODE_NONE: i32 = 1;
pub const CONNECTION_MODE_SUBJECT: i32 = 2;
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct AccessContext {
pub policy: String,
pub role: String,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct AgentInvocationContext {
pub provider_name: String,
pub session_id: String,
pub turn_id: String,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct AgentToolRef {
pub app: String,
pub operation: String,
pub connection: String,
pub instance: String,
pub title: String,
pub description: String,
pub credential_mode: String,
pub system: String,
pub run_as: Option<SubjectContext>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct AppInvokeGraphQLRequest {
pub app: String,
pub document: String,
pub variables: Option<serde_json::Map<String, serde_json::Value>>,
pub connection: String,
pub instance: String,
pub idempotency_key: String,
pub context: Option<RequestContext>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct AppInvokeRequest {
pub app: String,
pub operation: String,
pub params: Option<serde_json::Map<String, serde_json::Value>>,
pub connection: String,
pub instance: String,
pub idempotency_key: String,
pub credential_mode: String,
pub context: Option<RequestContext>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Catalog {
pub name: String,
pub display_name: String,
pub description: String,
pub icon_svg: String,
pub operations: Vec<CatalogOperation>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct CatalogOperation {
pub id: String,
pub method: String,
pub title: String,
pub description: String,
pub input_schema: String,
pub output_schema: String,
pub annotations: Option<OperationAnnotations>,
pub parameters: Vec<CatalogParameter>,
pub required_scopes: Vec<String>,
pub tags: Vec<String>,
pub read_only: bool,
pub visible: Option<bool>,
pub transport: String,
pub allowed_roles: Vec<String>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct CatalogParameter {
pub name: String,
pub r#type: String,
pub description: String,
pub required: bool,
pub default: Option<serde_json::Value>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ConnectionParamDef {
pub required: bool,
pub description: String,
pub default_value: String,
pub from: String,
pub field: String,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct CredentialContext {
pub mode: String,
pub subject_id: String,
pub connection: String,
pub instance: String,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ExecuteRequest {
pub operation: String,
pub params: Option<serde_json::Map<String, serde_json::Value>>,
pub token: String,
pub connection_params: std::collections::BTreeMap<String, String>,
pub invocation_id: String,
pub context: Option<RequestContext>,
pub idempotency_key: String,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct GetSessionCatalogRequest {
pub token: String,
pub connection_params: std::collections::BTreeMap<String, String>,
pub invocation_id: String,
pub context: Option<RequestContext>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct GetSessionCatalogResponse {
pub catalog: Option<Catalog>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct HTTPSubjectRequest {
pub binding: String,
pub method: String,
pub path: String,
pub content_type: String,
pub headers: std::collections::BTreeMap<String, StringList>,
pub query: std::collections::BTreeMap<String, StringList>,
pub params: Option<serde_json::Map<String, serde_json::Value>>,
pub raw_body: Vec<u8>,
pub security_scheme: String,
pub verified_subject: String,
pub verified_claims: std::collections::BTreeMap<String, String>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct HostContext {
pub public_base_url: String,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct InvocationContext {
pub request_id: String,
pub depth: i32,
pub call_chain: Vec<String>,
pub surface: String,
pub internal_connection_access: bool,
pub connection: String,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct OperationAnnotations {
pub read_only_hint: Option<bool>,
pub idempotent_hint: Option<bool>,
pub destructive_hint: Option<bool>,
pub open_world_hint: Option<bool>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct OperationResult {
pub status: i32,
pub body: Vec<u8>,
pub headers: std::collections::BTreeMap<String, StringList>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ProviderContext {
pub kind: String,
pub name: String,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ProviderMetadata {
pub name: String,
pub display_name: String,
pub description: String,
pub connection_mode: ConnectionMode,
pub auth_types: Vec<String>,
pub connection_params: std::collections::BTreeMap<String, ConnectionParamDef>,
pub static_catalog: Option<Catalog>,
pub supports_session_catalog: bool,
pub min_protocol_version: i32,
pub max_protocol_version: i32,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct RequestContext {
pub subject: Option<SubjectContext>,
pub credential: Option<CredentialContext>,
pub access: Option<AccessContext>,
pub workflow: Option<serde_json::Map<String, serde_json::Value>>,
pub host: Option<HostContext>,
pub agent_subject: Option<SubjectContext>,
pub caller: Option<ProviderContext>,
pub invocation: Option<InvocationContext>,
pub tool_refs: Vec<AgentToolRef>,
pub tool_refs_set: bool,
pub request_meta: Option<RequestMetaContext>,
pub agent: Option<AgentInvocationContext>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct RequestMetaContext {
pub client_ip: String,
pub remote_addr: String,
pub user_agent: String,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ResolveHTTPSubjectRequest {
pub request: Option<HTTPSubjectRequest>,
pub context: Option<RequestContext>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ResolveHTTPSubjectResponse {
pub subject: Option<SubjectContext>,
pub reject_status: i32,
pub reject_message: String,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct StartProviderRequest {
pub name: String,
pub config: Option<serde_json::Map<String, serde_json::Value>>,
pub protocol_version: i32,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct StartProviderResponse {
pub protocol_version: i32,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct StringList {
pub values: Vec<String>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct SubjectContext {
pub id: String,
pub credential_subject_id: String,
pub email: String,
pub display_name: String,
pub scopes: Vec<String>,
pub permissions: Vec<SubjectPermissionContext>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct SubjectPermissionContext {
pub app: String,
pub operations: Vec<String>,
pub all_operations: bool,
}
pub struct App {
inner: v1::app_client::AppClient<HostServiceChannel>,
timeout: Option<std::time::Duration>,
context: Option<RequestContext>,
}
impl App {
pub fn new(channel: tonic::transport::Channel) -> Self {
Self {
inner: v1::app_client::AppClient::new(plain_channel(channel)),
timeout: None,
context: None,
}
}
pub fn with_timeout(mut self, timeout: std::time::Duration) -> Self {
self.timeout = Some(timeout);
self
}
pub fn with_context(mut self, context: RequestContext) -> Self {
self.context = Some(context);
self
}
pub async fn connect() -> Result<Self, GestaltError> {
Self::connect_named("").await
}
pub async fn connect_named(name: &str) -> Result<Self, GestaltError> {
Ok(Self {
inner: v1::app_client::AppClient::new(connect_host_service("app", name).await?),
timeout: None,
context: None,
})
}
pub async fn invoke(
&mut self,
app: String,
operation: String,
params: Option<serde_json::Map<String, serde_json::Value>>,
options: AppInvokeOptions,
) -> Result<serde_json::Value, InvokeError> {
let request = AppInvokeRequest {
app,
operation,
params,
connection: options.connection,
instance: options.instance,
idempotency_key: options.idempotency_key,
credential_mode: options.credential_mode,
context: self.context.clone(),
};
let invoke_context_app = request.app.clone();
let invoke_context_operation = request.operation.clone();
let mut tonic_request = tonic::Request::new(to_wire_app_invoke_request(request));
if let Some(timeout) = self.timeout {
tonic_request.set_timeout(timeout);
}
let response = from_wire_operation_result(
self.inner
.invoke(tonic_request)
.await
.map_err(GestaltError::from)?
.into_inner(),
);
Ok(decode_app_result(
&invoke_context_app,
&invoke_context_operation,
response.status,
&response.body,
)?)
}
pub async fn invoke_raw(
&mut self,
request: AppInvokeRequest,
) -> Result<OperationResult, GestaltError> {
let mut request = request;
if request.context.is_none() {
request.context = self.context.clone();
}
let mut tonic_request = tonic::Request::new(to_wire_app_invoke_request(request));
if let Some(timeout) = self.timeout {
tonic_request.set_timeout(timeout);
}
let response = self.inner.invoke(tonic_request).await?;
Ok(from_wire_operation_result(response.into_inner()))
}
pub async fn invoke_graphql(
&mut self,
app: String,
document: String,
options: AppInvokeGraphQLOptions,
) -> Result<OperationResult, GestaltError> {
let request = AppInvokeGraphQLRequest {
app,
document,
connection: options.connection,
instance: options.instance,
idempotency_key: options.idempotency_key,
variables: options.variables,
context: self.context.clone(),
};
let mut tonic_request = tonic::Request::new(to_wire_app_invoke_graphql_request(request));
if let Some(timeout) = self.timeout {
tonic_request.set_timeout(timeout);
}
let response = self.inner.invoke_graph_ql(tonic_request).await?;
Ok(from_wire_operation_result(response.into_inner()))
}
pub async fn invoke_graphql_raw(
&mut self,
request: AppInvokeGraphQLRequest,
) -> Result<OperationResult, GestaltError> {
let mut request = request;
if request.context.is_none() {
request.context = self.context.clone();
}
let mut tonic_request = tonic::Request::new(to_wire_app_invoke_graphql_request(request));
if let Some(timeout) = self.timeout {
tonic_request.set_timeout(timeout);
}
let response = self.inner.invoke_graph_ql(tonic_request).await?;
Ok(from_wire_operation_result(response.into_inner()))
}
}
#[derive(Clone, Debug, Default)]
pub struct AppInvokeOptions {
pub connection: String,
pub instance: String,
pub idempotency_key: String,
pub credential_mode: String,
}
#[derive(Clone, Debug, Default)]
pub struct AppInvokeGraphQLOptions {
pub connection: String,
pub instance: String,
pub idempotency_key: String,
pub variables: Option<serde_json::Map<String, serde_json::Value>>,
}
pub struct AppProvider {
inner: v1::app_provider_client::AppProviderClient<tonic::transport::Channel>,
timeout: Option<std::time::Duration>,
context: Option<RequestContext>,
}
impl AppProvider {
pub fn new(channel: tonic::transport::Channel) -> Self {
Self {
inner: v1::app_provider_client::AppProviderClient::new(channel),
timeout: None,
context: None,
}
}
pub fn with_timeout(mut self, timeout: std::time::Duration) -> Self {
self.timeout = Some(timeout);
self
}
pub fn with_context(mut self, context: RequestContext) -> Self {
self.context = Some(context);
self
}
pub async fn get_metadata(&mut self) -> Result<ProviderMetadata, GestaltError> {
let mut tonic_request = tonic::Request::new(());
if let Some(timeout) = self.timeout {
tonic_request.set_timeout(timeout);
}
let response = self.inner.get_metadata(tonic_request).await?;
Ok(from_wire_provider_metadata(response.into_inner()))
}
pub async fn start_provider(
&mut self,
name: String,
protocol_version: i32,
config: Option<serde_json::Map<String, serde_json::Value>>,
) -> Result<StartProviderResponse, GestaltError> {
let request = StartProviderRequest {
name,
protocol_version,
config,
};
let mut tonic_request = tonic::Request::new(to_wire_start_provider_request(request));
if let Some(timeout) = self.timeout {
tonic_request.set_timeout(timeout);
}
let response = self.inner.start_provider(tonic_request).await?;
Ok(from_wire_start_provider_response(response.into_inner()))
}
pub async fn start_provider_raw(
&mut self,
request: StartProviderRequest,
) -> Result<StartProviderResponse, GestaltError> {
let mut tonic_request = tonic::Request::new(to_wire_start_provider_request(request));
if let Some(timeout) = self.timeout {
tonic_request.set_timeout(timeout);
}
let response = self.inner.start_provider(tonic_request).await?;
Ok(from_wire_start_provider_response(response.into_inner()))
}
pub async fn execute(
&mut self,
operation: String,
token: String,
invocation_id: String,
idempotency_key: String,
params: Option<serde_json::Map<String, serde_json::Value>>,
) -> Result<OperationResult, GestaltError> {
let request = ExecuteRequest {
operation,
token,
invocation_id,
idempotency_key,
params,
context: self.context.clone(),
..Default::default()
};
let mut tonic_request = tonic::Request::new(to_wire_execute_request(request));
if let Some(timeout) = self.timeout {
tonic_request.set_timeout(timeout);
}
let response = self.inner.execute(tonic_request).await?;
Ok(from_wire_operation_result(response.into_inner()))
}
pub async fn execute_raw(
&mut self,
request: ExecuteRequest,
) -> Result<OperationResult, GestaltError> {
let mut request = request;
if request.context.is_none() {
request.context = self.context.clone();
}
let mut tonic_request = tonic::Request::new(to_wire_execute_request(request));
if let Some(timeout) = self.timeout {
tonic_request.set_timeout(timeout);
}
let response = self.inner.execute(tonic_request).await?;
Ok(from_wire_operation_result(response.into_inner()))
}
pub async fn resolve_http_subject(
&mut self,
request: ResolveHTTPSubjectRequest,
) -> Result<ResolveHTTPSubjectResponse, GestaltError> {
let mut request = request;
if request.context.is_none() {
request.context = self.context.clone();
}
let mut tonic_request = tonic::Request::new(to_wire_resolve_http_subject_request(request));
if let Some(timeout) = self.timeout {
tonic_request.set_timeout(timeout);
}
let response = self.inner.resolve_http_subject(tonic_request).await?;
Ok(from_wire_resolve_http_subject_response(
response.into_inner(),
))
}
pub async fn get_session_catalog(
&mut self,
token: String,
invocation_id: String,
) -> Result<GetSessionCatalogResponse, GestaltError> {
let request = GetSessionCatalogRequest {
token,
invocation_id,
context: self.context.clone(),
..Default::default()
};
let mut tonic_request = tonic::Request::new(to_wire_get_session_catalog_request(request));
if let Some(timeout) = self.timeout {
tonic_request.set_timeout(timeout);
}
let response = self.inner.get_session_catalog(tonic_request).await?;
Ok(from_wire_get_session_catalog_response(
response.into_inner(),
))
}
pub async fn get_session_catalog_raw(
&mut self,
request: GetSessionCatalogRequest,
) -> Result<GetSessionCatalogResponse, GestaltError> {
let mut request = request;
if request.context.is_none() {
request.context = self.context.clone();
}
let mut tonic_request = tonic::Request::new(to_wire_get_session_catalog_request(request));
if let Some(timeout) = self.timeout {
tonic_request.set_timeout(timeout);
}
let response = self.inner.get_session_catalog(tonic_request).await?;
Ok(from_wire_get_session_catalog_response(
response.into_inner(),
))
}
}