healthier 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 54a75f98f4a16fb6e7c63563baedcbd7dee7d05a16848488a7eb02c416f139c3
4
- data.tar.gz: b2bbff5440b799ac64161c151f3633d8e534569bba013b85a3426915ac517716
3
+ metadata.gz: 5f347f1274991b95aa7403f00501e5fc901d48ac74c9d6d6ae44c68a5ee7bdc6
4
+ data.tar.gz: c8f8f1b4fca3b876d3ab3d134f14bc5e46514f45211d7df81a22b8202f3872df
5
5
  SHA512:
6
- metadata.gz: 19577350a2b28642f9b654d5c4bdb14b245b0de85f29e54f2ec7812a943d545eeda0757b9e0509462cdb82d6803fe029bead9f819660ffbce985a11dc8980dcb
7
- data.tar.gz: 4a7278b141c18f6523882acac1fc8255ab7e44c05d599269169b00684575b5631b97298375c3047567fef6f92ff39dd00b8269b114504cdd63b53a9dae179a92
6
+ metadata.gz: ce524e72bfe8aec9444c0602d7488362047d0b0bb33e674e922f8398e942a626113b4241eed3647c820b1ddba1ecedcf2e3611698d936c91c38f5399c6840b4d
7
+ data.tar.gz: d07d6415672d8e0c28865813d29a8ef959aace1a0491a6c439ede5c283856a0f5c25aace7cf2955184a195dc633f247dfc531d34204705a3a35aa5a26aa640e6
data/README.md CHANGED
@@ -72,8 +72,8 @@ mount Healthier::Engine => '/healthier'
72
72
  ENVs for basic authentication:
73
73
 
74
74
  ```bash
75
- USERNAME: 'demouser'
76
- PASSWORD: 'demouser@2023'
75
+ HEALTHIER_USERNAME: 'demouser'
76
+ HEALTHIER_PASSWORD: 'demouser@2023'
77
77
  ```
78
78
 
79
79
  ENVs for Bearer token authentication:
@@ -88,9 +88,59 @@ However, if you want to leave the API open, just set:
88
88
  Healthier.auth_mechanism = :none
89
89
  ```
90
90
 
91
- Example Curl:
92
- ```curl
93
- curl -H "Bearer b5f3396a876e4f2ec828d11514c94d95d2f2330d251496a78cbe0e9bc330d775" -H "Content-Type: application/json" 'https://migros.selise.dev/api/v2/cnc_rsp/healthier/ping'
91
+ ## Health Check Endpoints
92
+
93
+ Healthier provides three distinct health check endpoints for monitoring your application:
94
+
95
+ ### 1. Ping Endpoint (`GET /healthier/ping`)
96
+ Returns detailed health status of all configured services.
97
+
98
+ **Response:** JSON object with service health statuses
99
+ ```json
100
+ {
101
+ "postgresql": {
102
+ "status": "success",
103
+ "message": "PostgreSQL service is healthy"
104
+ },
105
+ "redis": {
106
+ "status": "success",
107
+ "message": "Redis service is healthy"
108
+ }
109
+ }
110
+ ```
111
+
112
+ ### 2. Live Endpoint (`GET /healthier/live`)
113
+ Basic liveness check that verifies if the application is running.
114
+
115
+ **Response:** Boolean value
116
+ ```json
117
+ true
118
+ ```
119
+
120
+ ### 3. Ready Endpoint (`GET /healthier/ready`)
121
+ Readiness check that verifies if all configured services are healthy and the application is ready to serve requests.
122
+
123
+ **Response:** Boolean value
124
+ ```json
125
+ true
126
+ ```
127
+
128
+ ## Example Usage
129
+
130
+ ```bash
131
+ # Ping - detailed service health
132
+ curl http://localhost:3000/healthier/ping
133
+
134
+ # Live - basic liveness check
135
+ curl http://localhost:3000/healthier/live
136
+
137
+ # Ready - service readiness check
138
+ curl http://localhost:3000/healthier/ready
139
+ ```
140
+
141
+ Example Curl with authentication:
142
+ ```bash
143
+ curl -H "Bearer your_token_here" -H "Content-Type: application/json" 'http://localhost:3000/healthier/ping'
94
144
  ```
