darkwing/server/dtos/request/
health_dto.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//! Data transfer objects for health check functionality.
//!
//! This module contains the structures and implementations needed to represent
//! the health status of various system components, including databases and
//! services. It supports both detailed and minimal health check responses.

use serde::{Deserialize, Serialize};

/// Key used for S3 health check operations.
/// This key is used to verify S3 bucket accessibility and operations.
pub const HEALTHCHECK_S3_KEY: &str = "darkwing-healthcheck-DONOTDELETE";

/// Response structure for health check endpoints.
///
/// This structure can represent both detailed and minimal health check
/// responses. In minimal mode, only the status and build timestamp are
/// included. In detailed mode, additional information about databases,
/// services, and git metadata is included.
#[derive(Serialize, Deserialize, Default, Debug)]
pub struct HealthResponse {
  /// Overall health status of the system.
  /// `true` indicates all checked components are healthy.
  pub status: bool,

  /// Status of database connections.
  /// Omitted in the serialized response if None.
  #[serde(skip_serializing_if = "Option::is_none")]
  pub databases: Option<DatabaseStatus>,

  /// Status of external services.
  /// Omitted in the serialized response if None.
  #[serde(skip_serializing_if = "Option::is_none")]
  pub services: Option<ServiceStatuses>,

  /// Timestamp when the application was built.
  pub build_timestamp: String,

  /// Git commit SHA of the deployed version.
  /// Omitted in the serialized response if None.
  #[serde(skip_serializing_if = "Option::is_none")]
  pub git_sha: Option<String>,

  /// Timestamp of the git commit.
  /// Omitted in the serialized response if None.
  #[serde(skip_serializing_if = "Option::is_none")]
  pub git_commit_timestamp: Option<String>,

  /// Git branch name of the deployed version.
  /// Omitted in the serialized response if None.
  #[serde(skip_serializing_if = "Option::is_none")]
  pub git_branch: Option<String>,
}

impl HealthResponse {
  /// Creates a new HealthResponse instance.
  ///
  /// # Arguments
  ///
  /// * `databases` - Optional status of database connections
  /// * `services` - Optional status of external services
  ///
  /// # Returns
  ///
  /// Returns a HealthResponse with computed status and appropriate metadata
  /// based on whether this is a detailed or minimal health check.
  pub fn new(
    databases: Option<DatabaseStatus>,
    services: Option<ServiceStatuses>,
  ) -> Self {
    let build_timestamp = env!("VERGEN_BUILD_TIMESTAMP").to_string();

    let small_response = databases.is_none() && services.is_none();

    let (git_sha, git_commit_timestamp, git_branch) = match small_response {
      true => (None, None, None),
      false => (
        Some(env!("VERGEN_GIT_SHA").to_string()),
        Some(env!("VERGEN_GIT_COMMIT_TIMESTAMP").to_string()),
        Some(env!("VERGEN_GIT_BRANCH").to_string()),
      ),
    };

    let status = match (&databases, &services) {
      // Both present - check both are ok
      (Some(db), Some(svc)) => db.is_ok() && svc.is_ok(),
      // Both not present - "small" health request, return true by default
      (None, None) => true,
      // One present, one not - unhealthy state
      _ => false,
    };

    Self {
      status,
      databases,
      services,
      build_timestamp,
      git_sha,
      git_commit_timestamp,
      git_branch,
    }
  }
}

/// Status information for database connections.
#[derive(Serialize, Deserialize, Default, Debug)]
pub struct DatabaseStatus {
  /// Status of the main MySQL database connection.
  pub main_mysql: bool,
  /// Status of the Redis cache connection.
  pub redis_cache: bool,
}

impl DatabaseStatus {
  /// Checks if all database connections are healthy.
  ///
  /// # Returns
  ///
  /// Returns `true` if both MySQL and Redis connections are healthy.
  pub fn is_ok(&self) -> bool {
    self.main_mysql && self.redis_cache
  }
}

/// Status information for an individual service.
#[derive(Serialize, Deserialize, Default, Debug)]
pub struct ServiceStatus {
  /// Indicates if the service is healthy.
  pub status: bool,
  /// Optional message providing additional status details.
  pub message: Option<String>,
}

/// Collection of service status information.
#[derive(Serialize, Deserialize, Default, Debug)]
pub struct ServiceStatuses {
  /// Status of the primary S3 storage service.
  pub primary_s3: ServiceStatus,
  /// Status of the backup S3 storage service.
  pub backup_s3: ServiceStatus,
}

impl ServiceStatuses {
  /// Checks if critical services are healthy.
  ///
  /// Currently only checks primary S3 status as backup S3 is not
  /// critical for application functionality.
  ///
  /// # Returns
  ///
  /// Returns `true` if critical services are healthy.
  pub fn is_ok(&self) -> bool {
    self.primary_s3.status
    // && self.backup_s3.status // This is not checked as of now, because backup
    // S3 is not necessary for the app to function
  }
}