summaryrefslogtreecommitdiff
path: root/lib/action/custom.rb
blob: 328a12f7e9437d7ff5a407747c1d3a744a2699da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
require 'base64'

require_relative '../http_helper'

module Action
  class Custom
    include HTTPHelper

    class BaseError < StandardError; end
    class MissingPayloadError < BaseError; end
    class MissingAPIEndpointsError < BaseError; end
    class MissingDataError < BaseError; end
    class UnsuccessfulError < BaseError; end

    NO_MESSAGE_TEXT = 'No message'.freeze
    DEFAULT_HEADERS = { 'Content-Type' => CONTENT_TYPE_JSON }.freeze

    def initialize(gl_id, payload)
      @gl_id = gl_id
      @payload = payload
    end

    def execute
      validate!
      result = process_api_endpoints

      if result && HTTP_SUCCESS_CODES.include?(result.code)
        result
      else
        raise_unsuccessful!(result)
      end
    end

    private

    attr_reader :gl_id, :payload

    def process_api_endpoints
      output = ''
      resp = nil

      data_with_gl_id = data.merge('gl_id' => gl_id)

      api_endpoints.each do |endpoint|
        url = "#{base_url}#{endpoint}"
        json = { 'data' => data_with_gl_id, 'output' => output }

        resp = post(url, {}, headers: DEFAULT_HEADERS, options: { json: json })
        return resp unless HTTP_SUCCESS_CODES.include?(resp.code)

        begin
          body = JSON.parse(resp.body)
        rescue JSON::ParserError
          raise UnsuccessfulError, 'Response was not valid JSON'
        end

        inform_client(body['message']) if body['message']

        print_flush(body['result'])

        # In the context of the git push sequence of events, it's necessary to read
        # stdin in order to capture output to pass onto subsequent commands
        output = read_stdin
      end

      resp
    end

    def base_url
      config.gitlab_url
    end

    def data
      @data ||= payload['data']
    end

    def api_endpoints
      data['api_endpoints']
    end

    def config
      @config ||= GitlabConfig.new
    end

    def api
      @api ||= GitlabNet.new
    end

    def read_stdin
      Base64.encode64($stdin.read)
    end

    def print_flush(str)
      return false unless str
      $stdout.print(Base64.decode64(str))
      $stdout.flush
    end

    def inform_client(str)
      $stderr.puts(str)
    end

    def validate!
      validate_payload!
      validate_data!
      validate_api_endpoints!
    end

    def validate_payload!
      raise MissingPayloadError if !payload.is_a?(Hash) || payload.empty?
    end

    def validate_data!
      raise MissingDataError unless data.is_a?(Hash)
    end

    def validate_api_endpoints!
      raise MissingAPIEndpointsError if !api_endpoints.is_a?(Array) ||
                                        api_endpoints.empty?
    end

    def raise_unsuccessful!(result)
      message = begin
        body = JSON.parse(result.body)
        message = body['message']
        message = Base64.decode64(body['result']) if !message && body['result'] && !body['result'].empty?
        message ? message : NO_MESSAGE_TEXT
      rescue JSON::ParserError
        NO_MESSAGE_TEXT
      end

      raise UnsuccessfulError, "#{message} (#{result.code})"
    end
  end
end