95
145
 
96
146
  ## Contributing
@@ -2,13 +2,22 @@
2
2
 
3
3
  module Healthier
4
4
  # Controller for performing health checks
5
- class ChecksController < ApplicationController
5
+ class HealthierController < ApplicationController
6
6
  include ActionController::HttpAuthentication::Basic::ControllerMethods
7
7
 
8
- before_action :authenticate_request
8
+ before_action :authenticate_request, except: [:live]
9
9
 
10
10
  def ping
11
- render json: Healthier::Engine.ping!
11
+ render json: Healthier::Doctor.ping!
12
+ end
13
+
14
+ # NOTE: Just return 200
15
+ def live
16
+ render json: Healthier::Doctor.up?
17
+ end
18
+
19
+ def ready
20
+ render json: Healthier::Doctor.ping!
12
21
  end
13
22
 
14
23
  protected
data/config/routes.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Healthier::Engine.routes.draw do
4
- get 'ping', to: 'checks#ping'
4
+ get 'ping', to: 'healthier#ping'
5
+ get 'live', to: 'healthier#live'
6
+ get 'ready', to: 'healthier#ready'
5
7
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Connectors
4
+ # ElasticSearch health checker
5
+ class ElasticSearch
6
+ def connect
7
+ url = ENV.fetch('ELASTICSEARCH_URL', 'http://localhost:9200')
8
+
9
+ require 'net/http'
10
+ require 'uri'
11
+ require 'json'
12
+
13
+ uri = URI.parse(url)
14
+ http = Net::HTTP.new(uri.host, uri.port)
15
+ http.read_timeout = 2
16
+ http.open_timeout = 2
17
+
18
+ response = http.get('/_cluster/health')
19
+ if response.code.to_i == 200
20
+ health_data = JSON.parse(response.body)
21
+ health_data['status'] != 'red'
22
+ else
23
+ false
24
+ end
25
+ rescue StandardError => e
26
+ puts "ElasticSearch Connection Error: #{e.message}"
27
+ false
28
+ end
29
+
30
+ def self.configured? # rubocop:disable Naming/PredicateMethod
31
+ raise StandardError, 'Please make sure you have Elasticsearch installed' unless defined?(::Elasticsearch::Client)
32
+
33
+ 'configured'
34
+ end
35
+ end
36
+ end
@@ -7,13 +7,14 @@ module Connectors
7
7
  @conn = ::Bunny.new
8
8
  @conn.start
9
9
  @channel = @conn.create_channel
10
- @queue = @channel.queue('health_check')
10
+ @queue = @channel.queue('health_check', auto_delete: true)
11
11
  @queue.publish('ping')
12
12
  true
13
13
  rescue ::Bunny::Exception, ::Bunny::TCPConnectionFailed, ::Bunny::PossibleAuthenticationFailureError => e
14
14
  puts "RabbitMQ Connection Error: #{e.message}"
15
15
  false
16
16
  ensure
17
+ # Queue with auto_delete: true will be automatically deleted when connection closes.
17
18
  @queue&.delete
18
19
  @queue&.purge
19
20
  @channel&.close
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Connectors
4
+ # Sidekiq health checker
5
+ class Sidekiq
6
+ def connect
7
+ ::Sidekiq.redis do |r|
8
+ r.ping == 'PONG'
9
+ end
10
+ rescue StandardError => e
11
+ puts "Sidekiq Connection Error: #{e.message}"
12
+ false
13
+ end
14
+
15
+ def self.configured?
16
+ raise StandardError, 'Please make sure you have Sidekiq installed' unless defined?(::Sidekiq)
17
+
18
+ 'configured'
19
+ end
20
+ end
21
+ end
@@ -32,6 +32,11 @@ module Healthier
32
32
  # config: {}
33
33
  # - name: 'rabbitmq'
34
34
  # config: {}
35
+ # - name: 'sidekiq'
36
+ # config: {}
37
+ # - name: 'elastic_search'
38
+ # config: {}
39
+
35
40
  YAML
