//// Caller-side SDK for aion-server workflow operations. import aion_client/error.{type Error} import aion_client/payload.{type Payload} import gleam/dynamic/decode import gleam/json import gleam/list import gleam/option.{type Option, None, Some} pub type Config { Config( endpoint: String, bearer_token: Option(String), namespace: String, tls: Bool, ) } pub type Client { Client(config: Config, transport: Transport) } pub type WorkflowHandle { WorkflowHandle(client: Client, workflow_id: String, run_id: String) } pub type Transport { Transport( start: fn(Config, StartRequest) -> Result(StartResponse, Error), signal: fn(Config, SignalRequest) -> Result(Nil, Error), query: fn(Config, QueryRequest) -> Result(Payload, Error), cancel: fn(Config, CancelRequest) -> Result(Nil, Error), list: fn(Config, ListRequest) -> Result(List(WorkflowSummary), Error), describe: fn(Config, DescribeRequest) -> Result(WorkflowDescription, Error), ) } pub type StartOptions { StartOptions( workflow_id: String, workflow_type: String, task_queue: String, idempotency_key: Option(String), ) } pub type SignalOptions { SignalOptions( workflow_id: String, run_id: Option(String), signal_name: String, ) } pub type QueryOptions { QueryOptions(workflow_id: String, run_id: Option(String), query_name: String) } pub type CancelOptions { CancelOptions(workflow_id: String, run_id: Option(String), reason: String) } pub type ListOptions { ListOptions(namespace: Option(String)) } pub type DescribeOptions { DescribeOptions(workflow_id: String, run_id: Option(String)) } pub type StartRequest { StartRequest(options: StartOptions, input: Payload) } pub type StartResponse { StartResponse(workflow_id: String, run_id: String) } pub type SignalRequest { SignalRequest(options: SignalOptions, input: Payload) } pub type QueryRequest { QueryRequest(options: QueryOptions, args: Payload) } pub type CancelRequest { CancelRequest(options: CancelOptions) } pub type ListRequest { ListRequest(options: ListOptions) } pub type DescribeRequest { DescribeRequest(options: DescribeOptions) } pub type WorkflowSummary { WorkflowSummary( workflow_id: String, run_id: String, workflow_type: String, status: String, ) } pub type WorkflowDescription { WorkflowDescription( workflow_id: String, run_id: String, workflow_type: String, status: String, ) } /// Connect once to an aion-server deployment. The current implementation stores /// validated connection configuration and reuses it for all operations; concrete /// transport failures surface as operation Results, preserving branchable errors. pub fn connect(config: Config) -> Result(Client, Error) { case config.endpoint == "" || config.namespace == "" { True -> Error(error.InvalidArgument) False -> Ok(Client(config: config, transport: unavailable_transport())) } } /// Test/conformance hook for injecting an HTTP/WebSocket transport while keeping /// the public SDK semantics identical. pub fn with_transport( config: Config, transport: Transport, ) -> Result(Client, Error) { case config.endpoint == "" || config.namespace == "" { True -> Error(error.InvalidArgument) False -> Ok(Client(config: config, transport: transport)) } } pub fn start( client: Client, options: StartOptions, input: input, encoder: fn(input) -> json.Json, ) -> Result(WorkflowHandle, Error) { start_raw(client, options, payload.encode(input, encoder)) } pub fn start_raw( client: Client, options: StartOptions, input: Payload, ) -> Result(WorkflowHandle, Error) { let Client(config: config, transport: transport) = client case transport.start(config, StartRequest(options: options, input: input)) { Ok(StartResponse(workflow_id: workflow_id, run_id: run_id)) -> Ok(WorkflowHandle( client: client, workflow_id: workflow_id, run_id: run_id, )) Error(error) -> Error(error) } } pub fn signal( client: Client, options: SignalOptions, input: input, encoder: fn(input) -> json.Json, ) -> Result(Nil, Error) { signal_raw(client, options, payload.encode(input, encoder)) } pub fn signal_raw( client: Client, options: SignalOptions, input: Payload, ) -> Result(Nil, Error) { let Client(config: config, transport: transport) = client transport.signal(config, SignalRequest(options: options, input: input)) } pub fn query( client: Client, options: QueryOptions, args: args, encoder: fn(args) -> json.Json, decoder: decode.Decoder(result), ) -> Result(result, Error) { query_raw(client, options, payload.encode(args, encoder), decoder) } pub fn query_raw( client: Client, options: QueryOptions, args: Payload, decoder: decode.Decoder(result), ) -> Result(result, Error) { case query_payload(client, options, args) { Ok(reply) -> payload.decode(reply, decoder) Error(error) -> Error(error) } } pub fn query_payload( client: Client, options: QueryOptions, args: Payload, ) -> Result(Payload, Error) { let Client(config: config, transport: transport) = client transport.query(config, QueryRequest(options: options, args: args)) } pub fn cancel(client: Client, options: CancelOptions) -> Result(Nil, Error) { let Client(config: config, transport: transport) = client transport.cancel(config, CancelRequest(options: options)) } pub fn list( client: Client, options: ListOptions, ) -> Result(List(WorkflowSummary), Error) { let Client(config: config, transport: transport) = client transport.list(config, ListRequest(options: options)) } pub fn describe( client: Client, options: DescribeOptions, ) -> Result(WorkflowDescription, Error) { let Client(config: config, transport: transport) = client transport.describe(config, DescribeRequest(options: options)) } pub fn latest_run(run_id: Option(String), default_run_id: String) -> String { case run_id { Some(run_id) -> run_id None -> default_run_id } } pub fn default_list_options(config: Config) -> ListOptions { let Config(namespace: namespace, ..) = config ListOptions(namespace: Some(namespace)) } pub fn workflow_ids(summaries: List(WorkflowSummary)) -> List(String) { summaries |> list.map(fn(summary) { let WorkflowSummary(workflow_id: workflow_id, ..) = summary workflow_id }) } fn unavailable_transport() -> Transport { Transport( start: fn(_, _) { Error(error.Unavailable) }, signal: fn(_, _) { Error(error.Unavailable) }, query: fn(_, _) { Error(error.Unavailable) }, cancel: fn(_, _) { Error(error.Unavailable) }, list: fn(_, _) { Error(error.Unavailable) }, describe: fn(_, _) { Error(error.Unavailable) }, ) }