bubblez 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/FUNDING.yml +1 -0
- data/.gitignore +48 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +77 -0
- data/CONTRIBUTING.md +31 -0
- data/Gemfile +4 -0
- data/LICENSE +373 -0
- data/README.md +78 -0
- data/Rakefile +5 -0
- data/bubblez.gemspec +46 -0
- data/lib/bubblez/config.rb +353 -0
- data/lib/bubblez/endpoint.rb +286 -0
- data/lib/bubblez/rest_client_resources.rb +489 -0
- data/lib/bubblez/rest_environment.rb +67 -0
- data/lib/bubblez/version.rb +24 -0
- data/lib/bubblez.rb +28 -0
- data/lib/tasks/coverage.rake +22 -0
- data/lib/tasks/spec.rake +5 -0
- metadata +223 -0
data/bubblez.gemspec
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'bubblez/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = Bubblez::VersionInformation.package_name
|
8
|
+
spec.version = Bubblez::VersionInformation.version_name
|
9
|
+
spec.date = Date.today.strftime("%Y-%m-%d")
|
10
|
+
spec.summary = 'Bubblez REST Client'
|
11
|
+
spec.homepage = 'https://github.com/FoamFactory/bubblez'
|
12
|
+
spec.authors = ['Scott Johnson']
|
13
|
+
spec.email = 'jaywir3@gmail.com'
|
14
|
+
spec.files = %w(lib/bubblez.rb lib/bubblez/rest_environment.rb lib/bubblez/version.rb)
|
15
|
+
spec.license = 'MPL-2.0'
|
16
|
+
spec.summary = %q{A gem for easily defining client REST interfaces in Ruby}
|
17
|
+
spec.description = %q{Retrofit, by Square, allows you to easily define annoations that will generate the necessary boilerplate code for your REST interfaces. Bubblez is a Gem that seeks to bring a similar style of boilerplate generation to Ruby.}
|
18
|
+
|
19
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
20
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
21
|
+
if spec.respond_to?(:metadata)
|
22
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
23
|
+
else
|
24
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
25
|
+
"public gem pushes."
|
26
|
+
end
|
27
|
+
|
28
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
29
|
+
f.match(%r{^(test|spec|features)/})
|
30
|
+
end
|
31
|
+
spec.bindir = "exe"
|
32
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
33
|
+
spec.require_paths = ["lib"]
|
34
|
+
|
35
|
+
spec.add_development_dependency "bundler", "~> 2.2.26"
|
36
|
+
spec.add_development_dependency 'rake', '~> 12.3', '>= 12.3.3'
|
37
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
38
|
+
spec.add_development_dependency "minitest-reporters", "~> 1.1"
|
39
|
+
spec.add_development_dependency "simplecov", "~> 0.16"
|
40
|
+
spec.add_development_dependency "webmock", "~> 3.5"
|
41
|
+
spec.add_development_dependency "vcr", "~> 3.0"
|
42
|
+
spec.add_development_dependency "rspec", "~> 3.8"
|
43
|
+
spec.add_development_dependency "os", "~> 1.1.4"
|
44
|
+
spec.add_dependency "addressable", "~> 2.5"
|
45
|
+
spec.add_dependency "rest-client", "~> 2.0"
|
46
|
+
end
|
@@ -0,0 +1,353 @@
|
|
1
|
+
require 'bubblez/rest_environment'
|
2
|
+
require 'bubblez/endpoint'
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
module Bubblez
|
6
|
+
class << self
|
7
|
+
attr_writer :configuration
|
8
|
+
end
|
9
|
+
|
10
|
+
##
|
11
|
+
# Configure the Bubblez instance.
|
12
|
+
#
|
13
|
+
# Use this method if you want to configure the Bubblez instance, typically during initialization of your Gem or
|
14
|
+
# application.
|
15
|
+
#
|
16
|
+
# @example In app/config/initializers/bubblez.rb
|
17
|
+
# Bubblez.configure do |config|
|
18
|
+
# config.endpoints = [
|
19
|
+
# {
|
20
|
+
# :type => :get,
|
21
|
+
# :location => :version,
|
22
|
+
# :authenticated => false,
|
23
|
+
# :api_key_required => false
|
24
|
+
# }
|
25
|
+
# ]
|
26
|
+
# end
|
27
|
+
def self.configure
|
28
|
+
yield(configuration)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.configuration
|
32
|
+
@configuration ||= Configuration.new
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# The configuration of the Bubblez rest client.
|
37
|
+
#
|
38
|
+
# Use this class if you want to retrieve configuration values set during initialization.
|
39
|
+
#
|
40
|
+
class Configuration
|
41
|
+
def initialize
|
42
|
+
@environments = Hash.new
|
43
|
+
@endpoints = Hash.new
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# Retrieve the {RestEnvironment} object defined as part of this Configuration having a specified name.
|
48
|
+
#
|
49
|
+
# @param [String] environment_name The name of the {RestEnvironment} to retrieve.
|
50
|
+
#
|
51
|
+
# The +environment_name+ is +nil+ by default, which will return the default configuration, if only one exists.
|
52
|
+
#
|
53
|
+
# @return [RestEnvironment] A new +RestEnvironment+ having the configuration that was created with key
|
54
|
+
# +environment_name+. Note that +RestEnvironment+s are essentially immutable once they are created, so
|
55
|
+
# an existing object will _never_ be returned.
|
56
|
+
#
|
57
|
+
def environment(environment_name = nil)
|
58
|
+
if environment_name.nil?
|
59
|
+
if @environments.length > 1
|
60
|
+
raise 'You must specify an environment_name parameter because more than one environment is defined'
|
61
|
+
end
|
62
|
+
|
63
|
+
env_hash = @environments[nil]
|
64
|
+
else
|
65
|
+
env_hash = @environments[environment_name]
|
66
|
+
end
|
67
|
+
|
68
|
+
if env_hash.nil?
|
69
|
+
if environment_name.nil?
|
70
|
+
raise 'No default environment specified'
|
71
|
+
end
|
72
|
+
|
73
|
+
raise 'No environment specified having name {}', environment_name
|
74
|
+
end
|
75
|
+
|
76
|
+
RestEnvironment.new(env_hash[:scheme], env_hash[:host], env_hash[:port], env_hash[:api_key],
|
77
|
+
env_hash[:api_key_name])
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Set the environments that can be used.
|
82
|
+
#
|
83
|
+
# @param [Array] environments The environments, as an array with each entry a +Hash+.
|
84
|
+
#
|
85
|
+
# One or more environments may be specified, but if more than one environment is specified, it is required that each
|
86
|
+
# environment have a +:environment_name:+ parameter to differentiate it from other environments.
|
87
|
+
#
|
88
|
+
# @example In app/config/environments/staging.rb:
|
89
|
+
# Bubblez.configure do |config|
|
90
|
+
# config.environments = [{
|
91
|
+
# :scheme => 'https',
|
92
|
+
# :host => 'stage.api.somehost.com',
|
93
|
+
# :port => '443',
|
94
|
+
# :api_key => 'something',
|
95
|
+
# :api_key_name => 'X-API-Key' # Optional
|
96
|
+
# }]
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
def environments=(environments)
|
100
|
+
default = nil
|
101
|
+
environments.each do |environment|
|
102
|
+
if environments.length > 1 && environment[:environment_name].nil?
|
103
|
+
message = 'More than one environment was specified and at least one of the environments does not have an ' \
|
104
|
+
':environment_name field. Verify all environments have an :environment_name.'
|
105
|
+
|
106
|
+
raise message
|
107
|
+
end
|
108
|
+
|
109
|
+
@environments = {}
|
110
|
+
env_api_key = 'X-API-Key'
|
111
|
+
env_api_key = environment[:api_key_name] if environment.key? :api_key_name
|
112
|
+
|
113
|
+
@environments[environment[:environment_name]] = {
|
114
|
+
scheme: environment[:scheme],
|
115
|
+
host: environment[:host],
|
116
|
+
port: environment[:port],
|
117
|
+
api_key: environment[:api_key],
|
118
|
+
api_key_name: env_api_key
|
119
|
+
}
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
##
|
124
|
+
# Retrieve the list of +Endpoint+s configured in this +Configuration+ object.
|
125
|
+
#
|
126
|
+
# @return {Array} An Array of {Endpoint}s.
|
127
|
+
#
|
128
|
+
def endpoints
|
129
|
+
@endpoints
|
130
|
+
end
|
131
|
+
|
132
|
+
##
|
133
|
+
# Add all {Endpoint} objects within this {Configuration} instance.
|
134
|
+
#
|
135
|
+
# {Endpoint} objects are defined using two required parameters: type and location, and three optional parameters:
|
136
|
+
# authenticated, api_key_required and name.
|
137
|
+
# - method: Indicates the HTTP method used to access the endpoint. Must be one of {Endpoint::METHODS}.
|
138
|
+
# - location: Indicates the path at which the {Endpoint} can be accessed on the host environment.
|
139
|
+
# - authenticated: (Optional) A true or false value indicating whether the {Endpoint} requires an authorization
|
140
|
+
# token to access it. Defaults to false.
|
141
|
+
# - api_key_required: (Optional) A true or false value indicating whether the {Endpoint} requires a API key to
|
142
|
+
# access it. Defaults to false.
|
143
|
+
# - name: (Optional): A +String+ indicating the name of the method to add. If not provided, the method name will
|
144
|
+
# be the same as the +location+.
|
145
|
+
#
|
146
|
+
def endpoints=(endpoints)
|
147
|
+
new_endpoints = Hash.new
|
148
|
+
endpoints.each do |ep|
|
149
|
+
endpoint_object = Endpoint.new ep[:method], ep[:location].to_s, ep[:authenticated], ep[:api_key_required], ep[:name], ep[:return_type], ep[:encode_authorization], ep[:headers]
|
150
|
+
|
151
|
+
new_endpoints[endpoint_object.get_key_string] = endpoint_object
|
152
|
+
end
|
153
|
+
|
154
|
+
@endpoints = new_endpoints
|
155
|
+
|
156
|
+
# Define all of the endpoints as methods on RestEnvironment
|
157
|
+
@endpoints.values.each do |endpoint|
|
158
|
+
if endpoint.name != nil
|
159
|
+
endpoint_name_as_sym = endpoint.name.to_sym
|
160
|
+
else
|
161
|
+
endpoint_name_as_sym = endpoint.get_location_string.to_sym
|
162
|
+
end
|
163
|
+
|
164
|
+
if Bubblez::RestEnvironment.instance_methods(false).include?(endpoint_name_as_sym)
|
165
|
+
Bubblez::RestEnvironment.class_exec do
|
166
|
+
remove_method endpoint_name_as_sym
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
if endpoint.method == :get
|
171
|
+
if endpoint.authenticated?
|
172
|
+
Bubblez::RestEnvironment.class_exec do
|
173
|
+
if endpoint.has_uri_params?
|
174
|
+
if endpoint.encode_authorization_header?
|
175
|
+
define_method(endpoint_name_as_sym) do |username, password, uri_params|
|
176
|
+
login_data = {
|
177
|
+
:login => username,
|
178
|
+
:password => password
|
179
|
+
}
|
180
|
+
auth_value = RestClientResources.get_encoded_authorization(endpoint, login_data)
|
181
|
+
RestClientResources.execute_get_authenticated self, endpoint, :basic, auth_value, uri_params, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
182
|
+
end
|
183
|
+
else
|
184
|
+
define_method(endpoint_name_as_sym) do |auth_token, uri_params|
|
185
|
+
RestClientResources.execute_get_authenticated self, endpoint, :bearer, auth_token, uri_params, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
186
|
+
end
|
187
|
+
end
|
188
|
+
else
|
189
|
+
if endpoint.encode_authorization_header?
|
190
|
+
define_method(endpoint_name_as_sym) do |username, password|
|
191
|
+
login_data = {
|
192
|
+
:username => username,
|
193
|
+
:password => password
|
194
|
+
}
|
195
|
+
auth_value = RestClientResources.get_encoded_authorization(endpoint, login_data)
|
196
|
+
|
197
|
+
RestClientResources.execute_get_authenticated self, endpoint, :basic, auth_value, {}, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
198
|
+
end
|
199
|
+
else
|
200
|
+
define_method(endpoint_name_as_sym) do |auth_token|
|
201
|
+
RestClientResources.execute_get_authenticated self, endpoint, :bearer, auth_token, {}, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
else
|
207
|
+
Bubblez::RestEnvironment.class_exec do
|
208
|
+
if endpoint.has_uri_params?
|
209
|
+
define_method(endpoint_name_as_sym) do |uri_params|
|
210
|
+
RestClientResources.execute_get_unauthenticated self, endpoint, uri_params, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
211
|
+
end
|
212
|
+
else
|
213
|
+
define_method(endpoint_name_as_sym) do
|
214
|
+
RestClientResources.execute_get_unauthenticated self, endpoint, {}, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
elsif endpoint.method == :post
|
220
|
+
if endpoint.authenticated? and !endpoint.encode_authorization_header?
|
221
|
+
Bubblez::RestEnvironment.class_exec do
|
222
|
+
define_method(endpoint_name_as_sym) do |auth_token, data|
|
223
|
+
RestClientResources.execute_post_authenticated self, endpoint, :bearer, auth_token, data, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
224
|
+
end
|
225
|
+
end
|
226
|
+
elsif endpoint.encode_authorization_header?
|
227
|
+
Bubblez::RestEnvironment.class_exec do
|
228
|
+
define_method(endpoint_name_as_sym) do |username, password, data = {}|
|
229
|
+
login_data = {
|
230
|
+
:username => username,
|
231
|
+
:password => password
|
232
|
+
}
|
233
|
+
|
234
|
+
auth_value = RestClientResources.get_encoded_authorization(endpoint, login_data)
|
235
|
+
# composite_headers = RestClientResources.build_composite_headers(endpoint.additional_headers, {
|
236
|
+
# Authorization: 'Basic ' + Base64.strict_encode64(auth_value)
|
237
|
+
# })
|
238
|
+
RestClientResources.execute_post_authenticated self, endpoint, :basic, auth_value, data, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
239
|
+
end
|
240
|
+
end
|
241
|
+
else
|
242
|
+
Bubblez::RestEnvironment.class_exec do
|
243
|
+
define_method(endpoint_name_as_sym) do |data|
|
244
|
+
composite_headers = endpoint.additional_headers
|
245
|
+
RestClientResources.execute_post_unauthenticated self, endpoint, data, composite_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
elsif endpoint.method == :delete
|
250
|
+
if endpoint.has_uri_params?
|
251
|
+
if endpoint.authenticated?
|
252
|
+
Bubblez::RestEnvironment.class_exec do
|
253
|
+
define_method(endpoint_name_as_sym) do |auth_token, uri_params|
|
254
|
+
RestClientResources.execute_delete_authenticated self, endpoint, auth_token, uri_params, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
255
|
+
end
|
256
|
+
end
|
257
|
+
else
|
258
|
+
Bubblez::RestEnvironment.class_exec do
|
259
|
+
define_method(endpoint_name_as_sym) do |uri_params|
|
260
|
+
RestClientResources.execute_delete_unauthenticated self, endpoint, uri_params, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
else
|
265
|
+
# XXX_jwir3: While MDN states that DELETE requests with a body are allowed, it seems that a number of
|
266
|
+
# documentation sites discourage its use. Thus, it's possible that, depending on the server API
|
267
|
+
# framework, the DELETE request could be rejected. In addition, RestClient doesn't seem to support DELETE
|
268
|
+
# requests with a body, so we're a bit stuck on this one, even if we wanted to support it.
|
269
|
+
raise 'DELETE requests without URI parameters are not allowed'
|
270
|
+
end
|
271
|
+
elsif endpoint.method == :patch
|
272
|
+
if endpoint.authenticated?
|
273
|
+
Bubblez::RestEnvironment.class_exec do
|
274
|
+
if endpoint.has_uri_params?
|
275
|
+
define_method(endpoint_name_as_sym) do |auth_token, uri_params, data|
|
276
|
+
RestClientResources.execute_patch_authenticated self, endpoint, auth_token, uri_params, data, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
277
|
+
end
|
278
|
+
else
|
279
|
+
define_method(endpoint_name_as_sym) do |auth_token, data|
|
280
|
+
RestClientResources.execute_patch_authenticated self, endpoint, auth_token, {}, data, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
else
|
285
|
+
Bubblez::RestEnvironment.class_exec do
|
286
|
+
if endpoint.has_uri_params?
|
287
|
+
define_method(endpoint_name_as_sym) do |uri_params, data|
|
288
|
+
RestClientResources.execute_patch_unauthenticated self, endpoint, uri_params, data, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
289
|
+
end
|
290
|
+
else
|
291
|
+
define_method(endpoint_name_as_sym) do |data|
|
292
|
+
RestClientResources.execute_patch_unauthenticated self, endpoint, {}, data, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
elsif endpoint.method == :put
|
298
|
+
if endpoint.authenticated?
|
299
|
+
Bubblez::RestEnvironment.class_exec do
|
300
|
+
if endpoint.has_uri_params?
|
301
|
+
define_method(endpoint_name_as_sym) do |auth_token, uri_params, data|
|
302
|
+
RestClientResources.execute_put_authenticated self, endpoint, auth_token, uri_params, data, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
303
|
+
end
|
304
|
+
else
|
305
|
+
define_method(endpoint_name_as_sym) do |auth_token, data|
|
306
|
+
RestClientResources.execute_put_authenticated self, endpoint, auth_token, {}, data, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
else
|
311
|
+
Bubblez::RestEnvironment.class_exec do
|
312
|
+
if endpoint.has_uri_params?
|
313
|
+
define_method(endpoint_name_as_sym) do |uri_params, data|
|
314
|
+
RestClientResources.execute_put_unauthenticated self, endpoint, uri_params, data, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
315
|
+
end
|
316
|
+
else
|
317
|
+
define_method(endpoint_name_as_sym) do |data|
|
318
|
+
RestClientResources.execute_put_unauthenticated self, endpoint, {}, data, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
elsif endpoint.method == :head
|
324
|
+
if endpoint.authenticated?
|
325
|
+
Bubblez::RestEnvironment.class_exec do
|
326
|
+
if endpoint.has_uri_params?
|
327
|
+
define_method(endpoint_name_as_sym) do |auth_token, uri_params|
|
328
|
+
RestClientResources.execute_head_authenticated self, endpoint, auth_token, uri_params, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
329
|
+
end
|
330
|
+
else
|
331
|
+
define_method(endpoint_name_as_sym) do |auth_token|
|
332
|
+
RestClientResources.execute_head_authenticated self, endpoint, auth_token, {}, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
else
|
337
|
+
Bubblez::RestEnvironment.class_exec do
|
338
|
+
if endpoint.has_uri_params?
|
339
|
+
define_method(endpoint_name_as_sym) do |uri_params|
|
340
|
+
RestClientResources.execute_head_unauthenticated self, endpoint, uri_params, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
341
|
+
end
|
342
|
+
else
|
343
|
+
define_method(endpoint_name_as_sym) do
|
344
|
+
RestClientResources.execute_head_unauthenticated self, endpoint, {}, endpoint.additional_headers, self.get_api_key_if_needed(endpoint), self.api_key_name
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
@@ -0,0 +1,286 @@
|
|
1
|
+
require 'addressable/template'
|
2
|
+
|
3
|
+
module Bubblez
|
4
|
+
##
|
5
|
+
# Representation of a single API endpoint within the Bubblez infrastructure.
|
6
|
+
#
|
7
|
+
# In order to access an API Endpoint, an {RestEnvironment} must also be provided. This class is an abstract
|
8
|
+
# representation of an +Endpoint+, without any information provided as part of the Environment. In other words, an
|
9
|
+
# Endpoint can be used with any +RestEnvironment+.
|
10
|
+
#
|
11
|
+
class Endpoint
|
12
|
+
##
|
13
|
+
# Controls the method used to access the endpoint. Must be one of {Endpoint::Methods}.
|
14
|
+
# @return [Symbol] the method used to access the endpoint. Will always be one of the symbols defined in
|
15
|
+
# {Endpoint::METHODS}.
|
16
|
+
attr_accessor :method
|
17
|
+
|
18
|
+
##
|
19
|
+
# Controls the location, relative to the web root of the host, used to access the endpoint.
|
20
|
+
# @return [String] the location relative to the web root of the host used to access the endpoint
|
21
|
+
attr_accessor :location
|
22
|
+
|
23
|
+
##
|
24
|
+
# Controls whether authentication is required to access this endpoint. Defaults to false.
|
25
|
+
# @return [Boolean] true, if authentication is required to access this endpoint; false, otherwise.
|
26
|
+
attr_accessor :authentication_required
|
27
|
+
|
28
|
+
##
|
29
|
+
# Controls whether an API key is required to access this endpoint. Defaults to false.
|
30
|
+
# @return [Boolean] true, if an API key is required to access this endpoint; false, otherwise.
|
31
|
+
attr_accessor :api_key_required
|
32
|
+
|
33
|
+
##
|
34
|
+
# Controls what type of object will be returned from the REST API call on success. Must be one of the symbols
|
35
|
+
# defined in {Endpoint::RETURN_TYPES}.
|
36
|
+
#
|
37
|
+
attr_accessor :return_type
|
38
|
+
|
39
|
+
##
|
40
|
+
# Controls which data values should be encoded as part of an Authorization header. They will be separated with a
|
41
|
+
# colon in the order they are received and Base64-encoded.
|
42
|
+
# @return [Array] An array of +Symbol+s specifying which of the data attributes should be Base64-encoded as part of
|
43
|
+
# an Authorization header. The values will be encoded in the order they are received.
|
44
|
+
attr_accessor :encode_authorization
|
45
|
+
|
46
|
+
##
|
47
|
+
# An array of parameters that are specified on the URI of this endpoint for each call.
|
48
|
+
attr_accessor :uri_params
|
49
|
+
|
50
|
+
## A template for specifying the complete URL for endpoints.
|
51
|
+
API_URL = ::Addressable::Template.new("{scheme}://{host}/{endpoint}")
|
52
|
+
|
53
|
+
## A template for specifying the complete URL for endpoints, with a port attached to the host.
|
54
|
+
API_URL_WITH_PORT = ::Addressable::Template.new("{scheme}://{host}:{port}/{endpoint}")
|
55
|
+
|
56
|
+
## The HTTP methods supported by a rest client utilizing Bubblez.
|
57
|
+
METHODS = %w[get post patch put delete head].freeze
|
58
|
+
|
59
|
+
## The possible return types for successful REST calls. Defaults to :body_as_string.
|
60
|
+
RETURN_TYPES = %w[full_response body_as_string body_as_object].freeze
|
61
|
+
|
62
|
+
##
|
63
|
+
# Construct a new instance of an Endpoint.
|
64
|
+
#
|
65
|
+
# @param [Symbol] method The type of the new Endpoint to create. Must be one of the methods in
|
66
|
+
# {Endpoint::METHODS}.
|
67
|
+
# @param [String] location The location, relative to the root of the host, at which the endpoint resides.
|
68
|
+
# @param [Boolean] auth_required If true, then authorization/authentication is required to access this endpoint.
|
69
|
+
# Defaults to +false+.
|
70
|
+
# @param [Boolean] api_key_required If true, then an API key is required to access this endpoint. Defaults to
|
71
|
+
# +false+.
|
72
|
+
# @param [String] name An optional name which will be given to the method that will execute this {Endpoint} within
|
73
|
+
# the context of a {RestClientResources} object.
|
74
|
+
# @param [Array<Symbol>] encode_authorization Parameters that should be treated as authorization parameters and
|
75
|
+
# encoded using a Base64 encoding.
|
76
|
+
#
|
77
|
+
def initialize(method, location, auth_required = false, api_key_required = false, name = nil, return_type = :body_as_string, encode_authorization = {}, headers = {})
|
78
|
+
@method = method
|
79
|
+
@location = location
|
80
|
+
@auth_required = auth_required
|
81
|
+
@api_key_required = api_key_required
|
82
|
+
@name = name
|
83
|
+
@encode_authorization = encode_authorization
|
84
|
+
@additional_headers = headers
|
85
|
+
|
86
|
+
unless Endpoint::RETURN_TYPES.include? return_type.to_s
|
87
|
+
return_type = :body_as_string
|
88
|
+
end
|
89
|
+
|
90
|
+
@return_type = return_type
|
91
|
+
|
92
|
+
@uri_params = []
|
93
|
+
|
94
|
+
# Strip the leading slash from the endpoint location, if it's there
|
95
|
+
if @location.to_s[0] == '/'
|
96
|
+
@location = @location.to_s.slice(1, @location.to_s.length)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Extract URI parameters and create symbols for them
|
100
|
+
# URI parameters are enclosed by curly braces '{' and '}'
|
101
|
+
@location.to_s.split('/').each do |uri_segment|
|
102
|
+
|
103
|
+
match_data = /\{(.*)\}/.match(uri_segment)
|
104
|
+
unless match_data == nil
|
105
|
+
@uri_params.push(match_data[1].to_sym)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
##
|
111
|
+
# Retrieve a +String+ that will identify this +Endpoint+ uniquely within a hash table.
|
112
|
+
#
|
113
|
+
# @return [String] A unique identifier for this Endpoint, including its method (get/post/put/etc..), location, whether or not it is authenticated, and whether it needs an API key to successfully execute.
|
114
|
+
#
|
115
|
+
def get_key_string
|
116
|
+
auth_string = '-unauthenticated'
|
117
|
+
if @auth_required
|
118
|
+
auth_string = '-authenticated'
|
119
|
+
end
|
120
|
+
|
121
|
+
api_key_string = ''
|
122
|
+
if @api_key_required
|
123
|
+
api_key_string = '-with-api-key'
|
124
|
+
end
|
125
|
+
|
126
|
+
method.to_s + "-" + @location.to_s + auth_string + api_key_string
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# Retrieve the base URL template for this +Endpoint+, given a +RestEnvironment+.
|
131
|
+
#
|
132
|
+
# @param [RestEnvironment] env The +RestEnvironment+ to use to access this endpoint.
|
133
|
+
#
|
134
|
+
# @return [Addressable::Template] A +Template+ containing the URL to use to access this +Endpoint+.
|
135
|
+
#
|
136
|
+
def get_base_url(env)
|
137
|
+
unless env.port == 80 || env.port == 443
|
138
|
+
return API_URL_WITH_PORT
|
139
|
+
end
|
140
|
+
|
141
|
+
API_URL
|
142
|
+
end
|
143
|
+
|
144
|
+
##
|
145
|
+
# Retrieve the URL to access this +Endpoint+, as a +String+ with all parameters expanded.
|
146
|
+
#
|
147
|
+
# @param [RestEnvironment] env The +RestEnvironment+ to use to access this +Endpoint+.
|
148
|
+
#
|
149
|
+
# @return [Addressable::URI] An +Addressable::URI+ containing the full URL to access this +Endpoint+ on the given
|
150
|
+
# +RestEnvironment+.
|
151
|
+
#
|
152
|
+
def get_expanded_url(env, uri_params = {})
|
153
|
+
url = get_base_url env
|
154
|
+
|
155
|
+
if is_complex?
|
156
|
+
special_url_string = '{scheme}://{host}/'
|
157
|
+
unless @port == 80 || @port == 443
|
158
|
+
special_url_string = '{scheme}://{host}:{port}/'
|
159
|
+
end
|
160
|
+
|
161
|
+
special_url_string = special_url_string + @location
|
162
|
+
|
163
|
+
uri_params.each do |param, value|
|
164
|
+
needle = "{#{param.to_s}}"
|
165
|
+
special_url_string = special_url_string.sub(needle, value.to_s)
|
166
|
+
end
|
167
|
+
|
168
|
+
url = ::Addressable::Template.new(special_url_string)
|
169
|
+
|
170
|
+
return url.expand(scheme: env.scheme, host: env.host, port: env.port)
|
171
|
+
end
|
172
|
+
|
173
|
+
url.expand(scheme: env.scheme, host: env.host, port: env.port, endpoint: @location)
|
174
|
+
end
|
175
|
+
|
176
|
+
##
|
177
|
+
# Determine if the location for this Endpoint is complex.
|
178
|
+
#
|
179
|
+
# @return [Boolean] true, if the location for this Endpoint is complex (contains a '/'); false, otherwise.
|
180
|
+
def is_complex?
|
181
|
+
@location.include? '/'
|
182
|
+
end
|
183
|
+
|
184
|
+
##
|
185
|
+
# Retrieve a String representing the location of this Endpoint.
|
186
|
+
#
|
187
|
+
# Complex Endpoints will have instances of '/' replaced with '_'.
|
188
|
+
#
|
189
|
+
# @return [String] The string representation of the location of this endpoint.
|
190
|
+
def get_location_string
|
191
|
+
unless is_complex?
|
192
|
+
return @location
|
193
|
+
end
|
194
|
+
|
195
|
+
@location.to_s.gsub('/', '_')
|
196
|
+
end
|
197
|
+
|
198
|
+
##
|
199
|
+
# Determine if this +Endpoint+ requires authentication/authorization to utilize
|
200
|
+
#
|
201
|
+
# @return [Boolean] true, if this +Endpoint+ requires authentication/authorization to use; false, otherwise.
|
202
|
+
def authenticated?
|
203
|
+
@auth_required
|
204
|
+
end
|
205
|
+
|
206
|
+
##
|
207
|
+
# Determine if an API key is required
|
208
|
+
#
|
209
|
+
# @return [Boolean] true, if an API key is required to make the request; false, otherwise.
|
210
|
+
def api_key_required?
|
211
|
+
api_key_required
|
212
|
+
end
|
213
|
+
|
214
|
+
##
|
215
|
+
# Set the name of the method on {RestClientResources} used to access this {Endpoint}.
|
216
|
+
#
|
217
|
+
# @param [String] name The name of the method used to access this {Endpoint}.
|
218
|
+
#
|
219
|
+
def name=(name)
|
220
|
+
@name = name
|
221
|
+
end
|
222
|
+
|
223
|
+
##
|
224
|
+
# Retrieve the name of the method on {RestClientResources} used to access this {Endpoint}.
|
225
|
+
#
|
226
|
+
# @return [String] A String containing the name of the method on {RestClientResources} used to access this
|
227
|
+
# {Endpoint}, or +nil+ if one wasn't provided.
|
228
|
+
#
|
229
|
+
def name
|
230
|
+
@name
|
231
|
+
end
|
232
|
+
|
233
|
+
##
|
234
|
+
# Determine if this {Endpoint} has a method name, different from the +location+ name, specified for it.
|
235
|
+
#
|
236
|
+
# @return [Boolean] true, if this {Endpoint} has a method name that is different than the +location+ name specified
|
237
|
+
# for the +Endpoint+, to be defined on {RestClientResources}; false, otherwise.
|
238
|
+
#
|
239
|
+
def name?
|
240
|
+
@name != nil
|
241
|
+
end
|
242
|
+
|
243
|
+
##
|
244
|
+
# Whether or not an Authorization header should be Base64-encoded.
|
245
|
+
#
|
246
|
+
# @return [Boolean] true, if attributes from the data array have been specified to be Base64-encoded as part of an
|
247
|
+
# Authorization header; false, otherwise.
|
248
|
+
#
|
249
|
+
def encode_authorization_header?
|
250
|
+
!@encode_authorization.nil? and @encode_authorization.length > 0
|
251
|
+
end
|
252
|
+
|
253
|
+
##
|
254
|
+
# Retrieve the return type of this REST endpoint.
|
255
|
+
#
|
256
|
+
# This will always be one of:
|
257
|
+
#
|
258
|
+
# - +full_response+ : Indicates that the full +Response+ object should be returned so that headers and
|
259
|
+
# return code can be used.
|
260
|
+
# - +body_as_object+ : Indicates that the body of the +Response+ should be parsed as a full +OpenStruct+
|
261
|
+
# object and returned.
|
262
|
+
# - +body_as_string+ : Indicates that only the body of the +Response+ object should be returned, as a +String+.
|
263
|
+
#
|
264
|
+
# By default, if this is not specified, it will be +body_as+string+.
|
265
|
+
#
|
266
|
+
def return_type
|
267
|
+
@return_type
|
268
|
+
end
|
269
|
+
|
270
|
+
def has_uri_params?
|
271
|
+
!@uri_params.empty?
|
272
|
+
end
|
273
|
+
|
274
|
+
def additional_headers
|
275
|
+
unless @additional_headers
|
276
|
+
@additional_headers = {}
|
277
|
+
end
|
278
|
+
|
279
|
+
@additional_headers
|
280
|
+
end
|
281
|
+
|
282
|
+
def has_additional_headers?
|
283
|
+
not additional_headers.empty?
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|