benichu/checklister

View on GitHub
lib/checklister/client.rb

Summary

Maintainability
A
1 hr
Test Coverage
require "gitlab"
require "octokit"

module Checklister
  # Implement a kind of Factory pattern
  #
  # We create an object without exposing the creation logic and then refer to newly created object using a common interface.
  class Client
    # The issue service backends we are currently supporting
    #
    #  - [x] gitlab
    #  - [x] github
    #  - [ ] bitbucket
    #
    IMPLEMENTED_BACKENDS = %w(gitlab github)

    attr_reader :api_client

    # Initialize all the issue service options required to be able to interact with it
    #
    # @example Initialize a gitlab client's options
    #   gitlab_client = Checklister::Client.new(kind: "gitlab", endpoint: "https://gitlab.com/api/v3", private_token: "supersecret")
    #
    # @param options [Hash] list of key/values to apply to the appropriately created API client
    # @option opts [String] :kind The issue service identifier ('github', 'gitlab', ...)
    # @option opts [String] :endpoint The issue service endpoint url
    # @option opts [String] :private_token The issue service user's private token
    #
    # @raise [ArgumentError] When no or an inexistent issue service kind is set
    #
    # @return [Object] a factory instance
    #
    def initialize(options = {})
      @kind = options.fetch(:kind) { raise ArgumentError, "No API client can be initialized" }
      raise(NotImplementedError, "No #{@kind} API client has been implemented") unless IMPLEMENTED_BACKENDS.include?(@kind)
      @options = options.reject { |k| [:kind].include? k }
      default_options = { user_agent: "Checklister for #{@kind} #{Checklister::VERSION}" }
      @options.merge! default_options
      @options_for_kind = prepare_options_for_kind(@options, @kind)
      @project_klass = nil
      @issue_klass = nil
      define_classes_for_kind(@kind)
      @api_client = get_api_client
    end

    def project
      @project ||= @project_klass.new(@api_client)
    end

    def issue
      @issue ||= @issue_klass.new(@api_client)
    end

    private

    # Initialize the issue service API client
    #
    # @example get an API client
    #   gitlab_client = Checklister::Client.new(kind: "gitlab", endpoint: "https://gitlab.com/api/v3", private_token: "supersecret")
    #   gtilab_client.get_api_client
    #
    # @return [Object] the API client based on our initialized options
    #
    def get_api_client
      case @kind
      when "gitlab"
        @api_client ||= ::Gitlab.client(@options_for_kind)
      when "github"
        @api_client ||= Octokit::Client.new(@options_for_kind)
      end
    end

    def prepare_options_for_kind(options, kind)
      httparty_options = {}
      if kind == "github"
        # Octokit uses ENV for overide
        # See: https://github.com/octokit/octokit.rb/blob/a98979107a4bf9741a05a1f751405f8a29f29b38/lib/octokit/default.rb#L136-L138
        options.delete(:user_agent)
        # :endpoint is not required
        options.delete(:endpoint)
        # :private_token is called :access_token
        options[:access_token] = options.delete(:private_token)
      elsif kind == "gitlab"
        httparty_options[:ssl_ca_file]  = options[:endpoint_certificate_path] unless options[:endpoint_certificate_path].to_s == ""
        httparty_options[:p12]          = File.read(options[:client_certificate_path]) unless options[:client_certificate_path].to_s == ""
        httparty_options[:p12_password] = options[:client_certificate_password] unless options[:client_certificate_password].to_s == ""
        options.merge!(httparty: httparty_options)
      end
      options
    end

    def define_classes_for_kind(kind)
      @project_klass = if kind == "gitlab"
                         Checklister::Gitlab::Project
                       elsif kind == "github"
                         Checklister::Github::Project
                       end
      @issue_klass = if kind == "gitlab"
                       Checklister::Gitlab::Issue
                     elsif kind == "github"
                       Checklister::Github::Issue
                     end
    end
  end
end