healthier 0.1.5 → 0.1.7

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: 9b9085660c0bf98cddf325db77e9d7f7a655c34e4af17e0febcc2d0468d42a10
4
- data.tar.gz: b4cbbda7e758b1375c07d3ff98efa31ac85a52a9ede9c67dd2a06a95c75f9103
3
+ metadata.gz: 851390d021df1149267018e9ec714cee68168c2f1f9b02c0998790c33da63005
4
+ data.tar.gz: d6468040657b6ef60a75d68650c054966e359c9e16e076104e15022daf0ac41c
5
5
  SHA512:
6
- metadata.gz: 522eea93d714a4afe55e18f20be294674a3406915b31c858567471117aa6e9089d8a8269517efb6bd2b8c29a70d895109e1047a93c7ec68a739d856b8656d6a2
7
- data.tar.gz: 65e6082188ace39b2013404301fa137596b501d0f301fa0679697311038217642a08e29a7b5786fb406fdecf54016d90d49e6f652e90b89a9ae101b2db29448a
6
+ metadata.gz: c4d56fa78ad0f10d2d3cacf39277926636883acbba04c51f0bf85f5cc9888026f3ab4c50484c79a7d7d71e9a644c324ed8421e7a7c6ae5de407380fb114bf397
7
+ data.tar.gz: d53429a445fdc042ec7e73eb9a5f6fe80ec017314a85c287d42074e7fefca2fc67c0417e374effd23c858fa43685dd3916bded4522310ed8e9d58c7d5cca78f3
data/README.md CHANGED
@@ -33,25 +33,37 @@ $ gem install healthier
33
33
  ```
34
34
 
35
35
  This will generate these two files:
36
+
36
37
  1. config/healthier.yml
38
+
37
39
  ```yaml
38
40
  ---
39
41
  healthier:
40
42
  depends_on:
41
- # Uncomment accordingly
42
- # - name: 'postgresql'
43
- # - name: 'mongodb'
44
- # - name: 'redis'
45
- # - name: 'rabbitmq'
43
+ # Uncomment accordingly
44
+ # - name: 'postgresql'
45
+ # config: {}
46
+ # - name: 'mongodb'
47
+ # config: {}
48
+ # - name: 'redis'
49
+ # config: {}
50
+ # - name: 'rabbitmq'
51
+ # config: {}
46
52
  ```
53
+ NOTE: In future, config above will accept a hash each service's configuration
54
+
47
55
  2. config/initializers/healthier.rb
56
+
48
57
  ```ruby
49
58
  Healthier.setup do |config|
59
+ # If you don't specify auth_mechanism, then it defaults to http_basic.
60
+ # Currently, only http_basic and bearer_token are supported
61
+ config.auth_mechanism = 'bearer_token'
50
62
  config.depends_on = config.healthier.call
51
63
  end
52
64
  ```
53
65
 
54
- In your application routes.rb file:
66
+ In your application routes.rb file(namespace according to your need):
55
67
 
56
68
  ```ruby
57
69
  mount Healthier::Engine => '/healthier'
@@ -64,6 +76,23 @@ USERNAME: 'demouser'
64
76
  PASSWORD: 'demouser@2023'
65
77
  ```
66
78
 
79
+ ENVs for Bearer token authentication:
80
+
81
+ ```bash
82
+ BEARER_TOKEN: 'e66cfadb3ce37a714fdf3df237dcfded9ed9a0f9ccb09e6a744d731e9214bae6fac7bf6d27cab3969702b7aa54c81fe1cff1842ac03b3d5f1a918e1f5061dcb80b80363976ae5f3b916c793fdfa5283ce5f387286152d62e2dcaa5c8c0f857ceb2cda045'
83
+ ```
84
+
85
+ However, if you want to leave the API open, just set:
86
+
87
+ ```ruby
88
+ Healthier.auth_mechanism = :none
89
+ ```
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'
94
+ ```
95
+
67
96
  ## Contributing
68
97
 
69
98
  Contribution directions go here.
@@ -3,5 +3,6 @@
3
3
  module Healthier
4
4
  # ApplicationController
5
5
  class ApplicationController < ActionController::Base
6
+ include Errors::ApiErrors
6
7
  end
