/*
* Hanzo Cloud API
*
* The Hanzo Cloud API as a customer calls it: every operation under /v1/ except the operator's admin product, relay routes, legacy spellings and capabilities still reached by flag. Tagged by product: the first path segment after /v1/.
*
* The version of the OpenAPI document: v1
*
* 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 [`delete_account_keys`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum DeleteAccountKeysError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_account_appearance`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetAccountAppearanceError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_account_avatar_by_org_by_user_by_digest`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetAccountAvatarByOrgByUserByDigestError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_account_csrf`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetAccountCsrfError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_account_embed`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetAccountEmbedError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_account_keys`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetAccountKeysError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`post_account_appearance`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PostAccountAppearanceError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`post_account_avatar`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PostAccountAvatarError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`post_account_keys`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PostAccountKeysError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`post_account_orgs`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PostAccountOrgsError {
UnknownValue(serde_json::Value),
}
/// Revokes the caller's own API key of the requested class. The class is the same field mint takes — `?type=publishable`, defaulting to secret — so revoking the key that ships in a browser bundle does not sign its holder out of their own API: the other key keeps working. Revoking is how a key is replaced when it does not need replacing; minting the same class again rotates it in one step. IAM drops the credential immediately, but the gateway caches keys for a few minutes, so a request that beat the cache expiry may still be served. For callers written against the older shape, the class is also accepted in a JSON request body, read only when `?type=` is absent.
pub async fn delete_account_keys(configuration: &configuration::Configuration, r#type: Option<&str>) -> Result<models::RevokedKey, Error<DeleteAccountKeysError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_type = r#type;
let uri_str = format!("{}/v1/account/keys", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::DELETE, &uri_str);
if let Some(ref param_value) = p_type {
req_builder = req_builder.query(&[("type", ¶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());
}
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::RevokedKey`"))),
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::RevokedKey`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<DeleteAccountKeysError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Returns the signed-in caller's own appearance preference — text size, density and accent — read from their IAM account so it is the same on every device and every Hanzo surface. An unset preference is an empty object. A transient IAM read failure reports the empty preference rather than a 5xx, so a surface applies its published default and never error-toasts on load — the same fail-soft the key read uses.
pub async fn get_account_appearance(configuration: &configuration::Configuration, ) -> Result<models::Appearance, Error<GetAccountAppearanceError>> {
let uri_str = format!("{}/v1/account/appearance", 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::Appearance`"))),
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::Appearance`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetAccountAppearanceError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Streams a profile photo's raw BYTES. This is the address stored on the user's IAM record and rendered directly by an `<img>`, so it takes no credentials — the 64-hex content digest in the path is the capability, and it can only be produced by someone who already has the image. The Content-Type is derived from the stored bytes and the response carries nosniff, so only a real raster image is ever served and only under its true type. Anything else — a miss, a malformed path, an object that is not an image — is one 404, and a hit caches for a year because the address is the content.
pub async fn get_account_avatar_by_org_by_user_by_digest(configuration: &configuration::Configuration, org: &str, user: &str, digest: &str) -> Result<(), Error<GetAccountAvatarByOrgByUserByDigestError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_org = org;
let p_user = user;
let p_digest = digest;
let uri_str = format!("{}/v1/account/avatar/{org}/{user}/{digest}", configuration.base_path, org=crate::apis::urlencode(p_org), user=crate::apis::urlencode(p_user), digest=crate::apis::urlencode(p_digest));
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();
if !status.is_client_error() && !status.is_server_error() {
Ok(())
} else {
let content = resp.text().await?;
let entity: Option<GetAccountAvatarByOrgByUserByDigestError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// IssueCSRFToken mints the anti-forgery token a browser echoes as X-CSRF-Token on every change it asks for. The token is bound to the caller's validated identity and expires, so one minted for one identity cannot authorize a change as another. It is answered no-store, so it is never cached by a shared proxy. This is the same-origin endpoint the embedded console reads — the Same-Origin Policy is what stops a cross-site page from reading the response and forging a change.
pub async fn get_account_csrf(configuration: &configuration::Configuration, ) -> Result<models::CsrfResp, Error<GetAccountCsrfError>> {
let uri_str = format!("{}/v1/account/csrf", 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::CsrfResp`"))),
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::CsrfResp`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetAccountCsrfError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Reports whether one of this brand's shared embedded apps (cms, erp, help) may be framed by the caller and is actually running, so a console module can choose between the embed and the provision panel. It answers two questions the browser cannot answer for itself. ENTITLEMENT is server-authoritative: each app is a single shared per-BRAND instance, so only a member of the owning brand org — or a SuperAdmin — is given the embed URL; every other caller gets phase \"not-entitled\" and no URL. REACHABILITY is a probe of that origin, which a cross-origin page cannot read for itself. The probed host is always <app>.<this deployment's own brand domain>: no part of it comes from the request, so this can never be steered into probing an arbitrary origin.
pub async fn get_account_embed(configuration: &configuration::Configuration, app: Option<&str>) -> Result<models::EmbedStatusResp, Error<GetAccountEmbedError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_app = app;
let uri_str = format!("{}/v1/account/embed", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref param_value) = p_app {
req_builder = req_builder.query(&[("app", ¶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());
}
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::EmbedStatusResp`"))),
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::EmbedStatusResp`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetAccountEmbedError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Returns the caller's own API keys — every type they hold, read AUTHORITATIVELY from IAM rather than from the session claim, which lags a key minted moments ago. No secret material comes back: a secret key is represented by its prefix, and only a publishable key (public by construction) carries its full value. A transient IAM read failure reports an empty set rather than a 5xx, so the page shows the honest empty state and never a fabricated key.
pub async fn get_account_keys(configuration: &configuration::Configuration, ) -> Result<models::ApiKeyList, Error<GetAccountKeysError>> {
let uri_str = format!("{}/v1/account/keys", 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::ApiKeyList`"))),
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::ApiKeyList`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetAccountKeysError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Stores the caller's appearance preference on their IAM account, preserving every other field of the row. The accent is validated as a real colour token before it is stored; an unset or invalid axis is dropped rather than stored.
pub async fn post_account_appearance(configuration: &configuration::Configuration, appearance: models::Appearance) -> Result<models::Appearance, Error<PostAccountAppearanceError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_appearance = appearance;
let uri_str = format!("{}/v1/account/appearance", 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());
};
req_builder = req_builder.json(&p_appearance);
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::Appearance`"))),
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::Appearance`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<PostAccountAppearanceError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Stores one image as the signed-in user's profile photo and answers the URL it is served from, which is also written to the user's IAM record — so every surface that already renders `avatar` picks it up with no further call. The body is a multipart form with a `file` part. The format is decided by the BYTES, never the filename or the part's Content-Type: png, jpeg, gif and webp are accepted and everything else is refused with 415, so an SVG cannot be stored as a picture and later served as a program. Over 8 MiB is 413; empty is 400. The photo is addressed by the sha256 of its bytes, so setting a new one yields a new URL rather than a stale cache of the old face. The caller is taken from the validated identity ONLY — there is no way to name a different subject — so this always sets your own photo, and a caller with no organization yet is refused.
pub async fn post_account_avatar(configuration: &configuration::Configuration, ) -> Result<(), Error<PostAccountAvatarError>> {
let uri_str = format!("{}/v1/account/avatar", 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();
if !status.is_client_error() && !status.is_server_error() {
Ok(())
} else {
let content = resp.text().await?;
let entity: Option<PostAccountAvatarError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Creates — or rotates — the caller's API key of the requested type and returns it ONCE. A real IAM failure surfaces as 502, never a fabricated key. Rotating is what creating means here: a user holds one key per type, so the endpoint is idempotent by (caller, type) and the superseded credential stops working. Two live secrets for one user would make \"revoke my key\" a lie.
pub async fn post_account_keys(configuration: &configuration::Configuration, key_type_in: models::KeyTypeIn) -> Result<models::MintedKey, Error<PostAccountKeysError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_key_type_in = key_type_in;
let uri_str = format!("{}/v1/account/keys", 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());
};
req_builder = req_builder.json(&p_key_type_in);
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::MintedKey`"))),
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::MintedKey`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<PostAccountKeysError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Onboard creates the caller's organization. Two flows, keyed on whether the caller already has a home org (mirrors app/onboard/route.ts): - FIRST-RUN (no home org): create + MOVE the user in as admin, so their next JWT carries the new owner and the cloud scopes everything to it. This is the path a fresh OAuth sign-up takes, from the sign-up application's org. - ADDITIONAL (owner set): create the org but do NOT move the user — a move changes their IAM owner (stripping a SuperAdmin's status + orphaning their current org). They reach the new org via the OrgSwitcher, which re-scopes X-Org-Id without touching IAM membership. A personal-org request from someone who already has an org is meaningless → 409.
pub async fn post_account_orgs(configuration: &configuration::Configuration, onboard_req: models::OnboardReq) -> Result<models::OnboardResp, Error<PostAccountOrgsError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_onboard_req = onboard_req;
let uri_str = format!("{}/v1/account/orgs", 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());
};
req_builder = req_builder.json(&p_onboard_req);
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::OnboardResp`"))),
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::OnboardResp`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<PostAccountOrgsError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}