privacygate 1.1.1
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 +7 -0
- data/.github/ISSUE_REQUEST_TEMPLATE/issue_request_template.md +36 -0
- data/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +51 -0
- data/.gitignore +51 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +278 -0
- data/Rakefile +6 -0
- data/examples/charge.rb +38 -0
- data/examples/checkout.rb +61 -0
- data/examples/event.rb +26 -0
- data/examples/webhook.rb +35 -0
- data/lib/privacygate/api_errors.rb +157 -0
- data/lib/privacygate/api_resources/base/api_object.rb +206 -0
- data/lib/privacygate/api_resources/base/api_resource.rb +25 -0
- data/lib/privacygate/api_resources/base/create.rb +15 -0
- data/lib/privacygate/api_resources/base/delete.rb +16 -0
- data/lib/privacygate/api_resources/base/list.rb +25 -0
- data/lib/privacygate/api_resources/base/save.rb +18 -0
- data/lib/privacygate/api_resources/base/update.rb +15 -0
- data/lib/privacygate/api_resources/charge.rb +14 -0
- data/lib/privacygate/api_resources/checkout.rb +19 -0
- data/lib/privacygate/api_resources/event.rb +13 -0
- data/lib/privacygate/api_response.rb +48 -0
- data/lib/privacygate/client.rb +120 -0
- data/lib/privacygate/util.rb +59 -0
- data/lib/privacygate/version.rb +3 -0
- data/lib/privacygate/webhooks.rb +52 -0
- data/lib/privacygate.rb +42 -0
- data/privacygate.gemspec +28 -0
- data/spec/api_resources/base/api_object_spec.rb +156 -0
- data/spec/api_resources/base/api_resource_spec.rb +32 -0
- data/spec/api_resources/charge_spec.rb +19 -0
- data/spec/api_resources/checkout_spec.rb +31 -0
- data/spec/api_resources/event_spec.rb +12 -0
- data/spec/endpont_spec.rb +103 -0
- data/spec/error_spec.rb +58 -0
- data/spec/response_spec.rb +43 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/webhook_spec.rb +36 -0
- metadata +161 -0
@@ -0,0 +1,157 @@
|
|
1
|
+
module PrivacyGate
|
2
|
+
module Errors
|
3
|
+
class APIError < StandardError
|
4
|
+
attr_reader :message
|
5
|
+
|
6
|
+
# Response contains a PrivacyGateResponse object
|
7
|
+
attr_accessor :response
|
8
|
+
|
9
|
+
attr_reader :http_body
|
10
|
+
attr_reader :http_headers
|
11
|
+
attr_reader :http_status
|
12
|
+
attr_reader :json_body
|
13
|
+
attr_reader :request_id
|
14
|
+
|
15
|
+
# Initializes a API error.
|
16
|
+
def initialize(message = nil, http_status: nil, http_body: nil,
|
17
|
+
json_body: nil, http_headers: nil)
|
18
|
+
@message = message
|
19
|
+
@http_status = http_status
|
20
|
+
@http_body = http_body
|
21
|
+
@http_headers = http_headers || {}
|
22
|
+
@json_body = json_body
|
23
|
+
@request_id = @http_headers["x-request-id"]
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
status_string = @http_status.nil? ? "" : "(Status #{@http_status}) "
|
28
|
+
id_string = @request_id.nil? ? "" : "(Request #{@request_id}) "
|
29
|
+
"#{status_string}#{id_string}#{@message}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# in case error connecting to privacygate server
|
34
|
+
class APIConnectionError < APIError
|
35
|
+
end
|
36
|
+
|
37
|
+
# Status 400
|
38
|
+
class BadRequestError < APIError
|
39
|
+
end
|
40
|
+
|
41
|
+
class ParamRequiredError < APIError
|
42
|
+
end
|
43
|
+
|
44
|
+
class InvalidRequestError < APIError
|
45
|
+
end
|
46
|
+
|
47
|
+
# Status 401
|
48
|
+
class AuthenticationError < APIError
|
49
|
+
end
|
50
|
+
|
51
|
+
# Status 404
|
52
|
+
class ResourceNotFoundError < APIError
|
53
|
+
end
|
54
|
+
|
55
|
+
# Status 422
|
56
|
+
class ValidationError < APIError
|
57
|
+
end
|
58
|
+
|
59
|
+
# Status 429
|
60
|
+
class RateLimitExceededError < APIError
|
61
|
+
end
|
62
|
+
|
63
|
+
# Status 500
|
64
|
+
class InternalServerError < APIError
|
65
|
+
end
|
66
|
+
|
67
|
+
# Status 503
|
68
|
+
class ServiceUnavailableError < APIError
|
69
|
+
end
|
70
|
+
|
71
|
+
# Webhook errors
|
72
|
+
class WebhookError < APIError
|
73
|
+
attr_accessor :sig_header
|
74
|
+
|
75
|
+
def initialize(message, sig_header, http_body: nil)
|
76
|
+
super(message, http_body: http_body)
|
77
|
+
@sig_header = sig_header
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class SignatureVerificationError < WebhookError
|
82
|
+
end
|
83
|
+
|
84
|
+
class WebhookInvalidPayload < WebhookError
|
85
|
+
end
|
86
|
+
|
87
|
+
# Errors handling
|
88
|
+
def self.handle_error_response(http_resp)
|
89
|
+
begin
|
90
|
+
resp = PrivacyGateResponse.from_faraday_hash(http_resp)
|
91
|
+
error_data = resp.data[:error]
|
92
|
+
|
93
|
+
raise APIError, "Unknown error" unless error_data
|
94
|
+
rescue JSON::ParserError, APIError
|
95
|
+
raise general_api_error(http_resp[:status], http_resp[:body])
|
96
|
+
end
|
97
|
+
error = specific_api_error(resp, error_data)
|
98
|
+
error.response = resp
|
99
|
+
raise(error)
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.general_api_error(status, body)
|
103
|
+
APIError.new("Invalid response object from API: #{body.inspect} " +
|
104
|
+
"(HTTP response code: #{status} http_body: #{body}")
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.specific_api_error(resp, error_data)
|
108
|
+
opts = {
|
109
|
+
http_body: resp.http_body,
|
110
|
+
http_headers: resp.http_headers,
|
111
|
+
http_status: resp.http_status,
|
112
|
+
json_body: resp.data,
|
113
|
+
}
|
114
|
+
case resp.http_status
|
115
|
+
when 400
|
116
|
+
# in case of known error code
|
117
|
+
case error_data[:type]
|
118
|
+
when 'param_required'
|
119
|
+
ParamRequiredError.new(error_data[:message], opts)
|
120
|
+
when 'validation_error'
|
121
|
+
ValidationError.new(error_data[:message], opts)
|
122
|
+
when 'invalid_request'
|
123
|
+
InvalidRequestError.new(error_data[:message], opts)
|
124
|
+
else
|
125
|
+
InvalidRequestError.new(error_data[:message], opts)
|
126
|
+
end
|
127
|
+
when 401 then
|
128
|
+
AuthenticationError.new(error_data[:message], opts)
|
129
|
+
when 404
|
130
|
+
ResourceNotFoundError.new(error_data[:message], opts)
|
131
|
+
when 429
|
132
|
+
RateLimitExceededError.new(error_data[:message], opts)
|
133
|
+
when 500
|
134
|
+
InternalServerError.new(error_data[:message], opts)
|
135
|
+
when 503
|
136
|
+
ServiceUnavailableError.new(error_data[:message], opts)
|
137
|
+
else
|
138
|
+
APIError.new(error_data[:message], opts)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.handle_network_error(e, api_base = nil)
|
143
|
+
api_base ||= @api_uri
|
144
|
+
case e
|
145
|
+
when Faraday::ConnectionFailed
|
146
|
+
message = "Unexpected error communicating when trying to connect to PrivacyGate."
|
147
|
+
when Faraday::SSLError
|
148
|
+
message = "Could not establish a secure connection to PrivacyGate."
|
149
|
+
when Faraday::TimeoutError
|
150
|
+
message = "Could not connect to PrivacyGate (#{api_base})."
|
151
|
+
else
|
152
|
+
message = "Unexpected error communicating with PrivacyGate."
|
153
|
+
end
|
154
|
+
raise APIConnectionError, message + "\n\n(Network error: #{e.message})"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
module PrivacyGate
|
2
|
+
module APIResources
|
3
|
+
module Base
|
4
|
+
# Base APIObject class
|
5
|
+
# Used to work and display with all the data
|
6
|
+
# that PrivacyGate API returns
|
7
|
+
class APIObject
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
def initialize(id = nil, client = nil)
|
11
|
+
@data = {}
|
12
|
+
@data[:id] = id if id
|
13
|
+
@client = client
|
14
|
+
@unsaved_values = Set.new
|
15
|
+
@transient_values = Set.new
|
16
|
+
end
|
17
|
+
|
18
|
+
# Base object options section
|
19
|
+
def [](k)
|
20
|
+
@data[k.to_sym]
|
21
|
+
end
|
22
|
+
|
23
|
+
def []=(k, v)
|
24
|
+
send(:"#{k}=", v)
|
25
|
+
end
|
26
|
+
|
27
|
+
def keys
|
28
|
+
@data.keys
|
29
|
+
end
|
30
|
+
|
31
|
+
def values
|
32
|
+
@data.values
|
33
|
+
end
|
34
|
+
|
35
|
+
def each(&blk)
|
36
|
+
@data.each(&blk)
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s(*_args)
|
40
|
+
JSON.pretty_generate(to_hash)
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_hash
|
44
|
+
@data.each_with_object({}) do |(key, value), output|
|
45
|
+
case value
|
46
|
+
when Array
|
47
|
+
output[key] = value.map {|v| v.respond_to?(:to_hash) ? v.to_hash : v}
|
48
|
+
else
|
49
|
+
output[key] = value.respond_to?(:to_hash) ? value.to_hash : value
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_json(*_a)
|
55
|
+
JSON.generate(@data)
|
56
|
+
end
|
57
|
+
|
58
|
+
def inspect
|
59
|
+
item_id = respond_to?(:id) && !id.nil? ? "id=#{id}" : "No ID"
|
60
|
+
"#{self.class}: #{item_id}> Serialized: " + JSON.pretty_generate(@data)
|
61
|
+
end
|
62
|
+
|
63
|
+
def respond_to_missing?(symbol, include_private = false)
|
64
|
+
@data && @data.key?(symbol) || super
|
65
|
+
end
|
66
|
+
|
67
|
+
def method_missing(name, *args)
|
68
|
+
if name.to_s.end_with?("=")
|
69
|
+
|
70
|
+
attr = name.to_s[0...-1].to_sym
|
71
|
+
val = args.first
|
72
|
+
add_accessors([attr], attr => val)
|
73
|
+
|
74
|
+
begin
|
75
|
+
mth = method(name)
|
76
|
+
rescue NameError
|
77
|
+
raise NoMethodError, "Cannot set #{attr} on this object."
|
78
|
+
end
|
79
|
+
|
80
|
+
return mth.call(args[0])
|
81
|
+
|
82
|
+
elsif @data.key?(name)
|
83
|
+
return @data[name]
|
84
|
+
end
|
85
|
+
|
86
|
+
begin
|
87
|
+
super
|
88
|
+
rescue NoMethodError => e
|
89
|
+
raise unless @transient_values.include?(name)
|
90
|
+
raise NoMethodError, e.message + " Available attributes: #{@data.keys.join(', ')}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Object serialize section
|
95
|
+
def serialize_params(options = {})
|
96
|
+
update_hash = {}
|
97
|
+
|
98
|
+
@data.each do |k, v|
|
99
|
+
if options[:push] || @unsaved_values.include?(k) || v.is_a?(APIObject)
|
100
|
+
push = options[:push] || @unsaved_values.include?(k)
|
101
|
+
update_hash[k.to_sym] = serialize_params_value(@data[k], push)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
update_hash.reject! {|_, v| v.nil? || v.empty?}
|
106
|
+
update_hash
|
107
|
+
end
|
108
|
+
|
109
|
+
def serialize_params_value(value, push)
|
110
|
+
if value.nil?
|
111
|
+
""
|
112
|
+
elsif value.is_a?(Array)
|
113
|
+
value.map {|v| serialize_params_value(v, push)}
|
114
|
+
|
115
|
+
elsif value.is_a?(Hash)
|
116
|
+
Util.convert_to_api_object(value, @opts).serialize_params
|
117
|
+
|
118
|
+
elsif value.is_a?(APIObject)
|
119
|
+
value.serialize_params(push: push)
|
120
|
+
else
|
121
|
+
value
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Object initialize/update section
|
126
|
+
def self.create_from(values, client = nil)
|
127
|
+
values = Util.symbolize_names(values)
|
128
|
+
new(values[:id], client).send(:initialize_from, values)
|
129
|
+
end
|
130
|
+
|
131
|
+
def initialize_from(values, partial = false)
|
132
|
+
removed = partial ? Set.new : Set.new(@data.keys - values.keys)
|
133
|
+
added = Set.new(values.keys - @data.keys)
|
134
|
+
|
135
|
+
remove_accessors(removed)
|
136
|
+
add_accessors(added, values)
|
137
|
+
|
138
|
+
removed.each do |k|
|
139
|
+
@data.delete(k)
|
140
|
+
@transient_values.add(k)
|
141
|
+
@unsaved_values.delete(k)
|
142
|
+
end
|
143
|
+
|
144
|
+
update_attributes(values)
|
145
|
+
values.each_key do |k|
|
146
|
+
@transient_values.delete(k)
|
147
|
+
@unsaved_values.delete(k)
|
148
|
+
end
|
149
|
+
|
150
|
+
self
|
151
|
+
end
|
152
|
+
|
153
|
+
def update_attributes(values)
|
154
|
+
values.each do |k, v|
|
155
|
+
add_accessors([k], values) unless metaclass.method_defined?(k.to_sym)
|
156
|
+
@data[k] = Util.convert_to_api_object(v, @client)
|
157
|
+
@unsaved_values.add(k)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
protected
|
163
|
+
|
164
|
+
def metaclass
|
165
|
+
class << self
|
166
|
+
self
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def remove_accessors(keys)
|
171
|
+
metaclass.instance_eval do
|
172
|
+
keys.each do |k|
|
173
|
+
# Remove methods for the accessor's reader and writer.
|
174
|
+
[k, :"#{k}=", :"#{k}?"].each do |method_name|
|
175
|
+
remove_method(method_name) if method_defined?(method_name)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def add_accessors(keys, values)
|
182
|
+
metaclass.instance_eval do
|
183
|
+
keys.each do |k|
|
184
|
+
if k == :method
|
185
|
+
define_method(k) {|*args| args.empty? ? @data[k] : super(*args)}
|
186
|
+
else
|
187
|
+
define_method(k) {@data[k]}
|
188
|
+
end
|
189
|
+
|
190
|
+
define_method(:"#{k}=") do |v|
|
191
|
+
if v != ""
|
192
|
+
@data[k] = Util.convert_to_api_object(v, @opts)
|
193
|
+
@unsaved_values.add(k)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
if [FalseClass, TrueClass].include?(values[k].class)
|
198
|
+
define_method(:"#{k}?") {@data[k]}
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module PrivacyGate
|
2
|
+
module APIResources
|
3
|
+
module Base
|
4
|
+
# Base resource class
|
5
|
+
# if you need to add additional API resource, inherit from APIResource
|
6
|
+
class APIResource < APIObject
|
7
|
+
class << self
|
8
|
+
attr_accessor :client
|
9
|
+
end
|
10
|
+
|
11
|
+
@client = nil
|
12
|
+
|
13
|
+
def self.retrieve(id, params = {})
|
14
|
+
resp = @client.request(:get, "#{self::RESOURCE_PATH}/#{id}", params)
|
15
|
+
Util.convert_to_api_object(resp.data, @client, self)
|
16
|
+
end
|
17
|
+
|
18
|
+
def refresh(params = {})
|
19
|
+
resp = @client.request(:get, "#{self.class::RESOURCE_PATH}/#{self[:id]}", params)
|
20
|
+
initialize_from(resp.data)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PrivacyGate
|
4
|
+
module APIResources
|
5
|
+
module Base
|
6
|
+
# create operations mixin
|
7
|
+
module Create
|
8
|
+
def create(params = {})
|
9
|
+
response = @client.request(:post, "#{self::RESOURCE_PATH}", params)
|
10
|
+
Util.convert_to_api_object(response.data, @client, self)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PrivacyGate
|
4
|
+
module APIResources
|
5
|
+
module Base
|
6
|
+
# delete opertaions mixin
|
7
|
+
module Delete
|
8
|
+
def delete
|
9
|
+
response = @client.request(:delete, "#{self.class::RESOURCE_PATH}/#{self[:id]}")
|
10
|
+
initialize_from(response.data)
|
11
|
+
self
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PrivacyGate
|
4
|
+
module APIResources
|
5
|
+
module Base
|
6
|
+
# list operations mixin
|
7
|
+
module List
|
8
|
+
def list(params = {})
|
9
|
+
resp = @client.request(:get, "#{self::RESOURCE_PATH}", params)
|
10
|
+
Util.convert_to_api_object(resp.data, @client, self)
|
11
|
+
end
|
12
|
+
|
13
|
+
def auto_paging(params = {}, &blk)
|
14
|
+
loop do
|
15
|
+
page = list(params)
|
16
|
+
last_id = page.data.empty? ? nil : page.data.last.id
|
17
|
+
break if last_id.nil?
|
18
|
+
params[:starting_after] = last_id
|
19
|
+
page.data.each(&blk)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PrivacyGate
|
4
|
+
module APIResources
|
5
|
+
module Base
|
6
|
+
# save operations mixin
|
7
|
+
module Save
|
8
|
+
def save
|
9
|
+
values = serialize_params(self)
|
10
|
+
values.delete(:id)
|
11
|
+
resp = @client.request(:put, "#{self.class::RESOURCE_PATH}/#{self[:id]}", self)
|
12
|
+
initialize_from(resp.data)
|
13
|
+
self
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PrivacyGate
|
4
|
+
module APIResources
|
5
|
+
module Base
|
6
|
+
# update operations mixin
|
7
|
+
module Update
|
8
|
+
def modify(id, params = {})
|
9
|
+
resp = @client.request(:put, "#{self::RESOURCE_PATH}/#{id}", params)
|
10
|
+
Util.convert_to_api_object(resp.data, @client, self)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module PrivacyGate
|
2
|
+
module APIResources
|
3
|
+
# Class that allows you to work with Charge resource
|
4
|
+
class Charge < Base::APIResource
|
5
|
+
# class methods
|
6
|
+
extend Base::List
|
7
|
+
extend Base::Create
|
8
|
+
|
9
|
+
# class constants
|
10
|
+
OBJECT_NAME = "charge".freeze
|
11
|
+
RESOURCE_PATH = "charges".freeze
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module PrivacyGate
|
2
|
+
module APIResources
|
3
|
+
# Class that allows you to work with Checkout resource
|
4
|
+
class Checkout < Base::APIResource
|
5
|
+
# class methods
|
6
|
+
extend Base::List
|
7
|
+
extend Base::Create
|
8
|
+
extend Base::Update
|
9
|
+
|
10
|
+
# instance methods
|
11
|
+
include Base::Save
|
12
|
+
include Base::Delete
|
13
|
+
|
14
|
+
# class constants
|
15
|
+
OBJECT_NAME = "checkout".freeze
|
16
|
+
RESOURCE_PATH = "checkouts".freeze
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module PrivacyGate
|
2
|
+
module APIResources
|
3
|
+
# Class that allows you to work with Event resource
|
4
|
+
class Event < Base::APIResource
|
5
|
+
# class methods
|
6
|
+
extend Base::List
|
7
|
+
|
8
|
+
# class constants
|
9
|
+
OBJECT_NAME = "event".freeze
|
10
|
+
RESOURCE_PATH = "events".freeze
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module PrivacyGate
|
2
|
+
class PrivacyGateResponse
|
3
|
+
attr_accessor :data
|
4
|
+
|
5
|
+
attr_accessor :http_body
|
6
|
+
|
7
|
+
attr_accessor :http_headers
|
8
|
+
|
9
|
+
attr_accessor :http_status
|
10
|
+
|
11
|
+
attr_accessor :request_id
|
12
|
+
|
13
|
+
# Initializes a PrivacyGateResponse object
|
14
|
+
# from a Hash like the kind returned as part of a Faraday exception.
|
15
|
+
def self.from_faraday_hash(http_resp)
|
16
|
+
resp = PrivacyGateResponse.new
|
17
|
+
resp.data = JSON.parse(http_resp[:body], symbolize_names: true)
|
18
|
+
resp.http_body = http_resp[:body]
|
19
|
+
resp.http_headers = http_resp[:headers]
|
20
|
+
resp.http_status = http_resp[:status]
|
21
|
+
resp.request_id = http_resp[:headers]["x-request-id"]
|
22
|
+
resp
|
23
|
+
end
|
24
|
+
|
25
|
+
# Initializes a PrivacyGateResponse object
|
26
|
+
# from a Faraday HTTP response object.
|
27
|
+
def self.from_faraday_response(http_resp)
|
28
|
+
resp = PrivacyGateResponse.new
|
29
|
+
resp.data = JSON.parse(http_resp.body, symbolize_names: true)
|
30
|
+
resp.http_body = http_resp.body
|
31
|
+
resp.http_headers = http_resp.headers
|
32
|
+
resp.http_status = http_resp.status
|
33
|
+
resp.request_id = http_resp.headers["x-request-id"]
|
34
|
+
|
35
|
+
# unpack nested data field if it exist
|
36
|
+
if resp.data.is_a? Hash and resp.data.fetch(:data, nil).is_a? Hash
|
37
|
+
resp.data.update(resp.data.delete(:data))
|
38
|
+
end
|
39
|
+
|
40
|
+
# warn in there warnings in response
|
41
|
+
if resp.data.is_a? Hash and resp.data.fetch(:warnings, nil).is_a? Array
|
42
|
+
warn(resp.data[:warnings].first.to_s)
|
43
|
+
end
|
44
|
+
|
45
|
+
resp
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PrivacyGate
|
4
|
+
BASE_API_URL = "https://api.privacygate.io/"
|
5
|
+
API_VERSION = "2018-03-22"
|
6
|
+
|
7
|
+
class Client
|
8
|
+
# API Client for the PrivacyGate API.
|
9
|
+
# Entry point for making requests to the PrivacyGate API.
|
10
|
+
# Full API docs available here: https://api.privacygate.io/docs/
|
11
|
+
|
12
|
+
def initialize(options = {})
|
13
|
+
# set API key and API URL
|
14
|
+
check_api_key!(options[:api_key])
|
15
|
+
@api_key = options[:api_key]
|
16
|
+
@api_uri = URI.parse(options[:api_url] || BASE_API_URL)
|
17
|
+
@api_ver = options[:api_ver] || API_VERSION
|
18
|
+
# create client obj
|
19
|
+
@conn = Faraday.new do |c|
|
20
|
+
c.use Faraday::Request::Multipart
|
21
|
+
c.use Faraday::Request::UrlEncoded
|
22
|
+
c.use Faraday::Response::RaiseError
|
23
|
+
c.adapter Faraday.default_adapter
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Set client-resource relations with all API resources
|
28
|
+
# provide client instance to each resource
|
29
|
+
def charge
|
30
|
+
APIResources::Charge.client = self
|
31
|
+
APIResources::Charge
|
32
|
+
end
|
33
|
+
|
34
|
+
def checkout
|
35
|
+
APIResources::Checkout.client = self
|
36
|
+
APIResources::Checkout
|
37
|
+
end
|
38
|
+
|
39
|
+
def event
|
40
|
+
APIResources::Event.client = self
|
41
|
+
APIResources::Event
|
42
|
+
end
|
43
|
+
|
44
|
+
def api_url(url = "", api_base = nil)
|
45
|
+
(api_base || PrivacyGate::BASE_API_URL) + url
|
46
|
+
end
|
47
|
+
|
48
|
+
def request_headers(api_key)
|
49
|
+
{
|
50
|
+
"User-Agent" => "PrivacyGate/#{PrivacyGate::VERSION}",
|
51
|
+
"Accept" => "application/json",
|
52
|
+
"X-CC-Api-Key" => api_key,
|
53
|
+
"X-CC-Version" => @api_ver,
|
54
|
+
"Content-Type" => "application/json",
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def check_api_key!(api_key)
|
59
|
+
raise AuthenticationError, "No API key provided" unless api_key
|
60
|
+
end
|
61
|
+
|
62
|
+
# request section
|
63
|
+
def request(method, path, params = {})
|
64
|
+
@last_response = nil
|
65
|
+
url = api_url(path, @api_uri)
|
66
|
+
headers = request_headers(@api_key)
|
67
|
+
|
68
|
+
body = nil
|
69
|
+
query_params = nil
|
70
|
+
|
71
|
+
case method.to_s.downcase.to_sym
|
72
|
+
when :get, :head, :delete
|
73
|
+
query_params = params
|
74
|
+
else
|
75
|
+
body = params.to_json
|
76
|
+
end
|
77
|
+
|
78
|
+
u = URI.parse(path)
|
79
|
+
unless u.query.nil?
|
80
|
+
query_params ||= {}
|
81
|
+
query_params = Hash[URI.decode_www_form(u.query)].merge(query_params)
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
http_resp = execute_request_with_rescues(@api_uri) do
|
86
|
+
@conn.run_request(method, url, body, headers) do |req|
|
87
|
+
req.params = query_params unless query_params.nil?
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
begin
|
92
|
+
resp = PrivacyGateResponse.from_faraday_response(http_resp)
|
93
|
+
rescue JSON::ParserError
|
94
|
+
raise Errors.general_api_error(http_resp.status, http_resp.body)
|
95
|
+
end
|
96
|
+
|
97
|
+
@last_response = resp
|
98
|
+
resp
|
99
|
+
end
|
100
|
+
|
101
|
+
# сollect errors during request execution if they occurred
|
102
|
+
def execute_request_with_rescues(api_base)
|
103
|
+
begin
|
104
|
+
resp = yield
|
105
|
+
rescue StandardError => e
|
106
|
+
case e
|
107
|
+
when Faraday::ClientError
|
108
|
+
if e.response
|
109
|
+
Errors.handle_error_response(e.response)
|
110
|
+
else
|
111
|
+
Errors.handle_network_error(e, api_base)
|
112
|
+
end
|
113
|
+
else
|
114
|
+
raise
|
115
|
+
end
|
116
|
+
end
|
117
|
+
resp
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|