7
8
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Errors
4
+ module ApiErrors
5
+ def self.included(base)
6
+ base.class_eval do
7
+ rescue_from StandardError, with: :bad_request
8
+ end
9
+ end
10
+
11
+ def bad_request(error)
12
+ render_error(:bad_request, error: { message: error.message })
13
+ end
14
+
15
+ def render_success(status, options = {})
16
+ options[:success] = true
17
+ render json: options, status: status
18
+ end
19
+
20
+ def render_error(status, options = {})
21
+ options[:error] = { message: I18n.t("api_errors.#{status}") } if options[:error].blank?
22
+ options[:success] = false
23
+ Rollbar.error(options[:error])
24
+ render json: options, status: status
25
+ end
26
+
27
+ def render_silent_error(status, options = {})
28
+ options[:error] = { message: I18n.t("api_errors.#{status}") } if options[:error].blank?
29
+ options[:success] = false
30
+ render json: options, status: status
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Checkers
4
+ # Mongodb health checker
5
+ class MongodbChecker
6
+ class << self
7
+ def check
8
+ raise 'Please make sure you have Mongodb installed' unless defined?(::Mongo::Client)
9
+
10
+ begin
11
+ @client = Mongo::Client.new(
12
+ ENV.fetch('MONGO_DB_URI', nil) || ['localhost:27017'],
13
+ max_retries: 2,
14
+ retry_interval: 1
15
+ )
16
+ @client.command(ping: 1)
17
+ true
18
+ rescue Mongo::Error::SocketError, Mongo::Error::NoServerAvailable
19
+ false
20
+ ensure
21
+ @client&.close
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Checkers
4
+ # Postgres health checker
5
+ class PostgresqlChecker
6
+ class << self
7
+ def check
8
+ raise 'Please make sure you have Postgres installed' unless defined?(::ActiveRecord::Base)
9
+
10
+ begin
11
+ # @conn = ::PG.connect(dbname: ENV['DATABASE_NAME'])
12
+ # @conn.exec('SELECT 1')
13
+ @conn = ActiveRecord::Base.connection
14
+ true
15
+ rescue ::PG::ConnectionBad
16
+ false
17
+ ensure
18
+ @conn&.close
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Checkers
4
+ # Rabbitmq health checker
5
+ class RabbitmqChecker
6
+ class << self
7
+ def check
8
+ raise 'Please make sure you have RabbitMQ installed' unless defined?(::Bunny)
9
+
10
+ begin
11
+ @conn = ::Bunny.new
12
+ @conn.start
13
+ channel = @conn.create_channel
14
+ queue = channel.queue('health_check')
15
+ queue.publish('ping')
16
+ true
17
+ rescue ::Bunny::Exception, ::Bunny::TCPConnectionFailed, ::Bunny::PossibleAuthenticationFailureError
18
+ false
19
+ ensure
20
+ @conn&.close
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Checkers
4
+ # Redis health checker
5
+ class RedisChecker
6
+ class << self
7
+ def check
8
+ raise 'Please make sure you have Redis installed' unless defined?(::Redis)
9
+
10
+ begin
11
+ @redis = Redis.new
12
+ @redis.ping == 'PONG'
13
+ rescue Redis::CannotConnectError
14
+ false
15
+ ensure
16
+ @redis&.close
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -10,6 +10,9 @@ module Healthier
10
10
  # frozen_string_literal: true
11
11
 
12
12
  Healthier.setup do |config|
13
+ # Currently, there are :basic_auth(default), :bearer_token for API authentication
14
+ # But if you want to leave the API open, then just set auth_mechanism to :none to skip authentication
15
+ # config.auth_mechanism = :bearer_token
13
16
  config.depends_on = config.healthier.call
14
17
  end
15
18
  RUBY
@@ -22,9 +25,13 @@ module Healthier
22
25
  # Uncomment accordingly
23
26
  depends_on:
24
27
  # - name: 'postgresql'
28
+ # config: {}
25
29
  # - name: 'mongodb'
30
+ # config: {}
26
31
  # - name: 'redis'
32
+ # config: {}
27
33
  # - name: 'rabbitmq'
34
+ # config: {}
28
35
  YAML
29
36
  end
30
37
  end
@@ -3,25 +3,31 @@
3
3
  module Healthier
4
4
  # Engine
5
5
  class ApiAuthenticator
6
- attr_accessor :controller
6
+ attr_accessor :controller, :request
7
7
 
8
8
  def initialize(controller)
9
9
  @controller = controller
10
+ @request = controller.request
10
11
  end
11
12
 
12
13
  def authenticate
14
+ return true if Healthier.auth_mechanism == :none
15
+
13
16
  send("authenticate_with_#{Healthier.auth_mechanism}")
14
17
  end
15
18
 
16
19
  def authenticate_with_bearer_token
20
+ tok = request.authorization.to_s
21
+ raise StandardError, 'Access Denied' unless tok.starts_with?('Bearer')
22
+
17
23
  controller.authenticate_with_http_token do |token, _options|