36
41
  end
37
42
  end
@@ -17,13 +17,13 @@ module Healthier
17
17
 
18
18
  def authenticate_with_bearer_token
19
19
  controller.authenticate_with_http_token do |token, _options|
20
- ENV['BEARER_TOKEN'] == token
20
+ ENV['HEALTHIER_BEARER_TOKEN'] == token
21
21
  end
22
22
  end
23
23
 
24
24
  def authenticate_with_basic_auth
25
25
  controller.authenticate_or_request_with_http_basic do |username, password|
26
- username == ENV['USERNAME'] && password == ENV['PASSWORD']
26
+ username == ENV['HEALTHIER_USERNAME'] && password == ENV['HEALTHIER_PASSWORD']
27
27
  end
28
28
  end
29
29
  end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Healthier
4
+ # Health Checker Doctor
5
+ class Doctor
6
+ SUCCESS = :success
7
+ FAILURE = :failure
8
+
9
+ def initialize(config = nil)
10
+ @config = config || Healthier.depends_on['healthier'] || {}
11
+ @connectors = load_connectors
12
+ end
13
+
14
+ def self.ping!
15
+ new.ping
16
+ end
17
+
18
+ def self.up?
19
+ new.up?
20
+ end
21
+
22
+ def self.running?
23
+ new.running?
24
+ end
25
+
26
+ def up?
27
+ # Basic health check - just return true for now
28
+ # Could be extended to check basic application health
29
+ true
30
+ end
31
+
32
+ def running?
33
+ # Check if all configured services are running
34
+ ping.values.all? { |service_result| service_result[:status] == SUCCESS }
35
+ end
36
+
37
+ def ping
38
+ @config.fetch('depends_on', []).each_with_object({}) do |service_config, result|
39
+ result[service_config['name']] = diagnose(service_config)
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def load_connectors
46
+ @config.fetch('depends_on', []).to_h do |service_config|
47
+ [
48
+ service_config['name'],
49
+ "::Connectors::#{service_config['name'].camelize}".constantize
50
+ ]
51
+ end
52
+ end
53
+
54
+ def diagnose(service_config)
55
+ connector_class = @connectors[service_config['name']]
56
+
57
+ return custom_diagnosis(service_config) if service_config['custom_checker']
58
+
59
+ begin
60
+ connector = connector_class.new
61
+ connection_result = connector.connect
62
+
63
+ if connection_result == true
64
+ {
65
+ status: SUCCESS,
66
+ message: "#{service_config['name'].camelize} service is healthy"
67
+ }
68
+ else
69
+ {
70
+ status: FAILURE,
71
+ message: "#{service_config['name'].camelize} service is not responding"
72
+ }
73
+ end
74
+ rescue StandardError => e
75
+ {
76
+ status: FAILURE,
77
+ message: "Diagnosis failed for #{service_config['name']}: #{e.message}"
78
+ }
79
+ end
80
+ end
81
+
82
+ def custom_diagnosis(service_config)
83
+ custom_checker = service_config['custom_checker']
84
+
85
+ if custom_checker.respond_to?(:call)
86
+ custom_checker.call
87
+ elsif custom_checker.is_a?(String)
88
+ connector_class = @connectors[service_config['name']]
89
+ connector_class.send(custom_checker)
90
+ else
91
+ {
92
+ status: FAILURE,
93
+ message: "Invalid diagnosis method for #{service_config['name']}"
94
+ }
95
+ end
96
+ end
97
+ end
98
+ end
@@ -1,9 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'doctor'
3
4
  require_relative '../connectors/mongodb'
4
5
  require_relative '../connectors/postgresql'
5
6
  require_relative '../connectors/rabbitmq'
6
7
  require_relative '../connectors/redis'
8
+ require_relative '../connectors/sidekiq'
9
+ require_relative '../connectors/elastic_search'
7
10
 
8
11
  module Healthier
9
12
  # Engine
@@ -13,38 +16,13 @@ module Healthier
13
16
  initializer 'healthier.after_initialize' do
14
17
  ActiveSupport.on_load(:after_initialize) do
