babelforce-manager-sdk 0.42.2

Rust SDK for the babelforce manager APIs โ€” auth, user & agent management, call reporting, metrics, and task automations.
Documentation
/*
 * babelforce Auth API
 *
 * OAuth 2.0 authorization endpoints: token issuance (`/oauth/token`), the authorization-code consent endpoint (`/oauth/authorize`) and token revocation (`/oauth/revoke`). Supports the Authorization Code grant with PKCE (RFC 7636) so public clients (SPA / CLI / mobile) can authenticate without exposing the user's password or holding a client secret.
 *
 * The version of the OpenAPI document: 0.0.0-dev
 * Contact: support@babelforce.com
 * Generated by: https://openapi-generator.tech
 */

use super::{configuration, ContentType, Error};
use crate::gen::auth::{apis::ResponseContent, models};
use reqwest;
use serde::{de::Error as _, Deserialize, Serialize};

/// struct for typed errors of method [`authorize`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum AuthorizeError {
    Status400(models::OAuth2Error),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`revoke`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RevokeError {
    Status400(models::OAuth2Error),
    UnknownValue(serde_json::Value),
}

/// struct for typed errors of method [`token`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum TokenError {
    Status400(models::OAuth2Error),
    UnknownValue(serde_json::Value),
}

/// First step of the Authorization Code flow (RFC 6749 ยง4.1.1). The user authenticates with babelforce and approves the client; babelforce then redirects back to `redirect_uri` with a short-lived `code`. The `redirect_uri` MUST exactly match the value registered for the client. For PKCE (RFC 7636) supply `code_challenge` (and optionally `code_challenge_method`, default `S256`); public clients MUST use PKCE.
pub async fn authorize(
    configuration: &configuration::Configuration,
    response_type: &str,
    client_id: &str,
    redirect_uri: &str,
    scope: &str,
    state: Option<&str>,
    code_challenge: Option<&str>,
    code_challenge_method: Option<&str>,
) -> Result<String, Error<AuthorizeError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_query_response_type = response_type;
    let p_query_client_id = client_id;
    let p_query_redirect_uri = redirect_uri;
    let p_query_scope = scope;
    let p_query_state = state;
    let p_query_code_challenge = code_challenge;
    let p_query_code_challenge_method = code_challenge_method;

    let uri_str = format!("{}/oauth/authorize", configuration.base_path);
    let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);

    req_builder = req_builder.query(&[("response_type", &p_query_response_type.to_string())]);
    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(&[("scope", &p_query_scope.to_string())]);
    if let Some(ref param_value) = p_query_state {
        req_builder = req_builder.query(&[("state", &param_value.to_string())]);
    }
    if let Some(ref param_value) = p_query_code_challenge {
        req_builder = req_builder.query(&[("code_challenge", &param_value.to_string())]);
    }
    if let Some(ref param_value) = p_query_code_challenge_method {
        req_builder = req_builder.query(&[("code_challenge_method", &param_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();
    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 `String`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `String`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<AuthorizeError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Revoke an access token or a refresh token. Revoking a refresh token also invalidates the access token issued with it. Per RFC 7009 ยง2.2 the endpoint responds `200` even for unknown or already-revoked tokens.
pub async fn revoke(
    configuration: &configuration::Configuration,
    token: &str,
    token_type_hint: Option<&str>,
    client_id: Option<&str>,
    client_secret: Option<&str>,
) -> Result<serde_json::Value, Error<RevokeError>> {
    // add a prefix to parameters to efficiently prevent name collisions
    let p_form_token = token;
    let p_form_token_type_hint = token_type_hint;
    let p_form_client_id = client_id;
    let p_form_client_secret = client_secret;

    let uri_str = format!("{}/oauth/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());
    if let Some(param_value) = p_form_token_type_hint {
        multipart_form_params.insert("token_type_hint", 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 `serde_json::Value`"))),
            ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `serde_json::Value`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<RevokeError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}

/// Issues access tokens (and refresh tokens). Supported `grant_type` values: `authorization_code` (exchange a code from `/oauth/authorize`, with PKCE `code_verifier` for public clients), `refresh_token` (refresh tokens are rotated on every use โ€” present the most recently issued one), `password` (legacy first-party only) and `client_credentials`. Which other fields are required depends on the grant; see each field's description.
pub async fn token(
    configuration: &configuration::Configuration,
    grant_type: &str,
    code: Option<&str>,
    redirect_uri: Option<&str>,
    code_verifier: Option<&str>,
    refresh_token: Option<&str>,
    username: Option<&str>,
    password: Option<&str>,
    client_id: Option<&str>,
    client_secret: Option<&str>,
    scope: Option<&str>,
    access_type: Option<&str>,
) -> Result<models::OAuthTokenResponse, Error<TokenError>> {
    // 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_redirect_uri = redirect_uri;
    let p_form_code_verifier = code_verifier;
    let p_form_refresh_token = refresh_token;
    let p_form_username = username;
    let p_form_password = password;
    let p_form_client_id = client_id;
    let p_form_client_secret = client_secret;
    let p_form_scope = scope;
    let p_form_access_type = access_type;

    let uri_str = format!("{}/oauth/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_redirect_uri {
        multipart_form_params.insert("redirect_uri", 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_refresh_token {
        multipart_form_params.insert("refresh_token", param_value.to_string());
    }
    if let Some(param_value) = p_form_username {
        multipart_form_params.insert("username", param_value.to_string());
    }
    if let Some(param_value) = p_form_password {
        multipart_form_params.insert("password", 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());
    }
    if let Some(param_value) = p_form_scope {
        multipart_form_params.insert("scope", param_value.to_string());
    }
    if let Some(param_value) = p_form_access_type {
        multipart_form_params.insert("access_type", 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::OAuthTokenResponse`"))),
            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::OAuthTokenResponse`")))),
        }
    } else {
        let content = resp.text().await?;
        let entity: Option<TokenError> = serde_json::from_str(&content).ok();
        Err(Error::ResponseError(ResponseContent {
            status,
            content,
            entity,
        }))
    }
}