use crate::io::api::{self, require_non_empty_secret, ApiResult, Configuration, Endpoint, Fallback, Param, Params, RemoteResource};
use crate::param;
use crate::util::constants::app::DEFAULT_RAID_DOMAIN;
use crate::util::constants::env::{RAID_API_TOKEN, RAID_SERVER_HOST, RAID_TOKEN_VARIABLE_NAMES};
use acorn_core::options::{ApiExtension, ApiOptions};
use acorn_schema::pid::raid::Metadata;
use acorn_schema::validation::Validate;
use bon::Builder;
use color_eyre::eyre::eyre;
use secrecy::ExposeSecret;
use serde::{Deserialize, Serialize};
use veil::Redact;
pub type Options = ApiOptions<Extension, Param>;
#[derive(Builder, Clone, Debug, Serialize, Deserialize)]
#[builder(start_fn = with_token, on(String, into), on(api::Secret, into))]
pub struct ErrorResponse {
#[serde(rename = "type")]
pub error_type: String,
pub title: String,
pub status: u16,
pub detail: String,
pub instance: String,
pub failures: Option<Vec<ErrorResponseFailure>>,
}
#[derive(Builder, Clone, Debug, Serialize, Deserialize)]
#[builder(start_fn = with_token, on(String, into), on(api::Secret, into))]
pub struct ErrorResponseFailure {
#[serde(rename = "fieldId")]
pub field_id: String,
#[serde(rename = "errorType")]
pub error_type: String,
pub message: String,
}
#[derive(Builder, Clone, Debug, Default)]
#[builder(start_fn = init)]
pub struct Extension {
pub metadata: Option<Metadata>,
pub prefix: Option<String>,
pub suffix: Option<String>,
}
#[derive(Builder, Deserialize, Serialize, Validate, Redact)]
#[builder(start_fn = with_token, on(String, into), on(api::Secret, into))]
pub struct ServicePoint {
pub identifier: String,
#[validate(url)]
pub url: String,
#[serde(skip, default)]
#[redact]
pub token: Option<api::Secret>,
}
#[derive(Debug, Deserialize, Serialize, Validate)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct ServicePointResponse {
#[serde(alias = "id")]
pub identifier: u64,
pub name: String,
#[validate(ror)]
#[serde(alias = "identifierOwner")]
pub owner: String,
#[serde(alias = "repositoryId")]
pub repository: Option<String>,
pub prefix: String,
#[serde(alias = "groupId")]
pub group: String,
#[serde(alias = "techEmail")]
pub tech_email: String,
#[serde(alias = "adminEmail")]
pub admin_email: String,
pub enabled: bool,
#[serde(alias = "appWritesEnabled")]
pub app_writes_enabled: bool,
}
impl ApiExtension for Extension {
fn default_domain() -> String {
String::from(DEFAULT_RAID_DOMAIN)
}
fn env_token_var() -> &'static str {
RAID_API_TOKEN
}
fn env_domain_var() -> &'static str {
RAID_SERVER_HOST
}
}
pub async fn create_record(options: &Options) -> ApiResult<Metadata> {
let template = "raid::api";
let action = "record::create";
let path = format!("{template}::{action}");
match require_non_empty_secret(&options.token, &path, &RAID_TOKEN_VARIABLE_NAMES) {
| Ok(token) => match Endpoint::from_template(template).map(|e| e.with_domain(&options.domain)) {
| Ok(endpoint) => match &options.extension.metadata {
| Some(value) => match serde_json::to_string(value) {
| Ok(body) => {
let params = Params::new()
.with_auth(ExposeSecret::expose_secret(&token), None)
.with(param!(Body, &body))
.with_custom(options.params())
.build();
let response = endpoint.invoke(action, Some(params)).await;
endpoint.handle_or::<Metadata, Fallback<ErrorResponse>>(response)
}
| Err(why) => Err(eyre!("Failed to serialize RAiD metadata to JSON — {why:#?}")),
},
| None => Err(eyre!("RAiD metadata is required for creating a record")),
},
| Err(why) => Err(why),
},
| Err(why) => Err(why),
}
}
pub async fn record(options: &Options) -> ApiResult<Vec<Metadata>> {
let template = "raid::api";
let action = "record";
let path = format!("{template}::{action}");
match require_non_empty_secret(&options.token, &path, &RAID_TOKEN_VARIABLE_NAMES) {
| Ok(token) => match Endpoint::from_template(template).map(|e| e.with_domain(&options.domain)) {
| Ok(endpoint) => match &options.identifier {
| Some(value) if !value.is_empty() => {
let params = Params::new()
.with_auth(ExposeSecret::expose_secret(&token), None)
.with_template("identifier", Some(value))
.with_custom(options.params())
.build();
let response = endpoint.invoke(action, Some(params)).await;
endpoint.handle::<Metadata>(response).map(|r| vec![r])
}
| Some(_) | None => {
let params = Params::new()
.with_auth(ExposeSecret::expose_secret(&token), None)
.with_custom(options.params())
.build();
let response = endpoint.invoke(action, Some(params)).await;
endpoint.handle::<Vec<Metadata>>(response)
}
},
| Err(why) => Err(why),
},
| Err(why) => Err(why),
}
}
pub async fn service_point(options: &Options) -> ApiResult<Vec<ServicePointResponse>> {
let template = "raid::api";
let action = "service-point";
let path = format!("{template}::{action}");
match require_non_empty_secret(&options.token, &path, &RAID_TOKEN_VARIABLE_NAMES) {
| Ok(token) => match Endpoint::from_template(template).map(|e| e.with_domain(&options.domain)) {
| Ok(endpoint) => match &options.identifier {
| Some(value) if !value.is_empty() => {
let params = Params::new()
.with_auth(ExposeSecret::expose_secret(&token), None)
.with_template("identifier", Some(value))
.with_custom(options.params())
.build();
let response = endpoint.invoke(action, Some(params)).await;
endpoint.handle::<ServicePointResponse>(response).map(|r| vec![r])
}
| Some(_) | None => {
let params = Params::new()
.with_auth(ExposeSecret::expose_secret(&token), None)
.with_custom(options.params())
.build();
let response = endpoint.invoke(action, Some(params)).await;
endpoint.handle::<Vec<ServicePointResponse>>(response)
}
},
| Err(why) => Err(why),
},
| Err(why) => Err(why),
}
}