/*
* SpatioAPI
*
* The REST API that owns every resource in your Spatio workspace: notes, sheets, slides, tasks, calendar events, mail, chat, files, and contacts. SpatioMCP wraps this API; Spatio Desktop reads from it. You can call it directly from your own code. All requests must be authenticated with a Personal Access Token (`Authorization: Bearer pat_...`) or an OAuth 2.1 access token, and use HTTPS. Official SDKs (MIT, generated from this spec on every release): - TypeScript: https://github.com/spatio-labs/spatio-ts (`npm install @spatio-labs/spatio-ts`) - Python: https://github.com/spatio-labs/spatio-py (`pip install spatio-sdk`) - Go: https://github.com/spatio-labs/spatio-go (`go get github.com/spatio-labs/spatio-go`) This specification is generated from the platform-service Go source on every push to `main`. The spec, not hand-written documentation, is the source of truth: server stubs and SDKs are generated from it, and any drift between the spec and the running service fails CI.
*
* The version of the OpenAPI document: v1
* Contact: hello@spatio.app
* Generated by: https://openapi-generator.tech
*/
use reqwest;
use serde::{Deserialize, Serialize, de::Error as _};
use crate::{apis::ResponseContent, models};
use super::{Error, configuration, ContentType};
/// struct for typed errors of method [`get_jwks`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetJwksError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_o_auth_discovery`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetOAuthDiscoveryError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_open_id_configuration`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetOpenIdConfigurationError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_user_info`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetUserInfoError {
Status401(),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`oauth_authorize`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum OauthAuthorizeError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`oauth_introspect`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum OauthIntrospectError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`oauth_revoke`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum OauthRevokeError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`oauth_token`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum OauthTokenError {
Status400(models::OAuthError),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`post_user_info`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PostUserInfoError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`register_o_auth_client`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RegisterOAuthClientError {
Status400(models::OAuthError),
Status429(),
UnknownValue(serde_json::Value),
}
/// The set of public keys RPs use to verify Spatio-issued id_tokens. Cached for 5 minutes at the edge. Always includes the currently-active signing key plus any retired keys that may still be in circulation (id_token TTL is 1 hour + slack).
pub async fn get_jwks(configuration: &configuration::Configuration, ) -> Result<models::Jwks, Error<GetJwksError>> {
let uri_str = format!("{}/.well-known/jwks.json", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::Jwks`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::Jwks`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetJwksError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Returns the canonical metadata for the Spatio OAuth 2.1 + OpenID Connect server. Third-party RPs use this to auto-discover endpoint URLs, supported scopes, and signing algorithms. Identical payload to `/.well-known/openid-configuration` — either path is acceptable; OIDC clients prefer the openid-configuration alias.
pub async fn get_o_auth_discovery(configuration: &configuration::Configuration, ) -> Result<models::DiscoveryDocument, Error<GetOAuthDiscoveryError>> {
let uri_str = format!("{}/.well-known/oauth-authorization-server", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::DiscoveryDocument`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::DiscoveryDocument`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetOAuthDiscoveryError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Alias of `/.well-known/oauth-authorization-server`. Provided so OIDC client libraries (NextAuth, Auth.js, oidc-client-ts, passport-openidconnect) auto-detect Spatio as an OIDC provider via their `wellKnown` / `discoveryUrl` config field.
pub async fn get_open_id_configuration(configuration: &configuration::Configuration, ) -> Result<models::DiscoveryDocument, Error<GetOpenIdConfigurationError>> {
let uri_str = format!("{}/.well-known/openid-configuration", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::DiscoveryDocument`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::DiscoveryDocument`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetOpenIdConfigurationError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Returns user claims gated by the scopes on the presenting access token. `sub` is always returned; `email`, `name`, etc. require their respective scopes.
pub async fn get_user_info(configuration: &configuration::Configuration, ) -> Result<models::UserInfoResponse, Error<GetUserInfoError>> {
let uri_str = format!("{}/oauth2/userinfo", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::UserInfoResponse`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::UserInfoResponse`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetUserInfoError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Browser-redirect endpoint. Validates the client + redirect_uri, packs the request into a signed JWT, and 302s the user's browser to the consent UI. The consent UI then POSTs to `/oauth2/authorize/confirm` with the user's decision. OIDC additions: `scope=openid+profile+email`, `nonce`, `prompt` (none|login|consent), `max_age`.
pub async fn oauth_authorize(configuration: &configuration::Configuration, client_id: &str, redirect_uri: &str, response_type: &str, code_challenge: &str, code_challenge_method: &str, scope: Option<&str>, state: Option<&str>, nonce: Option<&str>, prompt: Option<&str>, max_age: Option<i32>) -> Result<(), Error<OauthAuthorizeError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_query_client_id = client_id;
let p_query_redirect_uri = redirect_uri;
let p_query_response_type = response_type;
let p_query_code_challenge = code_challenge;
let p_query_code_challenge_method = code_challenge_method;
let p_query_scope = scope;
let p_query_state = state;
let p_query_nonce = nonce;
let p_query_prompt = prompt;
let p_query_max_age = max_age;
let uri_str = format!("{}/oauth2/authorize", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
req_builder = req_builder.query(&[("client_id", &p_query_client_id.to_string())]);
req_builder = req_builder.query(&[("redirect_uri", &p_query_redirect_uri.to_string())]);
req_builder = req_builder.query(&[("response_type", &p_query_response_type.to_string())]);
if let Some(ref param_value) = p_query_scope {
req_builder = req_builder.query(&[("scope", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_state {
req_builder = req_builder.query(&[("state", ¶m_value.to_string())]);
}
req_builder = req_builder.query(&[("code_challenge", &p_query_code_challenge.to_string())]);
req_builder = req_builder.query(&[("code_challenge_method", &p_query_code_challenge_method.to_string())]);
if let Some(ref param_value) = p_query_nonce {
req_builder = req_builder.query(&[("nonce", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_prompt {
req_builder = req_builder.query(&[("prompt", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_max_age {
req_builder = req_builder.query(&[("max_age", ¶m_value.to_string())]);
}
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
if !status.is_client_error() && !status.is_server_error() {
Ok(())
} else {
let content = resp.text().await?;
let entity: Option<OauthAuthorizeError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
pub async fn oauth_introspect(configuration: &configuration::Configuration, token: &str) -> Result<models::IntrospectionResponse, Error<OauthIntrospectError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_form_token = token;
let uri_str = format!("{}/oauth2/introspect", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
let mut multipart_form_params = std::collections::HashMap::new();
multipart_form_params.insert("token", p_form_token.to_string());
req_builder = req_builder.form(&multipart_form_params);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::IntrospectionResponse`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::IntrospectionResponse`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<OauthIntrospectError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
pub async fn oauth_revoke(configuration: &configuration::Configuration, token: &str) -> Result<(), Error<OauthRevokeError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_form_token = token;
let uri_str = format!("{}/oauth2/revoke", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
let mut multipart_form_params = std::collections::HashMap::new();
multipart_form_params.insert("token", p_form_token.to_string());
req_builder = req_builder.form(&multipart_form_params);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
if !status.is_client_error() && !status.is_server_error() {
Ok(())
} else {
let content = resp.text().await?;
let entity: Option<OauthRevokeError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
pub async fn oauth_token(configuration: &configuration::Configuration, grant_type: &str, code: Option<&str>, code_verifier: Option<&str>, redirect_uri: Option<&str>, refresh_token: Option<&str>, client_id: Option<&str>, client_secret: Option<&str>) -> Result<models::TokenResponse, Error<OauthTokenError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_form_grant_type = grant_type;
let p_form_code = code;
let p_form_code_verifier = code_verifier;
let p_form_redirect_uri = redirect_uri;
let p_form_refresh_token = refresh_token;
let p_form_client_id = client_id;
let p_form_client_secret = client_secret;
let uri_str = format!("{}/oauth2/token", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
let mut multipart_form_params = std::collections::HashMap::new();
multipart_form_params.insert("grant_type", p_form_grant_type.to_string());
if let Some(param_value) = p_form_code {
multipart_form_params.insert("code", param_value.to_string());
}
if let Some(param_value) = p_form_code_verifier {
multipart_form_params.insert("code_verifier", param_value.to_string());
}
if let Some(param_value) = p_form_redirect_uri {
multipart_form_params.insert("redirect_uri", param_value.to_string());
}
if let Some(param_value) = p_form_refresh_token {
multipart_form_params.insert("refresh_token", param_value.to_string());
}
if let Some(param_value) = p_form_client_id {
multipart_form_params.insert("client_id", param_value.to_string());
}
if let Some(param_value) = p_form_client_secret {
multipart_form_params.insert("client_secret", param_value.to_string());
}
req_builder = req_builder.form(&multipart_form_params);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::TokenResponse`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::TokenResponse`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<OauthTokenError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
pub async fn post_user_info(configuration: &configuration::Configuration, ) -> Result<models::UserInfoResponse, Error<PostUserInfoError>> {
let uri_str = format!("{}/oauth2/userinfo", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::UserInfoResponse`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::UserInfoResponse`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<PostUserInfoError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Returns a fresh `client_id` (and, for confidential clients, `client_secret`) plus a one-time `registration_access_token` the client can use later to update its registration. Public clients (mobile, SPA) MUST use `token_endpoint_auth_method: none` and PKCE. Rate-limited to 10 registrations per hour per source IP.
pub async fn register_o_auth_client(configuration: &configuration::Configuration, client_registration_request: models::ClientRegistrationRequest) -> Result<models::ClientRegistrationResponse, Error<RegisterOAuthClientError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_body_client_registration_request = client_registration_request;
let uri_str = format!("{}/oauth2/register", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
req_builder = req_builder.json(&p_body_client_registration_request);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::ClientRegistrationResponse`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::ClientRegistrationResponse`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<RegisterOAuthClientError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}