mhenrixon/stub_requests

View on GitHub
lib/stub_requests/uri/builder.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true

#
# Abstraction over WebMock to reduce duplication
#
# @author Mikael Henriksson <mikael@zoolutions.se>
# @since 0.1.0
#
module StubRequests
  #
  # Module URI organizes all gem logic regarding URI
  #
  # @author Mikael Henriksson <mikael@zoolutions.se>
  #
  module URI
    #
    # Builder constructs and validates URIs
    #
    # @author Mikael Henriksson <mikael@zoolutions.se>
    #
    class Builder
      #
      # @return [Regexp] A pattern for matching url segment keys
      URI_KEY = /(:[a-zA-Z_]+)/.freeze

      #
      # Convenience method to avoid .new.build
      #
      #
      # @raise [UriSegmentMismatch] when there are unused URI segments
      # @raise [UriSegmentMismatch] when the template have unplaced URI segments
      #
      # @param [String] the URI to the service endpoint
      # @param [Hash<Symbol>] route_params a list of uri_replacement keys
      #
      # @return [String] a validated URI string
      #
      def self.build(uri, route_params = {})
        new(uri, route_params).build
      end

      #
      # @!attribute [r] uri
      #   @return [String] the URI to the service endpoint
      attr_reader :uri
      #
      # @!attribute [r] route_params
      #   @return [Hash<Symbol] a hash with keys matching the {#template}
      attr_reader :route_params
      #
      # @!attribute [r] received_keys
      #   @return [Array<String>] a list with actual {#route_params} keys
      attr_reader :received_keys
      #
      # @!attribute [r] expected_keys
      #   @return [Array<String>] a list of expected route keys
      attr_reader :expected_keys

      #
      # Initializes a new Builder
      #
      #
      # @param [String] uri the URI used to reach the service
      # @param [Hash<Symbol>] route_params a list of uri_replacement keys
      #
      def initialize(uri, route_params = {})
        @uri           = +uri
        @route_params  = route_params.to_route_param
        @received_keys = @route_params.keys
        @expected_keys = uri.scan(URI_KEY).flatten.uniq
      end

      #
      # Builds a URI string
      #
      #
      # @raise [UriSegmentMismatch] when there are unused URI segments
      # @raise [UriSegmentMismatch] when the template have unplaced URI segments
      #
      # @return [String] a validated URI string
      #
      def build
        validate_uri_has_route_params!
        build_uri
        validate_uri

        uri
      end

      private

      def validate_uri_has_route_params!
        return if validate_uri_has_route_params

        raise UriSegmentMismatch, uri: uri, expected_keys: expected_keys, received_keys: received_keys
      end

      def validate_uri_has_route_params
        expected_keys.sort == received_keys.sort
      end

      def build_uri
        route_params.each do |key, value|
          replace_key(key, value)
        end
      end

      def replace_key(key, value)
        uri.gsub!(key, value.to_s)
      end

      #
      # Validates {#uri} is valid
      #
      #
      # @return [true, false]
      #
      def validate_uri
        StubRequests::URI::Validator.valid?(uri)
      rescue InvalidUri
        StubRequests.logger.warn("URI (#{uri}) is not valid.")
        false
      end

      #
      # Raise exception when {#validate_uri} is false
      #
      # @see #validate_uri
      #
      # @raise [InvalidUri] when #{uri} is invalid
      #
      # @return [void]
      #
      # :nocov:
      # :nodoc:
      def validate_uri!
        raise InvalidUri, uri unless validate_uri
      end
    end
  end
end