Files

307 lines
10 KiB
Rust

use common_utils::custom_serde;
use masking::StrongSecret;
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;
use utoipa::ToSchema;
/// The request body for creating an API Key.
#[derive(Debug, Deserialize, ToSchema, Serialize)]
#[serde(deny_unknown_fields)]
pub struct CreateApiKeyRequest {
/// A unique name for the API Key to help you identify it.
#[schema(max_length = 64, example = "Sandbox integration key")]
pub name: String,
/// A description to provide more context about the API Key.
#[schema(
max_length = 256,
example = "Key used by our developers to integrate with the sandbox environment"
)]
pub description: Option<String>,
/// An expiration date for the API Key. Although we allow keys to never expire, we recommend
/// rotating your keys once every 6 months.
#[schema(example = "2022-09-10T10:11:12Z")]
pub expiration: ApiKeyExpiration,
}
/// The response body for creating an API Key.
#[derive(Debug, Serialize, ToSchema)]
pub struct CreateApiKeyResponse {
/// The identifier for the API Key.
#[schema(max_length = 64, example = "5hEEqkgJUyuxgSKGArHA4mWSnX", value_type = String)]
pub key_id: common_utils::id_type::ApiKeyId,
/// The identifier for the Merchant Account.
#[schema(max_length = 64, example = "y3oqhf46pyzuxjbcn2giaqnb44", value_type = String)]
pub merchant_id: common_utils::id_type::MerchantId,
/// The unique name for the API Key to help you identify it.
#[schema(max_length = 64, example = "Sandbox integration key")]
pub name: String,
/// The description to provide more context about the API Key.
#[schema(
max_length = 256,
example = "Key used by our developers to integrate with the sandbox environment"
)]
pub description: Option<String>,
/// The plaintext API Key used for server-side API access. Ensure you store the API Key
/// securely as you will not be able to see it again.
#[schema(value_type = String, max_length = 128)]
pub api_key: StrongSecret<String>,
/// The time at which the API Key was created.
#[schema(example = "2022-09-10T10:11:12Z")]
#[serde(with = "common_utils::custom_serde::iso8601")]
pub created: PrimitiveDateTime,
/// The expiration date for the API Key.
#[schema(example = "2022-09-10T10:11:12Z")]
pub expiration: ApiKeyExpiration,
/*
/// The date and time indicating when the API Key was last used.
#[schema(example = "2022-09-10T10:11:12Z")]
#[serde(with = "common_utils::custom_serde::iso8601::option")]
pub last_used: Option<PrimitiveDateTime>,
*/
}
/// The response body for retrieving an API Key.
#[derive(Debug, Serialize, ToSchema)]
pub struct RetrieveApiKeyResponse {
/// The identifier for the API Key.
#[schema(max_length = 64, example = "5hEEqkgJUyuxgSKGArHA4mWSnX", value_type = String)]
pub key_id: common_utils::id_type::ApiKeyId,
/// The identifier for the Merchant Account.
#[schema(max_length = 64, example = "y3oqhf46pyzuxjbcn2giaqnb44", value_type = String)]
pub merchant_id: common_utils::id_type::MerchantId,
/// The unique name for the API Key to help you identify it.
#[schema(max_length = 64, example = "Sandbox integration key")]
pub name: String,
/// The description to provide more context about the API Key.
#[schema(
max_length = 256,
example = "Key used by our developers to integrate with the sandbox environment"
)]
pub description: Option<String>,
/// The first few characters of the plaintext API Key to help you identify it.
#[schema(value_type = String, max_length = 64)]
pub prefix: StrongSecret<String>,
/// The time at which the API Key was created.
#[schema(example = "2022-09-10T10:11:12Z")]
#[serde(with = "common_utils::custom_serde::iso8601")]
pub created: PrimitiveDateTime,
/// The expiration date for the API Key.
#[schema(example = "2022-09-10T10:11:12Z")]
pub expiration: ApiKeyExpiration,
/*
/// The date and time indicating when the API Key was last used.
#[schema(example = "2022-09-10T10:11:12Z")]
#[serde(with = "common_utils::custom_serde::iso8601::option")]
pub last_used: Option<PrimitiveDateTime>,
*/
}
/// The request body for updating an API Key.
#[derive(Debug, Deserialize, ToSchema, Serialize)]
#[serde(deny_unknown_fields)]
pub struct UpdateApiKeyRequest {
/// A unique name for the API Key to help you identify it.
#[schema(max_length = 64, example = "Sandbox integration key")]
pub name: Option<String>,
/// A description to provide more context about the API Key.
#[schema(
max_length = 256,
example = "Key used by our developers to integrate with the sandbox environment"
)]
pub description: Option<String>,
/// An expiration date for the API Key. Although we allow keys to never expire, we recommend
/// rotating your keys once every 6 months.
#[schema(example = "2022-09-10T10:11:12Z")]
pub expiration: Option<ApiKeyExpiration>,
#[serde(skip_deserializing)]
#[schema(value_type = String)]
pub key_id: common_utils::id_type::ApiKeyId,
#[serde(skip_deserializing)]
#[schema(value_type = String)]
pub merchant_id: common_utils::id_type::MerchantId,
}
/// The response body for revoking an API Key.
#[derive(Debug, Serialize, ToSchema)]
pub struct RevokeApiKeyResponse {
/// The identifier for the Merchant Account.
#[schema(max_length = 64, example = "y3oqhf46pyzuxjbcn2giaqnb44", value_type = String)]
pub merchant_id: common_utils::id_type::MerchantId,
/// The identifier for the API Key.
#[schema(max_length = 64, example = "5hEEqkgJUyuxgSKGArHA4mWSnX", value_type = String)]
pub key_id: common_utils::id_type::ApiKeyId,
/// Indicates whether the API key was revoked or not.
#[schema(example = "true")]
pub revoked: bool,
}
/// The constraints that are applicable when listing API Keys associated with a merchant account.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ListApiKeyConstraints {
/// The maximum number of API Keys to include in the response.
pub limit: Option<i64>,
/// The number of API Keys to skip when retrieving the list of API keys.
pub skip: Option<i64>,
}
/// The expiration date and time for an API Key.
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
pub enum ApiKeyExpiration {
/// The API Key does not expire.
#[serde(with = "never")]
Never,
/// The API Key expires at the specified date and time.
#[serde(with = "custom_serde::iso8601")]
DateTime(PrimitiveDateTime),
}
impl From<ApiKeyExpiration> for Option<PrimitiveDateTime> {
fn from(expiration: ApiKeyExpiration) -> Self {
match expiration {
ApiKeyExpiration::Never => None,
ApiKeyExpiration::DateTime(date_time) => Some(date_time),
}
}
}
impl From<Option<PrimitiveDateTime>> for ApiKeyExpiration {
fn from(date_time: Option<PrimitiveDateTime>) -> Self {
date_time.map_or(Self::Never, Self::DateTime)
}
}
// This implementation is required as otherwise, `serde` would serialize and deserialize
// `ApiKeyExpiration::Never` as `null`, which is not preferable.
// Reference: https://github.com/serde-rs/serde/issues/1560#issuecomment-506915291
mod never {
const NEVER: &str = "never";
pub fn serialize<S>(serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(NEVER)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<(), D::Error>
where
D: serde::Deserializer<'de>,
{
struct NeverVisitor;
impl serde::de::Visitor<'_> for NeverVisitor {
type Value = ();
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, r#""{NEVER}""#)
}
fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
if value == NEVER {
Ok(())
} else {
Err(E::invalid_value(serde::de::Unexpected::Str(value), &self))
}
}
}
deserializer.deserialize_str(NeverVisitor)
}
}
impl<'a> ToSchema<'a> for ApiKeyExpiration {
fn schema() -> (
&'a str,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
) {
use utoipa::openapi::{KnownFormat, ObjectBuilder, OneOfBuilder, SchemaFormat, SchemaType};
(
"ApiKeyExpiration",
OneOfBuilder::new()
.item(
ObjectBuilder::new()
.schema_type(SchemaType::String)
.enum_values(Some(["never"])),
)
.item(
ObjectBuilder::new()
.schema_type(SchemaType::String)
.format(Some(SchemaFormat::KnownFormat(KnownFormat::DateTime))),
)
.into(),
)
}
}
#[cfg(test)]
mod api_key_expiration_tests {
#![allow(clippy::unwrap_used)]
use super::*;
#[test]
fn test_serialization() {
assert_eq!(
serde_json::to_string(&ApiKeyExpiration::Never).unwrap(),
r#""never""#
);
let date = time::Date::from_calendar_date(2022, time::Month::September, 10).unwrap();
let time = time::Time::from_hms(11, 12, 13).unwrap();
assert_eq!(
serde_json::to_string(&ApiKeyExpiration::DateTime(PrimitiveDateTime::new(
date, time
)))
.unwrap(),
r#""2022-09-10T11:12:13.000Z""#
);
}
#[test]
fn test_deserialization() {
assert_eq!(
serde_json::from_str::<ApiKeyExpiration>(r#""never""#).unwrap(),
ApiKeyExpiration::Never
);
let date = time::Date::from_calendar_date(2022, time::Month::September, 10).unwrap();
let time = time::Time::from_hms(11, 12, 13).unwrap();
assert_eq!(
serde_json::from_str::<ApiKeyExpiration>(r#""2022-09-10T11:12:13.000Z""#).unwrap(),
ApiKeyExpiration::DateTime(PrimitiveDateTime::new(date, time))
);
}
#[test]
fn test_null() {
let result = serde_json::from_str::<ApiKeyExpiration>("null");
assert!(result.is_err());
let result = serde_json::from_str::<Option<ApiKeyExpiration>>("null").unwrap();
assert_eq!(result, None);
}
}