15
18
  dependent_services = Healthier.depends_on.dig('healthier', 'depends_on')
19
+ next unless dependent_services
20
+
16
21
  dependent_services.each do |service_conf|
17
- connector = "::Connectors::#{service_conf['name'].camelize}".constantize
18
- connector.configured?
22
+ connector_class = "::Connectors::#{service_conf['name'].camelize}".constantize
23
+ connector_class.configured?
19
24
  end
20
25
  end
21
26
  end
22
-
23
- def ping!
24
- conf['depends_on'].each_with_object({}) do |dependent, obj|
25
- obj[dependent['name']] = ping_it(dependent['name'])
26
- end
27
- end
28
-
29
- def conf
30
- @conf ||= Healthier.depends_on['healthier']
31
- end
32
-
33
- def ping_it(service)
34
- connector = "::Connectors::#{service.camelize}".constantize
35
- connection = connector.new.connect
36
-
37
- if connection == true
38
- return {
39
- status: Healthier::SUCCESS,
40
- message: 'pong'
41
- }
42
- end
43
-
44
- {
45
- status: Healthier::FAILURE,
46
- message: "#{service.camelize} service is not running. Please check your connection"
47
- }
48
- end
49
27
  end
50
28
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Healthier
4
4
  # MAJOR.MINOR.PATCH
5
- VERSION = '0.2.3'
5
+ VERSION = '0.2.5'
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: healthier
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nima Yonten
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-06-17 00:00:00.000000000 Z
11
+ date: 2025-12-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -24,9 +24,12 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '6.0'
27
- description: Description of Healthier.
27
+ description: Healthier provides comprehensive health check endpoints (live, ready,
28
+ ping) for Rails applications with support for monitoring multiple backing services
29
+ including PostgreSQL, MongoDB, Redis, RabbitMQ, Sidekiq, and ElasticSearch. It includes
30
+ built-in authentication and flexible configuration options.
28
31
  email:
29
- - nima.yonten1729@gmail.com
32
+ - bugloper@hey.com
30
33
  executables: []
31
34
  extensions: []
32
35
  extra_rdoc_files: []
@@ -35,15 +38,18 @@ files:
35
38
  - README.md
36
39
  - Rakefile
37
40
  - app/controllers/healthier/application_controller.rb
38
- - app/controllers/healthier/checks_controller.rb
41
+ - app/controllers/healthier/healthier_controller.rb
39
42
  - config/routes.rb
43
+ - lib/connectors/elastic_search.rb
40
44
  - lib/connectors/mongodb.rb
41
45
  - lib/connectors/postgresql.rb
42
46
  - lib/connectors/rabbitmq.rb
43
47
  - lib/connectors/redis.rb
48
+ - lib/connectors/sidekiq.rb
44
49
  - lib/generators/healthier/config_generator.rb
45
50
  - lib/healthier.rb
46
51
  - lib/healthier/api_authenticator.rb
52
+ - lib/healthier/doctor.rb
47
53
  - lib/healthier/engine.rb
48
54
  - lib/healthier/version.rb
49
55
  - lib/tasks/healthier_tasks.rake
@@ -55,8 +61,8 @@ metadata:
55
61
  homepage_uri: https://github.com/SELISEdigitalplatforms/healthier
56
62
  source_code_uri: https://github.com/SELISEdigitalplatforms/healthier
57
63
  changelog_uri: https://github.com/SELISEdigitalplatforms/healthier
58
- rubygems_mfa_required: 'true'
59
- post_install_message:
64
+ rubygems_mfa_required: 'false'
65
+ post_install_message:
60
66
  rdoc_options: []
61
67
  require_paths:
62
68
  - lib
@@ -71,8 +77,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
71
77
  - !ruby/object:Gem::Version
72
78
  version: '0'
73
79
  requirements: []
74
- rubygems_version: 3.4.10
75
- signing_key:
80
+ rubygems_version: 3.4.1
81
+ signing_key:
76
82
  specification_version: 4
77
- summary: Summary of Healthier.
83
+ summary: Healthier is a gem that provides a health check endpoint for your Rails application.
78
84
  test_files: []