use chrono::{DateTime, Utc};
use reqwest::StatusCode;
use serde::{Deserialize, Serialize};
use std::time::Instant;
use thiserror::Error as ThisError;
use utoipa::ToSchema;
use crate::source_error;
#[derive(Serialize, Deserialize, Debug, ToSchema)]
pub struct LicenseCheckRequest {
pub account_id: String,
pub license_key: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
#[allow(dead_code)]
pub enum DisplaySchedule {
Once,
Session,
Every { seconds: u64 },
Always,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)]
pub struct LicenseInformation {
pub current: DateTime<Utc>,
pub valid_until: Option<DateTime<Utc>>,
pub is_trial: bool,
pub description_html: String,
pub extension_url: Option<String>,
pub remind_starting_at: Option<DateTime<Utc>>,
pub remind_schedule: DisplaySchedule,
}
#[derive(ThisError, Debug, Clone, PartialEq)]
pub enum LicenseRetrievalError {
#[error("failed to serialize request to JSON due to: {error}")]
SerializeRequestToJsonFailed { error: String },
#[error("failed to send request due to: {error}")]
SendRequestFailed { error: String },
#[error("license information returned could not be deserialized due to: {error}")]
JsonDeserializeFailed { error: String },
#[error("server indicated the service is currently unavailable (503)")]
ServiceUnavailable,
#[error("server responded with an unexpected HTTP status code ({0})")]
UnexpectedResponseStatusCode(StatusCode),
}
#[derive(Debug, Clone, PartialEq)]
pub enum LicenseRetrievalResult {
Exists(LicenseInformation),
DoesNotExistInvalid,
Unable(LicenseRetrievalError),
}
pub async fn retrieve_license(
cloud_api_endpoint: &str,
license_key: &str,
) -> (Instant, LicenseRetrievalResult) {
let endpoint_license_retrieval = format!("{cloud_api_endpoint}/license");
let client = reqwest::Client::new();
let result = client
.get(&endpoint_license_retrieval)
.header(
reqwest::header::AUTHORIZATION,
format!("Bearer {}", license_key),
)
.send()
.await;
let now = Instant::now();
let result = match result {
Ok(response) => {
let status_code = response.status();
if status_code == StatusCode::OK {
match response.json::<LicenseInformation>().await {
Ok(license_information) => LicenseRetrievalResult::Exists(license_information),
Err(error) => LicenseRetrievalResult::Unable(
LicenseRetrievalError::JsonDeserializeFailed {
error: error.to_string(),
},
),
}
} else if status_code == StatusCode::FORBIDDEN {
LicenseRetrievalResult::DoesNotExistInvalid
} else if status_code == StatusCode::SERVICE_UNAVAILABLE {
LicenseRetrievalResult::Unable(LicenseRetrievalError::ServiceUnavailable)
} else {
LicenseRetrievalResult::Unable(LicenseRetrievalError::UnexpectedResponseStatusCode(
status_code,
))
}
}
Err(e) => {
let source_err = source_error(&e);
let error = format!("{e}, source: {source_err}");
LicenseRetrievalResult::Unable(LicenseRetrievalError::SendRequestFailed { error })
}
};
(now, result)
}