obstools/healthcheck-endpoint

View on GitHub
lib/healthcheck_endpoint/configuration.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
# frozen_string_literal: true

module HealthcheckEndpoint
  class Configuration
    ATTRIBUTES = %i[
      services
      services_startup
      services_liveness
      services_readiness
      endpoints_namespace
      endpoint_startup
      endpoint_liveness
      endpoint_readiness
      endpoint_startup_status_success
      endpoint_liveness_status_success
      endpoint_readiness_status_success
      endpoint_startup_status_failure
      endpoint_liveness_status_failure
      endpoint_readiness_status_failure
    ].freeze
    ENDPOINTS_NAMESPACE = '/healthcheck'
    ENDPOINT_STARTUP = '/startup'
    ENDPOINT_LIVENESS = '/liveness'
    ENDPOINT_READINESS = '/readiness'
    DEFAULT_HTTP_STATUS_SUCCESS = 200
    DEFAULT_HTTP_STATUS_FAILURE = 500
    AVILABLE_HTTP_STATUSES_SUCCESS = (DEFAULT_HTTP_STATUS_SUCCESS..226).freeze
    AVILABLE_HTTP_STATUSES_FAILURE = (DEFAULT_HTTP_STATUS_FAILURE..511).freeze

    Settings = ::Struct.new(*HealthcheckEndpoint::Configuration::ATTRIBUTES, keyword_init: true) do
      def update(&block)
        return self unless block

        tap(&block)
      end
    end

    attr_reader(*HealthcheckEndpoint::Configuration::ATTRIBUTES)

    def initialize(&block)
      configuration_settings = build_configuration_settings(&block)
      HealthcheckEndpoint::Configuration::ATTRIBUTES.each do |attribute|
        public_send(:"#{attribute}=", configuration_settings.public_send(attribute))
      end
    end

    HealthcheckEndpoint::Configuration::ATTRIBUTES.each do |attribute|
      define_method(:"#{attribute}=") do |argument|
        validate_attribute(__method__, attribute, argument)
        instance_variable_set(:"@#{attribute}", argument)
      end
    end

    private

    def build_configuration_settings(&block) # rubocop:disable Metrics/MethodLength
      HealthcheckEndpoint::Configuration::Settings.new(
        services: {},
        services_startup: [],
        services_liveness: [],
        services_readiness: [],
        endpoints_namespace: HealthcheckEndpoint::Configuration::ENDPOINTS_NAMESPACE,
        endpoint_startup: HealthcheckEndpoint::Configuration::ENDPOINT_STARTUP,
        endpoint_liveness: HealthcheckEndpoint::Configuration::ENDPOINT_LIVENESS,
        endpoint_readiness: HealthcheckEndpoint::Configuration::ENDPOINT_READINESS,
        endpoint_startup_status_success: HealthcheckEndpoint::Configuration::DEFAULT_HTTP_STATUS_SUCCESS,
        endpoint_liveness_status_success: HealthcheckEndpoint::Configuration::DEFAULT_HTTP_STATUS_SUCCESS,
        endpoint_readiness_status_success: HealthcheckEndpoint::Configuration::DEFAULT_HTTP_STATUS_SUCCESS,
        endpoint_startup_status_failure: HealthcheckEndpoint::Configuration::DEFAULT_HTTP_STATUS_FAILURE,
        endpoint_liveness_status_failure: HealthcheckEndpoint::Configuration::DEFAULT_HTTP_STATUS_FAILURE,
        endpoint_readiness_status_failure: HealthcheckEndpoint::Configuration::DEFAULT_HTTP_STATUS_FAILURE
      ).update(&block)
    end

    def validate_attribute(method, attribute, value) # rubocop:disable Metrics/AbcSize
      raise_unless(HealthcheckEndpoint::Error::Configuration::ArgumentType, method, *validator_argument_type(attribute, value))
      case attribute
      when HealthcheckEndpoint::Configuration::ATTRIBUTES[0]
        raise_unless(HealthcheckEndpoint::Error::Configuration::NotCallableService, method, *validator_services_callable(value))
      when *HealthcheckEndpoint::Configuration::ATTRIBUTES[1..3]
        raise_unless(HealthcheckEndpoint::Error::Configuration::UnknownService, method, *validator_services_conformity(value))
      when *HealthcheckEndpoint::Configuration::ATTRIBUTES[4..7]
        raise_unless(HealthcheckEndpoint::Error::Configuration::EnpointPattern, method, *validator_endpoint(value))
      when *HealthcheckEndpoint::Configuration::ATTRIBUTES[8..10]
        raise_unless(HealthcheckEndpoint::Error::Configuration::HttpStatusSuccess, method, *validator_http_status_success(value))
      when *HealthcheckEndpoint::Configuration::ATTRIBUTES[11..13]
        raise_unless(HealthcheckEndpoint::Error::Configuration::HttpStatusFailure, method, *validator_http_status_failure(value))
      end
    end

    def validator_argument_type(method_name, argument)
      [
        argument,
        argument.is_a?(
          case method_name
          when :services then ::Hash
          when *HealthcheckEndpoint::Configuration::ATTRIBUTES[1..3] then ::Array
          when *HealthcheckEndpoint::Configuration::ATTRIBUTES[4..7] then ::String
          when *HealthcheckEndpoint::Configuration::ATTRIBUTES[8..13] then ::Integer
          end
        )
      ]
    end

    def validator_endpoint(argument)
      [argument, argument[%r{\A/.*\z}]]
    end

    def validator_http_status_success(argument)
      [argument, HealthcheckEndpoint::Configuration::AVILABLE_HTTP_STATUSES_SUCCESS.include?(argument)]
    end

    def validator_http_status_failure(argument)
      [argument, HealthcheckEndpoint::Configuration::AVILABLE_HTTP_STATUSES_FAILURE.include?(argument)]
    end

    def validator_services_callable(services_to_check, target_service = nil)
      result = services_to_check.all? do |service_name, service_context|
        target_service = service_name
        service_context.respond_to?(:call)
      end

      [target_service, result]
    end

    def validator_services_conformity(services_to_check, target_service = nil)
      result = services_to_check.all? do |service_name|
        target_service = service_name
        services.key?(service_name)
      end

      [target_service, result]
    end

    def raise_unless(exception_class, argument_name, argument_context, condition)
      raise exception_class.new(argument_context, argument_name) unless condition
    end
  end
end