18
- ENV['BEARER_TOKEN'] == token
24
+ raise StandardError, 'Invalid token' unless ENV['BEARER_TOKEN'] == token
19
25
  end
20
26
  end
21
27
 
22
28
  def authenticate_with_basic_auth
23
29
  controller.authenticate_or_request_with_http_basic do |username, password|
24
- username == ENV['USERNAME'] && password == ENV['PASSWORD']
30
+ raise StandardError, 'Invalid credentials' unless username == ENV['USERNAME'] && password == ENV['PASSWORD']
25
31
  end
26
32
  end
27
33
  end
@@ -1,35 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../checkers/mongodb_checker'
4
+ require_relative '../checkers/postgresql_checker'
5
+ require_relative '../checkers/rabbitmq_checker'
6
+ require_relative '../checkers/redis_checker'
7
+
3
8
  module Healthier
4
9
  # Engine
5
10
  class Engine < ::Rails::Engine
6
11
  isolate_namespace Healthier
7
12
 
8
- PING_CONF = HashWithIndifferentAccess.new(
9
- {
10
- postgresql: {
11
- pinger: -> { ActiveRecord::Base.connection },
12
- message: 'PostgreSQL service is not running. Please check your connection'
13
- },
14
- mongodb: {
15
- pinger: -> { Mongoid.default_client.command(ping: 1) },
16
- message: 'MongoDB service is not running. Please check your connection'
17
- },
18
- redis: {
19
- pinger: -> { Redis.new },
20
- message: 'Redis service is not running. Please check your connection'
21
- },
22
- rabbitmq: {
23
- pinger: -> { Bunny.new },
24
- message: 'RabbitMQ service is not running. Please check your connection'
25
- }
26
- }
27
- )
28
-
29
- # TODO: Refactor this whole code
30
- SUCCESS = 200
31
- FAILURE = 400
32
-
33
13
  def ping!
34
14
  conf['depends_on'].each_with_object({}) do |dependant, obj|
35
15
  obj[dependant['name']] = ping_it(dependant['name'])
@@ -41,9 +21,15 @@ module Healthier
41
21
  end
42
22
 
43
23
  def ping_it(service)
44
- return { status: SUCCESS, message: 'pong' } if PING_CONF[service]['pinger'].call
24
+ checked = checker(service).check
25
+
26
+ return { status: Healthier::SUCCESS, message: 'pong' } if checked == true
27
+
28
+ { status: Healthier::FAILURE, message: "#{service.camelize} service is not running. Please check your connection" }
29
+ end
45
30
 
46
- { status: FAILURE, message: PING_CONF[service]['message'] }
31
+ def checker(service)
32
+ "::Checkers::#{service.camelize}Checker".constantize
47
33
  end
48
34
  end
49
35
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Healthier
4
4
  # MAJOR.MINOR.PATCH
5
- VERSION = '0.1.5'
5
+ VERSION = '0.1.7'
6
6
  end
data/lib/healthier.rb CHANGED
@@ -10,6 +10,9 @@ module Healthier
10
10
  yield self
11
11
  end
12
12
 
13
+ SUCCESS = 200
14
+ FAILURE = 503
15
+
13
16
  mattr_accessor :healthier, default: -> { YAML.load_file(Rails.root.join('config/healthier.yml')) }
14
17
  mattr_accessor :auth_mechanism, default: 'basic_auth'
15
18
  mattr_accessor :depends_on, default: {}
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.1.5
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nima Yonten
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-03 00:00:00.000000000 Z
11
+ date: 2023-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -36,7 +36,12 @@ files:
36
36
  - Rakefile
37
37
  - app/controllers/healthier/application_controller.rb
38
38
  - app/controllers/healthier/checks_controller.rb
39
+ - app/lib/errors/api_errors.rb
39
40
  - config/routes.rb
41
+ - lib/checkers/mongodb_checker.rb
42
+ - lib/checkers/postgresql_checker.rb
43
+ - lib/checkers/rabbitmq_checker.rb
44
+ - lib/checkers/redis_checker.rb
40
45
  - lib/generators/healthier/config_generator.rb
41
46
  - lib/healthier.rb
42
47
  - lib/healthier/api_authenticator.rb
@@ -51,6 +56,7 @@ metadata:
51
56
  homepage_uri: https://github.com/selisebt/healthier
52
57
  source_code_uri: https://github.com/selisebt/healthier
53
58
  changelog_uri: https://github.com/selisebt/healthier
59
+ rubygems_mfa_required: 'true'
54
60
  post_install_message:
55
61
  rdoc_options: []
56
62
  require_paths: