bubblez 1.0.0
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/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
|