Skip to main content

owlauth_types/
lib.rs

1#![forbid(unsafe_code)]
2
3use std::fmt;
4
5use serde::Serialize;
6use utoipa::{OpenApi, ToSchema};
7
8/// OAuth error codes exposed by `OwlAuth`.
9#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, ToSchema)]
10#[serde(rename_all = "snake_case")]
11#[schema(rename_all = "snake_case")]
12pub enum OAuthErrorCode {
13    /// The request is missing a required parameter or is otherwise malformed.
14    InvalidRequest,
15    /// Client authentication failed.
16    InvalidClient,
17    /// The authorization grant is invalid or expired.
18    InvalidGrant,
19}
20
21impl fmt::Display for OAuthErrorCode {
22    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
23        let value = match self {
24            Self::InvalidRequest => "invalid_request",
25            Self::InvalidClient => "invalid_client",
26            Self::InvalidGrant => "invalid_grant",
27        };
28        formatter.write_str(value)
29    }
30}
31
32/// Response returned by the server health endpoint.
33#[derive(Clone, Debug, Eq, PartialEq, Serialize, ToSchema)]
34pub struct HealthResponse {
35    /// Stable health status. Healthy servers return `ok`.
36    pub status: String,
37}
38
39#[utoipa::path(
40    get,
41    path = "/health",
42    responses(
43        (status = 200, description = "The server is healthy", body = HealthResponse)
44    )
45)]
46#[doc(hidden)]
47#[must_use]
48pub fn get_health() -> HealthResponse {
49    HealthResponse {
50        status: "ok".to_owned(),
51    }
52}
53
54#[derive(OpenApi)]
55#[openapi(
56    info(
57        title = "OwlAuth API",
58        description = "Public HTTP API for the OwlAuth server"
59    ),
60    paths(get_health),
61    components(schemas(HealthResponse, OAuthErrorCode))
62)]
63struct ApiDoc;
64
65/// Generates the current server `OpenAPI` document from Rust protocol definitions.
66#[must_use]
67pub fn openapi() -> utoipa::openapi::OpenApi {
68    ApiDoc::openapi()
69}
70
71#[cfg(test)]
72mod tests {
73    use serde_json::json;
74
75    use super::openapi;
76
77    #[test]
78    fn generated_openapi_matches_wire_values() {
79        let document = serde_json::to_value(openapi()).expect("generated OpenAPI should serialize");
80
81        assert!(document["paths"]["/health"].is_object());
82        assert_eq!(document["info"]["version"], env!("CARGO_PKG_VERSION"));
83        assert_eq!(
84            document["components"]["schemas"]["OAuthErrorCode"]["enum"],
85            json!(["invalid_request", "invalid_client", "invalid_grant"])
86        );
87    }
88}