auth_framework/api/
versioning.rs

1//! API Versioning Support
2//!
3//! Provides version negotiation and backwards compatibility
4
5use axum::{
6    extract::Request,
7    http::{HeaderMap, StatusCode},
8    middleware::Next,
9    response::Response,
10};
11
12/// API version information
13#[derive(Debug, Clone, PartialEq, Default)]
14pub enum ApiVersion {
15    #[default]
16    V1,
17    V2,
18}
19
20impl ApiVersion {
21    /// Parse version from Accept header or path
22    pub fn from_header(headers: &HeaderMap) -> Option<Self> {
23        if let Some(accept) = headers.get("accept")
24            && let Ok(accept_str) = accept.to_str()
25        {
26            if accept_str.contains("application/vnd.authframework.v2+json") {
27                return Some(ApiVersion::V2);
28            } else if accept_str.contains("application/vnd.authframework.v1+json") {
29                return Some(ApiVersion::V1);
30            }
31        }
32        None
33    }
34
35    /// Convert to header value
36    pub fn to_header_value(&self) -> &'static str {
37        match self {
38            ApiVersion::V1 => "application/vnd.authframework.v1+json",
39            ApiVersion::V2 => "application/vnd.authframework.v2+json",
40        }
41    }
42}
43
44/// Middleware to handle API versioning
45pub async fn version_middleware(request: Request, next: Next) -> Result<Response, StatusCode> {
46    let headers = request.headers();
47    let version = ApiVersion::from_header(headers).unwrap_or_default();
48
49    // Store version in request extensions for handlers to use
50    let mut request = request;
51    request.extensions_mut().insert(version);
52
53    let mut response = next.run(request).await;
54
55    // Add version info to response headers
56    let default_version = ApiVersion::default();
57    let version = response
58        .extensions()
59        .get::<ApiVersion>()
60        .unwrap_or(&default_version)
61        .to_header_value();
62
63    response
64        .headers_mut()
65        .insert("api-version", version.parse().unwrap());
66
67    Ok(response)
68}
69
70/// Extract API version from request
71pub fn get_api_version(request: &Request) -> ApiVersion {
72    request
73        .extensions()
74        .get::<ApiVersion>()
75        .cloned()
76        .unwrap_or_default()
77}
78
79