bubbles-rest-client 0.0.8 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +155 -0
- data/bubbles.gemspec +1 -1
- data/lib/bubbles/config.rb +169 -7
- data/lib/bubbles/endpoint.rb +71 -9
- data/lib/bubbles/rest_client_resources.rb +239 -33
- data/lib/bubbles/rest_environment.rb +7 -48
- data/lib/bubbles/version.rb +1 -1
- metadata +3 -5
- data/fixtures/vcr_cassettes/get_students_authenticated.yml +0 -46
- data/fixtures/vcr_cassettes/get_version_unauthenticated.yml +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3db39174193ea887659c8e1d859048526b0c5335
|
4
|
+
data.tar.gz: 8254a56110041009e0e03c32aae4b1a1de08b914
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c0f6e722b868cf82d60f0a12e17b3ea24cc59f65ea756e93bf07d906670b49dd700a44f2f2631b0d1ef6ff264bfa0c13d1c74dfaf2e26e7944f1d3ae175924e
|
7
|
+
data.tar.gz: 3211a7eda8805bdba2678c551f73dd8212d188ce371a4a3a526010b8a6507071f5ba41d41ef93a253afa37bf99b5a3f955f9098f963d0b3f83dfbc79078744a7
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -22,3 +22,158 @@ GitHubService service = retrofit.create(GitHubService.class);
|
|
22
22
|
What this does is allow you to focus on your _handling_ of the REST responses, rather than worrying about the boilerplate code required to set up the client side of the REST API.
|
23
23
|
|
24
24
|
_bubbles_ is a Gem that seeks to provide this same behavior.
|
25
|
+
|
26
|
+
## :warning: Limitations
|
27
|
+
**Please read this section before using!**
|
28
|
+
|
29
|
+
Currently, bubbles has a number of limitations that make it likely not suitable for use in a production environment. Each of these is tracked by an issue on our [issues page](https://github.com/FoamFactory/bubbles/issues).
|
30
|
+
|
31
|
+
- Passing an API key with a request is restricted to using `X-Api-Key` as a header key (#10).
|
32
|
+
- Some request methods (`PATCH`, `PUT`, `DELETE`) do not currently allow unauthenticated access. In other words, it is not possible to perform a `DELETE` request on your API without passing an authorization token. (#9)
|
33
|
+
- Only three types of environment are currently allowed. Only `local`, `staging` and `production` are recognized as possible environments to connect to for the purposes of using bubbles. You can add any scheme, host, and port to any of these, but the names are currently hardcoded, as is the number of possible hosts. (#4)
|
34
|
+
- Not all possible combinations of `has_uri_params`, `authenticated`, and `api_key_required` are tested. In some cases, such as with `GET` requests, there aren't any tests for possible configuration cases that might cause issues when combined. (#12)
|
35
|
+
- Usage of the `expect_json` configuration parameter is not intuitive. You should think of this as more of a "I want an object back from the API", rather than how it's worded. (#11)
|
36
|
+
|
37
|
+
If you're interested in working on any of the issues above, please feel free to submit a pull request and a member of our team will review that pull request within a couple of days.
|
38
|
+
|
39
|
+
## Usage
|
40
|
+
If you're using Rails, it's suggested to have a `config/initializers/bubbles.rb` configuration file where you can easily configure your endpoints and environments. If you're not using Rails, then you can put this configuration just about anywhere, provided it's executed before where you want to use it.
|
41
|
+
|
42
|
+
## Quickstart
|
43
|
+
In `config/initializers/bubbles.rb`, add the following:
|
44
|
+
```ruby
|
45
|
+
require 'bubbles'
|
46
|
+
|
47
|
+
Bubbles.configure do |config|
|
48
|
+
config.endpoints = [
|
49
|
+
{
|
50
|
+
:method => :get,
|
51
|
+
:location => :version,
|
52
|
+
:authenticated => false,
|
53
|
+
:api_key_required => false
|
54
|
+
}
|
55
|
+
]
|
56
|
+
|
57
|
+
config.local_environment = {
|
58
|
+
:scheme => 'http',
|
59
|
+
:host => '0.0.0.0',
|
60
|
+
:port => '1234'
|
61
|
+
}
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
The `config.endpoints` section is where you configure which endpoints you want to support. The `config.local_environment` defines an environment, or remote configuration, for accessing the endpoint on a specific remote destination.
|
66
|
+
|
67
|
+
Now, you can use this endpoint with:
|
68
|
+
```ruby
|
69
|
+
require 'bubbles'
|
70
|
+
|
71
|
+
...
|
72
|
+
|
73
|
+
def version
|
74
|
+
resources = Bubbles::Resources.new
|
75
|
+
|
76
|
+
# The following will make a GET request to
|
77
|
+
# http://0.0.0.0:1234/version and return the result.
|
78
|
+
result = resources.local_environment.version
|
79
|
+
|
80
|
+
puts(result)
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
## Detailed Documentation
|
85
|
+
There are currently two parts to a bubbles configuration: the _environments_ and the _endpoints_. Bubbles is configured in a _bubbles configuration block_:
|
86
|
+
```ruby
|
87
|
+
Bubbles.configure do |config|
|
88
|
+
...
|
89
|
+
end
|
90
|
+
```
|
91
|
+
|
92
|
+
This configuration block can be run at any time, but is typically set up in the initializer section of an app's startup. If desired, configuration can happen separately. That is, you can initialize environments within your initializer file and then initialize endpoints within another section of the application. Just note that when endpoints are defined, it overwrites _all_ endpoints of a configuration, not just the ones you choose to change.
|
93
|
+
|
94
|
+
### Environments
|
95
|
+
Three environments are currently available to be set up within bubbles. These are:
|
96
|
+
- `local_environment` : Designed to be used for a local API for development testing.
|
97
|
+
- `staging_environment` : Designed to be used for a remote API for second-stage testing or production-like deployment.
|
98
|
+
- `production_environment` : Designed to be used for a production environment.
|
99
|
+
|
100
|
+
While the names are hardcoded, the environments can be used for anything - you could easily use a `local_environment` to store the information for one of your production servers.
|
101
|
+
|
102
|
+
#### Configuration of Environments
|
103
|
+
Environments are configured as part of the _bubbles configuration block_ and can have the following parameters:
|
104
|
+
|
105
|
+
- `scheme`: The scheme for accessing endpoints on this host. Should be one of `http` or `https`. Defaults to `http`.
|
106
|
+
- `host`: A domain name or IP address for the remote host to access for the environment. Defaults to `127.0.0.1`.
|
107
|
+
- `port`: The port to use to access the remote host. Defaults to `1234`.
|
108
|
+
- `api_key`: The API key to send along with requests for a given environment, if an API key is required. This is optional, and defaults to `nil`.
|
109
|
+
|
110
|
+
You can configure all three environments at once in the _bubbles configuration block_:
|
111
|
+
```ruby
|
112
|
+
Bubbles.configure do |config|
|
113
|
+
config.local_environment = {
|
114
|
+
:scheme => 'http',
|
115
|
+
:host => '0.0.0.0',
|
116
|
+
:port => '1234'
|
117
|
+
}
|
118
|
+
|
119
|
+
config.staging_environment = {
|
120
|
+
:scheme => 'http',
|
121
|
+
:host => 'stage.api.foamfactory.com',
|
122
|
+
:port => '80'
|
123
|
+
}
|
124
|
+
|
125
|
+
config.production_environment = {
|
126
|
+
:scheme => 'https',
|
127
|
+
:host => 'api.foamfactory.com',
|
128
|
+
:port => '443'
|
129
|
+
}
|
130
|
+
end
|
131
|
+
```
|
132
|
+
|
133
|
+
If you choose a scheme of `http` and leave off the `port` configuration variable, it will default to `80`. Similarly, `https` will default to a port of `443`.
|
134
|
+
|
135
|
+
#### Configuration of Endpoints
|
136
|
+
Endpoints are the meat and potatoes of REST interaction. By indicating a _method_, _uri_, _body_, and _headers_, you are effectively making a function call on a remote server.
|
137
|
+
|
138
|
+
_Endpoints_ are specified as an array of objects within the _bubbles configuration block_:
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
config.endpoints = [
|
142
|
+
# Individual endpoint definitions go here
|
143
|
+
]
|
144
|
+
```
|
145
|
+
|
146
|
+
When processing each of these endpoint definitions, a method is created on instances of `RestEnvironment` that allows you to call the method in question. For example, an endpoint defined as:
|
147
|
+
```ruby
|
148
|
+
{
|
149
|
+
:method => :get,
|
150
|
+
:location => :version,
|
151
|
+
:authenticated => false,
|
152
|
+
:api_key_required => false
|
153
|
+
}
|
154
|
+
```
|
155
|
+
|
156
|
+
will create a method on instances of `RestEnvironment` called `version`, which will execute the appropriate REST call (via `RestClient`) and return a `RestClient::Response` object.
|
157
|
+
|
158
|
+
Each _endpoint_ object can have the following attributes:
|
159
|
+
|
160
|
+
| Name | Description | Required? | Default |
|
161
|
+
| :--- | :------------------ | :-------: | :-----: |
|
162
|
+
| `method`| The HTTP method to use to access the API for this endpoint. Must be one of `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, or `HEAD`. | Yes | N/A |
|
163
|
+
| `location`| The path to access the endpoint. This is placed after the `host:port` section to build the URI. It may have URI parameters in the form of `{paramName}`. If a URI parameter is specified within the `location`, a `uri_params` hash will be expected to be passed to the calling method to replace the placeholder values. | Yes | N/A |
|
164
|
+
| `name` | The name to give the method created to make this REST call. | No | The value of the `location` parameter, with slashes (`/`) replaced with underscores (`_`). |
|
165
|
+
| `authorization` | Whether or not this endpoint requires authentication prior to executing the call. If true, then an `authorization_token` will be added to the method as a parameter to be passed when the method is called. This parameter will be placed in an `Authorization` header when the REST call is executed. | No | `false` |
|
166
|
+
| `api_key_required` | Whether or not an API key is required. If `true`, a parameter will be added to the method created to execute the REST API call named `api_key`. The value of this parameter will be set as the value of the `X-Api-Key` header when making the REST API call. | No | `false` |
|
167
|
+
| `expect_json` | Whether or not JSON is expected as the format of the `body` of both the Request and Response. Additionally, specifying `true` for this option will cause methods executing REST requests to return `OpenStruct` objects representing the fully-parsed JSON response (on success). See note under "Limitations", above, on using this parameter. | No | `false` |
|
168
|
+
| `encode_authorization` | Whether the `data` passed as part of the request should be re-encoded as an `Authorization: Basic` header (and Base64 encoded). Typically, this is only used for initial username/password authentication. | No | `false` |
|
169
|
+
|
170
|
+
### Examples
|
171
|
+
#### GET the version of the software (unauthenticated, no API key required)
|
172
|
+
|
173
|
+
#### GET a specific user by id (authentication required)
|
174
|
+
|
175
|
+
#### POST a login (i.e. retrieve an authorization token)
|
176
|
+
|
177
|
+
#### DELETE a user by id
|
178
|
+
|
179
|
+
#### PATCH a user's information by providing a body containing information to update
|
data/bubbles.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = Bubbles::VersionInformation.version_name
|
9
9
|
spec.date = Date.today.strftime("%Y-%m-%d")
|
10
10
|
spec.summary = 'FoamFactory REST Client'
|
11
|
-
spec.homepage = '
|
11
|
+
spec.homepage = 'https://github.com/FoamFactory/bubbles'
|
12
12
|
spec.authors = ['Scott Johnson']
|
13
13
|
spec.email = 'jaywir3@gmail.com'
|
14
14
|
spec.files = %w(lib/bubbles.rb lib/bubbles/rest_environment.rb lib/bubbles/version.rb)
|
data/lib/bubbles/config.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'bubbles/rest_environment'
|
2
2
|
require 'bubbles/endpoint'
|
3
|
+
require 'base64'
|
3
4
|
|
4
5
|
module Bubbles
|
5
6
|
class << self
|
@@ -41,14 +42,17 @@ module Bubbles
|
|
41
42
|
@local_environment_scheme = 'http'
|
42
43
|
@local_environment_host = '127.0.0.1'
|
43
44
|
@local_environment_port = '1234'
|
45
|
+
@local_environment_api_key = nil
|
44
46
|
|
45
47
|
@staging_environment_scheme = 'http'
|
46
48
|
@staging_environment_host = '127.0.0.1'
|
47
49
|
@staging_environment_port = '1234'
|
50
|
+
@staging_environment_api_key = nil
|
48
51
|
|
49
52
|
@production_environment_scheme = 'http'
|
50
53
|
@production_environment_host = '127.0.0.1'
|
51
54
|
@production_environment_port = '1234'
|
55
|
+
@production_environment_api_key = nil
|
52
56
|
|
53
57
|
@endpoints = Hash.new
|
54
58
|
end
|
@@ -57,7 +61,7 @@ module Bubbles
|
|
57
61
|
# Retrieve the local {RestEnvironment} object defined as part of this Configuration.
|
58
62
|
#
|
59
63
|
def local_environment
|
60
|
-
RestEnvironment.new(@local_environment_scheme, @local_environment_host, @local_environment_port)
|
64
|
+
RestEnvironment.new(@local_environment_scheme, @local_environment_host, @local_environment_port, @local_environment_api_key)
|
61
65
|
end
|
62
66
|
|
63
67
|
##
|
@@ -78,13 +82,14 @@ module Bubbles
|
|
78
82
|
@local_environment_scheme = env[:scheme]
|
79
83
|
@local_environment_host = env[:host]
|
80
84
|
@local_environment_port = env[:port]
|
85
|
+
@local_environment_api_key = env[:api_key]
|
81
86
|
end
|
82
87
|
|
83
88
|
##
|
84
89
|
# Retrieve the staging {RestEnvironment} object defined as part of this Configuration.
|
85
90
|
#
|
86
91
|
def staging_environment
|
87
|
-
RestEnvironment.new(@staging_environment_scheme, @staging_environment_host, @staging_environment_port)
|
92
|
+
RestEnvironment.new(@staging_environment_scheme, @staging_environment_host, @staging_environment_port, @staging_environment_api_key)
|
88
93
|
end
|
89
94
|
|
90
95
|
##
|
@@ -105,13 +110,14 @@ module Bubbles
|
|
105
110
|
@staging_environment_scheme = env[:scheme]
|
106
111
|
@staging_environment_host = env[:host]
|
107
112
|
@staging_environment_port = env[:port]
|
113
|
+
@staging_environment_api_key = env[:api_key]
|
108
114
|
end
|
109
115
|
|
110
116
|
##
|
111
117
|
# Retrieve the production {RestEnvironment} object defined as part of this Configuration.
|
112
118
|
#
|
113
119
|
def production_environment
|
114
|
-
RestEnvironment.new(@production_environment_scheme, @production_environment_host, @production_environment_port)
|
120
|
+
RestEnvironment.new(@production_environment_scheme, @production_environment_host, @production_environment_port, @production_environment_api_key)
|
115
121
|
end
|
116
122
|
|
117
123
|
##
|
@@ -124,7 +130,7 @@ module Bubbles
|
|
124
130
|
# config.production_environment = {
|
125
131
|
# :scheme => 'https',
|
126
132
|
# :host => 'api.somehost.com',
|
127
|
-
# :port => '443'
|
133
|
+
# :port => '443',
|
128
134
|
# }
|
129
135
|
# end
|
130
136
|
#
|
@@ -132,6 +138,7 @@ module Bubbles
|
|
132
138
|
@production_environment_scheme = env[:scheme]
|
133
139
|
@production_environment_host = env[:host]
|
134
140
|
@production_environment_port = env[:port]
|
141
|
+
@production_environment_api_key = env[:api_key]
|
135
142
|
end
|
136
143
|
|
137
144
|
##
|
@@ -160,7 +167,7 @@ module Bubbles
|
|
160
167
|
def endpoints=(endpoints)
|
161
168
|
new_endpoints = Hash.new
|
162
169
|
endpoints.each do |ep|
|
163
|
-
endpoint_object = Endpoint.new ep[:method], ep[:location].to_s, ep[:authenticated], ep[:api_key_required], ep[:name]
|
170
|
+
endpoint_object = Endpoint.new ep[:method], ep[:location].to_s, ep[:authenticated], ep[:api_key_required], ep[:name], ep[:expect_json], ep[:encode_authorization]
|
164
171
|
|
165
172
|
new_endpoints[endpoint_object.get_key_string] = endpoint_object
|
166
173
|
end
|
@@ -184,8 +191,14 @@ module Bubbles
|
|
184
191
|
if endpoint.method == :get
|
185
192
|
if endpoint.authenticated?
|
186
193
|
Bubbles::RestEnvironment.class_exec do
|
187
|
-
|
188
|
-
|
194
|
+
if endpoint.has_uri_params?
|
195
|
+
define_method(endpoint_name_as_sym) do |auth_token, uri_params|
|
196
|
+
RestClientResources.execute_get_authenticated self, endpoint, auth_token, uri_params
|
197
|
+
end
|
198
|
+
else
|
199
|
+
define_method(endpoint_name_as_sym) do |auth_token|
|
200
|
+
RestClientResources.execute_get_authenticated self, endpoint, auth_token, {}
|
201
|
+
end
|
189
202
|
end
|
190
203
|
end
|
191
204
|
else
|
@@ -195,6 +208,155 @@ module Bubbles
|
|
195
208
|
end
|
196
209
|
end
|
197
210
|
end
|
211
|
+
elsif endpoint.method == :post
|
212
|
+
if endpoint.authenticated?
|
213
|
+
Bubbles::RestEnvironment.class_exec do
|
214
|
+
define_method(endpoint_name_as_sym) do |auth_token, data|
|
215
|
+
RestClientResources.execute_post_authenticated self, endpoint, auth_token, data
|
216
|
+
end
|
217
|
+
end
|
218
|
+
else
|
219
|
+
if endpoint.api_key_required?
|
220
|
+
Bubbles::RestEnvironment.class_exec do
|
221
|
+
define_method(endpoint_name_as_sym) do |data|
|
222
|
+
additional_headers = {}
|
223
|
+
if endpoint.encode_authorization_header?
|
224
|
+
count = 0
|
225
|
+
auth_value = ''
|
226
|
+
endpoint.encode_authorization.each { |auth_key|
|
227
|
+
if data[auth_key]
|
228
|
+
if count > 0
|
229
|
+
auth_value = auth_value + ':' + data[auth_key]
|
230
|
+
else
|
231
|
+
auth_value = data[auth_key]
|
232
|
+
end
|
233
|
+
|
234
|
+
count = count + 1
|
235
|
+
|
236
|
+
data.delete(auth_key)
|
237
|
+
end
|
238
|
+
}
|
239
|
+
|
240
|
+
additional_headers[:Authorization] = 'Basic ' + Base64.strict_encode64(auth_value)
|
241
|
+
end
|
242
|
+
|
243
|
+
RestClientResources.execute_post_with_api_key self, endpoint, self.api_key, data, additional_headers
|
244
|
+
end
|
245
|
+
end
|
246
|
+
else
|
247
|
+
raise 'Unauthenticated POST requests without an API key are not allowed'
|
248
|
+
end
|
249
|
+
end
|
250
|
+
elsif endpoint.method == :delete
|
251
|
+
if endpoint.authenticated?
|
252
|
+
Bubbles::RestEnvironment.class_exec do
|
253
|
+
if endpoint.has_uri_params?
|
254
|
+
define_method(endpoint_name_as_sym) do |auth_token, uri_params|
|
255
|
+
RestClientResources.execute_delete_authenticated self, endpoint, auth_token, uri_params
|
256
|
+
end
|
257
|
+
else
|
258
|
+
# NOTE: While MDN states that DELETE requests with a body are allowed, it seems that a number of
|
259
|
+
# documentation sites discourage its use. Thus, it's possible that, depending on the server API
|
260
|
+
# framework, the DELETE request could be rejected. As such, we're disallowing it here, BUT if we
|
261
|
+
# get feedback from users that it should be supported, we can add support for it.
|
262
|
+
raise 'DELETE requests without URI parameters are not allowed'
|
263
|
+
# define_method(endpoint_name_as_sym) do |auth_token|
|
264
|
+
# RestClientResources.execute_delete_authenticated self, endpoint, auth_token, {}
|
265
|
+
# end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
else
|
269
|
+
raise 'Unauthenticated DELETE requests are not allowed'
|
270
|
+
# Bubbles::RestEnvironment.class_exec do
|
271
|
+
# define_method(endpoint_name_as_sym) do
|
272
|
+
# RestClientResources.execute_delete_unauthenticated self, endpoint
|
273
|
+
# end
|
274
|
+
# end
|
275
|
+
end
|
276
|
+
elsif endpoint.method == :patch
|
277
|
+
if endpoint.authenticated?
|
278
|
+
Bubbles::RestEnvironment.class_exec do
|
279
|
+
if endpoint.has_uri_params?
|
280
|
+
define_method(endpoint_name_as_sym) do |auth_token, uri_params, data|
|
281
|
+
RestClientResources.execute_patch_authenticated self, endpoint, auth_token, uri_params, data
|
282
|
+
end
|
283
|
+
else
|
284
|
+
define_method(endpoint_name_as_sym) do |auth_token, data|
|
285
|
+
RestClientResources.execute_patch_authenticated self, endpoint, auth_token, nil, data
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
else
|
290
|
+
raise 'Unauthenticated PATCH requests are not implemented'
|
291
|
+
# Bubbles::RestEnvironment.class_exec do
|
292
|
+
# define_method(endpoint_name_as_sym) do
|
293
|
+
# RestClientResources.execute_delete_unauthenticated self, endpoint
|
294
|
+
# end
|
295
|
+
# end
|
296
|
+
end
|
297
|
+
elsif endpoint.method == :put
|
298
|
+
if endpoint.authenticated?
|
299
|
+
Bubbles::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
|
303
|
+
end
|
304
|
+
else
|
305
|
+
define_method(endpoint_name_as_sym) do |auth_token, data|
|
306
|
+
RestClientResources.execute_put_authenticated self, endpoint, auth_token, nil, data
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
else
|
311
|
+
raise 'Unauthenticated PUT requests are not implemented'
|
312
|
+
# Bubbles::RestEnvironment.class_exec do
|
313
|
+
# define_method(endpoint_name_as_sym) do
|
314
|
+
# RestClientResources.execute_delete_unauthenticated self, endpoint
|
315
|
+
# end
|
316
|
+
# end
|
317
|
+
end
|
318
|
+
elsif endpoint.method == :head
|
319
|
+
if endpoint.authenticated?
|
320
|
+
Bubbles::RestEnvironment.class_exec do
|
321
|
+
if endpoint.has_uri_params?
|
322
|
+
define_method(endpoint_name_as_sym) do |auth_token, uri_params|
|
323
|
+
RestClientResources.execute_head_authenticated self, endpoint, auth_token, uri_params
|
324
|
+
end
|
325
|
+
else
|
326
|
+
define_method(endpoint_name_as_sym) do |auth_token|
|
327
|
+
RestClientResources.execute_head_authenticated self, endpoint, auth_token, {}
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
331
|
+
elsif endpoint.api_key_required?
|
332
|
+
Bubbles::RestEnvironment.class_exec do
|
333
|
+
if endpoint.has_uri_params?
|
334
|
+
define_method(endpoint_name_as_sym) do |api_key, uri_params|
|
335
|
+
additional_headers = {}
|
336
|
+
additional_headers['X-Api-Key'] = api_key
|
337
|
+
RestClientResources.execute_head_unauthenticated self, endpoint, uri_params, additional_headers
|
338
|
+
end
|
339
|
+
else
|
340
|
+
define_method(endpoint_name_as_sym) do |api_key|
|
341
|
+
additional_headers = {}
|
342
|
+
additional_headers['X-Api-Key'] = api_key
|
343
|
+
RestClientResources.execute_head_unauthenticated self, endpoint, {}, additional_headers
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
else
|
348
|
+
Bubbles::RestEnvironment.class_exec do
|
349
|
+
if endpoint.has_uri_params?
|
350
|
+
define_method(endpoint_name_as_sym) do |uri_params|
|
351
|
+
RestClientResources.execute_head_unauthenticated self, endpoint, uri_params, {}
|
352
|
+
end
|
353
|
+
else
|
354
|
+
define_method(endpoint_name_as_sym) do
|
355
|
+
RestClientResources.execute_head_unauthenticated self, endpoint, {}, {}
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|
198
360
|
end
|
199
361
|
end
|
200
362
|
end
|
data/lib/bubbles/endpoint.rb
CHANGED
@@ -9,22 +9,42 @@ module Bubbles
|
|
9
9
|
# Endpoint can be used with any +RestEnvironment+.
|
10
10
|
#
|
11
11
|
class Endpoint
|
12
|
-
##
|
12
|
+
##
|
13
|
+
# Controls the method used to access the endpoint. Must be one of {Endpoint::Methods}.
|
13
14
|
# @return [Symbol] the method used to access the endpoint. Will always be one of the symbols defined in {Endpoint::METHODS}.
|
14
15
|
attr_accessor :method
|
15
16
|
|
16
|
-
##
|
17
|
+
##
|
18
|
+
# Controls the location, relative to the web root of the host, used to access the endpoint.
|
17
19
|
# @return [String] the location relative to the web root of the host used to access the endpoint
|
18
20
|
attr_accessor :location
|
19
21
|
|
20
|
-
##
|
22
|
+
##
|
23
|
+
# Controls whether authentication is required to access this endpoint. Defaults to false.
|
21
24
|
# @return [Boolean] true, if authentication is required to access this endpoint; false, otherwise.
|
22
25
|
attr_accessor :authentication_required
|
23
26
|
|
24
|
-
##
|
27
|
+
##
|
28
|
+
# Controls whether an API key is required to access this endpoint. Defaults to false.
|
25
29
|
# @return [Boolean] true, if an API key is required to access this endpoint; false, otherwise.
|
26
30
|
attr_accessor :api_key_required
|
27
31
|
|
32
|
+
##
|
33
|
+
# Controls whether JSON is the expected form of output from this +Endpoint+.
|
34
|
+
# @return [Boolean] true, if JSON is the expected form of output from this +Endpoint+; false, otherwise.
|
35
|
+
attr_accessor :expect_json
|
36
|
+
|
37
|
+
##
|
38
|
+
# Controls which data values should be encoded as part of an Authorization header. They will be separated with a
|
39
|
+
# colon in the order they are received and Base64-encoded.
|
40
|
+
# @return [Array] An array of +Symbol+s specifying which of the data attributes should be Base64-encoded as part of
|
41
|
+
# an Authorization header. The values will be encoded in the order they are received.
|
42
|
+
attr_accessor :encode_authorization
|
43
|
+
|
44
|
+
##
|
45
|
+
# An array of parameters that are specified on the URI of this endpoint for each call.
|
46
|
+
attr_accessor :uri_params
|
47
|
+
|
28
48
|
## A template for specifying the complete URL for endpoints.
|
29
49
|
API_URL = ::Addressable::Template.new("{scheme}://{host}/{endpoint}")
|
30
50
|
|
@@ -33,7 +53,7 @@ module Bubbles
|
|
33
53
|
|
34
54
|
|
35
55
|
## The HTTP methods supported by a rest client utilizing Bubbles.
|
36
|
-
METHODS = %w[get].freeze
|
56
|
+
METHODS = %w[get post patch put delete head].freeze
|
37
57
|
|
38
58
|
##
|
39
59
|
# Construct a new instance of an Endpoint.
|
@@ -46,18 +66,32 @@ module Bubbles
|
|
46
66
|
# +false+.
|
47
67
|
# @param [String] name An optional name which will be given to the method that will execute this {Endpoint} within
|
48
68
|
# the context of a {RestClientResources} object.
|
69
|
+
# @param [Boolean] expect_json Whether or not to expect a JSON response from this +Endpoint+. Defaults to +false+.
|
49
70
|
#
|
50
|
-
def initialize(method, location, auth_required = false, api_key_required = false, name = nil)
|
71
|
+
def initialize(method, location, auth_required = false, api_key_required = false, name = nil, expect_json = false, encode_authorization = {})
|
51
72
|
@method = method
|
52
73
|
@location = location
|
53
74
|
@auth_required = auth_required
|
54
75
|
@api_key_required = api_key_required
|
55
76
|
@name = name
|
77
|
+
@expect_json = expect_json
|
78
|
+
@encode_authorization = encode_authorization
|
79
|
+
@uri_params = []
|
56
80
|
|
57
81
|
# Strip the leading slash from the endpoint location, if it's there
|
58
82
|
if @location.to_s[0] == '/'
|
59
83
|
@location = @location.to_s.slice(1, @location.to_s.length)
|
60
84
|
end
|
85
|
+
|
86
|
+
# Extract URI parameters and create symbols for them
|
87
|
+
# URI parameters are enclosed by curly braces '{' and '}'
|
88
|
+
@location.to_s.split('/').each do |uri_segment|
|
89
|
+
|
90
|
+
match_data = /\{(.*)\}/.match(uri_segment)
|
91
|
+
unless match_data == nil
|
92
|
+
@uri_params.push(match_data[1].to_sym)
|
93
|
+
end
|
94
|
+
end
|
61
95
|
end
|
62
96
|
|
63
97
|
##
|
@@ -101,16 +135,22 @@ module Bubbles
|
|
101
135
|
#
|
102
136
|
# @return [String] A +String+ containing the full URL to access this +Endpoint+ on the given {RestEnvironment}.
|
103
137
|
#
|
104
|
-
def get_expanded_url(env)
|
138
|
+
def get_expanded_url(env, uri_params = {})
|
105
139
|
url = get_base_url env
|
106
140
|
|
107
141
|
if is_complex?
|
108
|
-
special_url_string = '{scheme}://{
|
142
|
+
special_url_string = '{scheme}://{host}/'
|
109
143
|
unless @port == 80 || @port == 443
|
110
|
-
special_url_string = '{scheme}://{
|
144
|
+
special_url_string = '{scheme}://{host}:{port}/'
|
111
145
|
end
|
112
146
|
|
113
147
|
special_url_string = special_url_string + @location
|
148
|
+
|
149
|
+
uri_params.each do |param, value|
|
150
|
+
needle = "{#{param.to_s}}"
|
151
|
+
special_url_string = special_url_string.sub(needle, value.to_s)
|
152
|
+
end
|
153
|
+
|
114
154
|
url = ::Addressable::Template.new(special_url_string)
|
115
155
|
|
116
156
|
return url.expand(scheme: env.scheme, host: env.host, port: env.port)
|
@@ -149,6 +189,14 @@ module Bubbles
|
|
149
189
|
@auth_required
|
150
190
|
end
|
151
191
|
|
192
|
+
##
|
193
|
+
# Determine if an API key is required
|
194
|
+
#
|
195
|
+
# @return [Boolean] true, if an API key is required to make the request; false, otherwise.
|
196
|
+
def api_key_required?
|
197
|
+
api_key_required
|
198
|
+
end
|
199
|
+
|
152
200
|
##
|
153
201
|
# Set the name of the method on {RestClientResources} used to access this {Endpoint}.
|
154
202
|
#
|
@@ -177,5 +225,19 @@ module Bubbles
|
|
177
225
|
def name?
|
178
226
|
@name == nil
|
179
227
|
end
|
228
|
+
|
229
|
+
##
|
230
|
+
# Whether or not an Authorization header should be Base64-encoded.
|
231
|
+
#
|
232
|
+
# @return [Boolean] true, if attributes from the data array have been specified to be Base64-encoded as part of an
|
233
|
+
# Authorization header; false, otherwise.
|
234
|
+
#
|
235
|
+
def encode_authorization_header?
|
236
|
+
@encode_authorization.length > 0
|
237
|
+
end
|
238
|
+
|
239
|
+
def has_uri_params?
|
240
|
+
!@uri_params.empty?
|
241
|
+
end
|
180
242
|
end
|
181
243
|
end
|
@@ -44,26 +44,14 @@ module Bubbles
|
|
44
44
|
# @return [RestClient::Response] The +Response+ resulting from the execution of the GET call.
|
45
45
|
#
|
46
46
|
def self.execute_get_unauthenticated(env, endpoint)
|
47
|
-
|
48
|
-
|
49
|
-
begin
|
47
|
+
execute_rest_call(env, endpoint, nil, nil, nil) do |env, url, data, headers|
|
50
48
|
if env.scheme == 'https'
|
51
|
-
|
52
|
-
.get(
|
53
|
-
:content_type => :json,
|
54
|
-
:accept => :json
|
55
|
-
})
|
49
|
+
next RestClient::Resource.new(url.to_s, :verify_ssl => OpenSSL::SSL::VERIFY_NONE)
|
50
|
+
.get(headers)
|
56
51
|
else
|
57
|
-
|
58
|
-
{
|
59
|
-
:content_type => :json
|
60
|
-
})
|
52
|
+
next RestClient.get(url.to_s, headers)
|
61
53
|
end
|
62
|
-
rescue Errno::ECONNREFUSED
|
63
|
-
return {:error => 'Unable to connect to host ' + env.host.to_s + ':' + env.port.to_s}.to_json
|
64
54
|
end
|
65
|
-
|
66
|
-
response
|
67
55
|
end
|
68
56
|
|
69
57
|
##
|
@@ -74,32 +62,190 @@ module Bubbles
|
|
74
62
|
# @param [RestEnvironment] env The +RestEnvironment+ to use to execute the request
|
75
63
|
# @param [Endpoint] endpoint The +Endpoint+ which should be requested
|
76
64
|
# @param [String] auth_token The authorization token to use for authentication.
|
65
|
+
# @param [Hash] uri_params A +Hash+ of identifiers to values to replace in the URI string.
|
77
66
|
#
|
78
67
|
# @return [RestClient::Response] The +Response+ resulting from the execution of the GET call.
|
79
68
|
#
|
80
|
-
def self.execute_get_authenticated(env, endpoint, auth_token)
|
81
|
-
|
69
|
+
def self.execute_get_authenticated(env, endpoint, auth_token, uri_params)
|
70
|
+
execute_rest_call(env, endpoint, nil, auth_token, nil, uri_params) do |env, url, data, headers|
|
71
|
+
if env.scheme == 'https'
|
72
|
+
next RestClient::Resource.new(url.to_s, :verify_ssl => OpenSSL::SSL::VERIFY_NONE)
|
73
|
+
.get(headers)
|
74
|
+
else
|
75
|
+
next RestClient.get(url.to_s, headers)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
82
79
|
|
83
|
-
|
80
|
+
##
|
81
|
+
# Execute a HEAD request without authentication.
|
82
|
+
#
|
83
|
+
# This is the same as a GET request, but will only return headers and not the response body.
|
84
|
+
#
|
85
|
+
# @param [RestEnvironment] env The +RestEnvironment+ to use to execute the request
|
86
|
+
# @param [Endpoint] endpoint The +Endpoint+ which should be requested
|
87
|
+
#
|
88
|
+
# @return [RestClient::Response] The +Response+ resulting from the execution of the GET call.
|
89
|
+
#
|
90
|
+
def self.execute_head_unauthenticated(env, endpoint, uri_params, additional_headers)
|
91
|
+
execute_rest_call(env, endpoint, nil, nil, additional_headers, uri_params) do |env, url, data, headers|
|
84
92
|
if env.scheme == 'https'
|
85
|
-
|
86
|
-
.
|
87
|
-
:authorization => 'Bearer ' + auth_token,
|
88
|
-
:content_type => :json,
|
89
|
-
:accept => :json
|
90
|
-
})
|
93
|
+
next RestClient::Resource.new(url.to_s, :verify_ssl => OpenSSL::SSL::VERIFY_NONE)
|
94
|
+
.head(headers)
|
91
95
|
else
|
92
|
-
|
93
|
-
{
|
94
|
-
:authorization => 'Bearer ' + auth_token,
|
95
|
-
:content_type => :json
|
96
|
-
})
|
96
|
+
next RestClient.head(url.to_s, headers)
|
97
97
|
end
|
98
|
-
rescue Errno::ECONNREFUSED
|
99
|
-
return {:error => 'Unable to connect to host ' + env.host.to_s + ':' + env.port.to_s}.to_json
|
100
98
|
end
|
99
|
+
end
|
101
100
|
|
102
|
-
|
101
|
+
##
|
102
|
+
# Execute a HEAD request with authentication.
|
103
|
+
#
|
104
|
+
# Currently, only Authorization: Bearer is supported. This is the same as a GET request, but will only return
|
105
|
+
# headers and not the response body.
|
106
|
+
#
|
107
|
+
# @param [RestEnvironment] env The +RestEnvironment+ to use to execute the request
|
108
|
+
# @param [Endpoint] endpoint The +Endpoint+ which should be requested
|
109
|
+
# @param [String] auth_token The authorization token to use for authentication.
|
110
|
+
# @param [Hash] uri_params A +Hash+ of identifiers to values to replace in the URI string.
|
111
|
+
#
|
112
|
+
# @return [RestClient::Response] The +Response+ resulting from the execution of the GET call.
|
113
|
+
#
|
114
|
+
def self.execute_head_authenticated(env, endpoint, auth_token, uri_params)
|
115
|
+
execute_rest_call(env, endpoint, nil, auth_token, nil, uri_params) do |env, url, data, headers|
|
116
|
+
if env.scheme == 'https'
|
117
|
+
next RestClient::Resource.new(url.to_s, :verify_ssl => OpenSSL::SSL::VERIFY_NONE)
|
118
|
+
.head(headers)
|
119
|
+
else
|
120
|
+
next RestClient.head(url.to_s, headers)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
##
|
126
|
+
# Execute a POST request without authentication, but requiring an API key.
|
127
|
+
#
|
128
|
+
# @param [RestEnvironment] env The +RestEnvironment+ to use to execute the request
|
129
|
+
# @param [Endpoint] endpoint The +Endpoint+ which should be requested
|
130
|
+
# @param [String] api_key The API key to use to process the request. Will be placed in an 'X-API-KEY' header.
|
131
|
+
# @param [Hash] data A +Hash+ of key-value pairs that will be sent in the body of the http request.
|
132
|
+
# @param [Hash] headers (Optional) A +Hash+ of key-value pairs that will be sent as HTTP headers as part of the
|
133
|
+
# request. Defaults to +nil+.
|
134
|
+
#
|
135
|
+
# @return [RestClient::Response] The +Response+ resulting from the execution of the POST call.
|
136
|
+
#
|
137
|
+
def self.execute_post_with_api_key(env, endpoint, api_key, data, headers=nil)
|
138
|
+
additional_headers = {
|
139
|
+
'X-Api-Key' => api_key
|
140
|
+
}
|
141
|
+
|
142
|
+
unless headers.nil?
|
143
|
+
headers.each { |nextHeader|
|
144
|
+
additional_headers[nextHeader[0]] = nextHeader[1]
|
145
|
+
}
|
146
|
+
end
|
147
|
+
|
148
|
+
execute_rest_call(env, endpoint, data, nil, additional_headers) do |env, url, data, headers|
|
149
|
+
if env.scheme == 'https'
|
150
|
+
next RestClient::Resource.new(url.to_s, :verify_ssl => OpenSSL::SSL::VERIFY_NONE)
|
151
|
+
.post(data.to_json, additional_headers)
|
152
|
+
else
|
153
|
+
next RestClient.post url.to_s, data.to_json, additional_headers
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
##
|
159
|
+
# Execute a POST request with authentication in the form of an authorization token.
|
160
|
+
#
|
161
|
+
# @param [RestEnvironment] env The +RestEnvironment+ to use to execute the request
|
162
|
+
# @param [Endpoint] endpoint The +Endpoint+ which should be requested
|
163
|
+
# @param [String] auth_token The authorization token retrieved during some former authentication call. Will be
|
164
|
+
# placed into a Authorization header.
|
165
|
+
# @param [Hash] data A +Hash+ of key-value pairs that will be sent in the body of the http request.
|
166
|
+
#
|
167
|
+
# @return [RestClient::Response] The +Response+ resulting from the execution of the POST call.
|
168
|
+
#
|
169
|
+
def self.execute_post_authenticated(env, endpoint, auth_token, data)
|
170
|
+
return execute_rest_call(env, endpoint, data, auth_token, nil) do |env, url, data, headers|
|
171
|
+
if env.scheme == 'https'
|
172
|
+
next RestClient::Resource.new(url.to_s, :verify_ssl => OpenSSL::SSL::VERIFY_NONE)
|
173
|
+
.post(data.to_json, headers)
|
174
|
+
|
175
|
+
else
|
176
|
+
next RestClient.post(url.to_s, data.to_json, headers)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
##
|
182
|
+
# Execute a PATCH request with authentication in the form of an authorization token.
|
183
|
+
#
|
184
|
+
# @param [RestEnvironment] env The +RestEnvironment+ to use to execute the request
|
185
|
+
# @param [Endpoint] endpoint The +Endpoint+ which should be requested
|
186
|
+
# @param [String] auth_token The authorization token retrieved during some former authentication call. Will be
|
187
|
+
# placed into a Authorization header.
|
188
|
+
# @param [Hash] uri_params A +Hash+ of identifiers to values to replace in the URI string.
|
189
|
+
# @param [Hash] data A +Hash+ of key-value pairs that will be sent in the body of the http request.
|
190
|
+
#
|
191
|
+
# @return [RestClient::Response] The +Response+ resulting from the execution of the PATCH call.
|
192
|
+
#
|
193
|
+
def self.execute_patch_authenticated(env, endpoint, auth_token, uri_params, data)
|
194
|
+
return execute_rest_call(env, endpoint, data, auth_token, nil, uri_params) do |env, url, data, headers|
|
195
|
+
if env.scheme == 'https'
|
196
|
+
next RestClient::Resource.new(url.to_s, :verify_ssl => OpenSSL::SSL::VERIFY_NONE)
|
197
|
+
.patch(data.to_json, headers)
|
198
|
+
|
199
|
+
else
|
200
|
+
next RestClient.patch(url.to_s, data.to_json, headers)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
##
|
206
|
+
# Execute a PUT request with authentication in the form of an authorization token.
|
207
|
+
#
|
208
|
+
# @param [RestEnvironment] env The +RestEnvironment+ to use to execute the request
|
209
|
+
# @param [Endpoint] endpoint The +Endpoint+ which should be requested
|
210
|
+
# @param [String] auth_token The authorization token retrieved during some former authentication call. Will be
|
211
|
+
# placed into a Authorization header.
|
212
|
+
# @param [Hash] uri_params A +Hash+ of identifiers to values to replace in the URI string.
|
213
|
+
# @param [Hash] data A +Hash+ of key-value pairs that will be sent in the body of the http request.
|
214
|
+
#
|
215
|
+
# @return [RestClient::Response] The +Response+ resulting from the execution of the PUT call.
|
216
|
+
#
|
217
|
+
def self.execute_put_authenticated(env, endpoint, auth_token, uri_params, data)
|
218
|
+
return execute_rest_call(env, endpoint, data, auth_token, nil, uri_params) do |env, url, data, headers|
|
219
|
+
if env.scheme == 'https'
|
220
|
+
next RestClient::Resource.new(url.to_s, :verify_ssl => OpenSSL::SSL::VERIFY_NONE)
|
221
|
+
.put(data.to_json, headers)
|
222
|
+
|
223
|
+
else
|
224
|
+
next RestClient.put(url.to_s, data.to_json, headers)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
##
|
230
|
+
# Execute a DELETE request with authentication in the form of an authorization token.
|
231
|
+
#
|
232
|
+
# @param [RestEnvironment] env The +RestEnvironment+ to use to execute the request
|
233
|
+
# @param [Endpoint] endpoint The +Endpoint+ which should be requested
|
234
|
+
# @param [String] auth_token The authorization token retrieved during some former authentication call. Will be
|
235
|
+
# placed into a Authorization header.
|
236
|
+
# @param [Hash] uri_params A +Hash+ of identifiers to values to replace in the URI string.
|
237
|
+
#
|
238
|
+
# @return [RestClient::Response] The +Response+ resulting from the execution of the DELETE call.
|
239
|
+
#
|
240
|
+
def self.execute_delete_authenticated(env, endpoint, auth_token, uri_params)
|
241
|
+
execute_rest_call(env, endpoint, nil, auth_token, nil, uri_params) do |env, url, data, headers|
|
242
|
+
if env.scheme == 'https'
|
243
|
+
next RestClient::Resource.new(url.to_s, :verify_ssl => OpenSSL::SSL::VERIFY_NONE)
|
244
|
+
.delete(headers)
|
245
|
+
else
|
246
|
+
next RestClient.delete(url.to_s, headers)
|
247
|
+
end
|
248
|
+
end
|
103
249
|
end
|
104
250
|
|
105
251
|
##
|
@@ -120,5 +266,65 @@ module Bubbles
|
|
120
266
|
|
121
267
|
self.local_environment
|
122
268
|
end
|
269
|
+
|
270
|
+
private
|
271
|
+
|
272
|
+
##
|
273
|
+
# Execute a REST call to the API.
|
274
|
+
#
|
275
|
+
# This is the workhorse of the +RestClientResources+ class. It performs the necessary setup of headers and the HTTP
|
276
|
+
# request, and then executes the remote API call.
|
277
|
+
#
|
278
|
+
# @param [RestEnvironment] env The +RestEnvironment+ to use to make this API call. Must not be +nil+.
|
279
|
+
# @param [Endpoint] The +Endpoint+ to call. Must not be +nil+.
|
280
|
+
# @param [Hash] The body of the request. May be +nil+ or empty for requests not requiring a body.
|
281
|
+
# @param [String] auth_token The authorization token used to authenticate to the API. May be +nil+ for requests that
|
282
|
+
# don't require authentication.
|
283
|
+
# @param [Hash] headers A +Hash+ of key-value pairs to add to the HTTP request as headers. May be +nil+ if none are
|
284
|
+
# required.
|
285
|
+
# @param [Block] block The block to execute that actually performs the HTTP request.
|
286
|
+
#
|
287
|
+
# @return [RestClient::Response|OpenStruct] If "expect_json" is enabled for the +Endpoint+ being executed, then this
|
288
|
+
# will return an +OpenStruct+; otherwise, the +Response+ will be returned.
|
289
|
+
#
|
290
|
+
def self.execute_rest_call(env, endpoint, data, auth_token, headers, uri_params = {}, &block)
|
291
|
+
unless block
|
292
|
+
raise ArgumentError('This method requires that a block is given.')
|
293
|
+
end
|
294
|
+
|
295
|
+
url = endpoint.get_expanded_url env, uri_params
|
296
|
+
|
297
|
+
begin
|
298
|
+
if data == nil
|
299
|
+
data = {}
|
300
|
+
end
|
301
|
+
|
302
|
+
if headers == nil
|
303
|
+
headers = {
|
304
|
+
:content_type => :json
|
305
|
+
}
|
306
|
+
else
|
307
|
+
headers[:content_type] = :json
|
308
|
+
end
|
309
|
+
|
310
|
+
unless auth_token == nil
|
311
|
+
headers[:authorization] = 'Bearer ' + auth_token
|
312
|
+
end
|
313
|
+
|
314
|
+
if endpoint.expect_json
|
315
|
+
headers[:accept] = :json
|
316
|
+
end
|
317
|
+
|
318
|
+
response = block.call(env, url, data, headers)
|
319
|
+
rescue Errno::ECONNREFUSED
|
320
|
+
response = { :error => 'Unable to connect to host ' + env.host.to_s + ':' + env.port.to_s }.to_json
|
321
|
+
end
|
322
|
+
|
323
|
+
unless endpoint.expect_json or endpoint.method != :head
|
324
|
+
return response
|
325
|
+
end
|
326
|
+
|
327
|
+
JSON.parse(response, object_class: OpenStruct)
|
328
|
+
end
|
123
329
|
end
|
124
330
|
end
|
@@ -2,16 +2,17 @@ require 'addressable/template'
|
|
2
2
|
|
3
3
|
module Bubbles
|
4
4
|
class RestEnvironment
|
5
|
-
attr_accessor :scheme, :host, :port
|
5
|
+
attr_accessor :scheme, :host, :port, :api_key
|
6
6
|
|
7
7
|
##
|
8
8
|
# Construct a new instance of +RestEnvironment+.
|
9
9
|
#
|
10
|
-
# @param scheme The scheme to use for communicating with the host. Currently, http and https are supported.
|
11
|
-
# @param host The host to communicate with.
|
12
|
-
# @param port The port on which the communication channel should operate.
|
10
|
+
# @param [String] scheme The scheme to use for communicating with the host. Currently, http and https are supported.
|
11
|
+
# @param [String] host The host to communicate with.
|
12
|
+
# @param [Integer] port The port on which the communication channel should operate.
|
13
|
+
# @param [String] api_key (Optional) The API key to use to identify your client with the API. Defaults to +nil+.
|
13
14
|
#
|
14
|
-
def initialize(scheme='https', host='api.foamfactory.com', port=443)
|
15
|
+
def initialize(scheme='https', host='api.foamfactory.com', port=443, api_key=nil)
|
15
16
|
@scheme = scheme
|
16
17
|
@port = port
|
17
18
|
|
@@ -20,49 +21,7 @@ module Bubbles
|
|
20
21
|
end
|
21
22
|
|
22
23
|
@host = host
|
24
|
+
@api_key = api_key
|
23
25
|
end
|
24
|
-
|
25
|
-
##
|
26
|
-
# Add an unauthenticated endpoint to this +RestEnvironment+.
|
27
|
-
#
|
28
|
-
# @param type The type of the endpoint. Must be one of [:get].
|
29
|
-
# @param endpoint The path to the endpoint, without the leading slash.
|
30
|
-
#
|
31
|
-
# def add_unauthenticated_endpoint(type, endpoint)
|
32
|
-
# if type == :get
|
33
|
-
# unless endpoint.include? "/"
|
34
|
-
# @endpoints[endpoint] = get_url.expand(scheme: @scheme, environment_host: @host, port: @port, endpoint: endpoint).to_s
|
35
|
-
# return
|
36
|
-
# end
|
37
|
-
#
|
38
|
-
# @endpoints[endpoint] = get_url_with_special_endpoint.expand(scheme: @scheme, environment_host: @host, port: @port, endpoint: endpoint).to_s
|
39
|
-
# end
|
40
|
-
# end
|
41
|
-
|
42
|
-
# def get_endpoint_string(endpoint)
|
43
|
-
# @endpoints[endpoint]
|
44
|
-
# end
|
45
|
-
|
46
|
-
def get_base_url
|
47
|
-
|
48
|
-
end
|
49
|
-
|
50
|
-
private
|
51
|
-
##
|
52
|
-
# Get an addressable template for a 'special' endpoint (one containing '/' characters)
|
53
|
-
#
|
54
|
-
# @param endpoint The endpoint to get an addressable URL to.
|
55
|
-
#
|
56
|
-
# @returns The ::Addressable::Template without the endpoint parameter encoded in the URI.
|
57
|
-
#
|
58
|
-
# def get_url_with_special_endpoint(endpoint)
|
59
|
-
# special_url_string = '{scheme}://{environment_host}/'
|
60
|
-
# unless @port == 80 || @port == 443
|
61
|
-
# special_url_string = '{scheme}://{environment_host}:{port}/'
|
62
|
-
# end
|
63
|
-
#
|
64
|
-
# special_url_string = special_url_string + endpoint
|
65
|
-
# ::Addressable::Template.new(special_url_string)
|
66
|
-
# end
|
67
26
|
end
|
68
27
|
end
|
data/lib/bubbles/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bubbles-rest-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Johnson
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-04-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -165,15 +165,13 @@ files:
|
|
165
165
|
- README.md
|
166
166
|
- Rakefile
|
167
167
|
- bubbles.gemspec
|
168
|
-
- fixtures/vcr_cassettes/get_students_authenticated.yml
|
169
|
-
- fixtures/vcr_cassettes/get_version_unauthenticated.yml
|
170
168
|
- lib/bubbles.rb
|
171
169
|
- lib/bubbles/config.rb
|
172
170
|
- lib/bubbles/endpoint.rb
|
173
171
|
- lib/bubbles/rest_client_resources.rb
|
174
172
|
- lib/bubbles/rest_environment.rb
|
175
173
|
- lib/bubbles/version.rb
|
176
|
-
homepage:
|
174
|
+
homepage: https://github.com/FoamFactory/bubbles
|
177
175
|
licenses:
|
178
176
|
- MPL-2.0
|
179
177
|
metadata:
|
@@ -1,46 +0,0 @@
|
|
1
|
-
---
|
2
|
-
http_interactions:
|
3
|
-
- request:
|
4
|
-
method: get
|
5
|
-
uri: http://127.0.0.1:1234/students
|
6
|
-
body:
|
7
|
-
encoding: US-ASCII
|
8
|
-
string: ''
|
9
|
-
headers:
|
10
|
-
Accept:
|
11
|
-
- "*/*"
|
12
|
-
Accept-Encoding:
|
13
|
-
- gzip, deflate
|
14
|
-
User-Agent:
|
15
|
-
- rest-client/2.0.2 (darwin16.1.0 x86_64) ruby/2.4.0p0
|
16
|
-
Authorization:
|
17
|
-
- Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjcmVhdGlvbl9kYXRlIjoiMjAxNy0xMC0xNVQxMToyNjozMS0wNTowMCIsImV4cGlyYXRpb25fZGF0ZSI6IjIwMTctMTEtMTRUMTE6MjY6MzEtMDU6MDAiLCJ1c2VyX2lkIjoxfQ.dyCWwE4wk7aTfjnGncsqp_jq5QyICKYQPkBh5nLQwFU
|
18
|
-
Content-Type:
|
19
|
-
- application/json
|
20
|
-
Host:
|
21
|
-
- 127.0.0.1:1234
|
22
|
-
response:
|
23
|
-
status:
|
24
|
-
code: 200
|
25
|
-
message: OK
|
26
|
-
headers:
|
27
|
-
Content-Type:
|
28
|
-
- application/json; charset=utf-8
|
29
|
-
Etag:
|
30
|
-
- W/"9d4b7d98d38caa096dee4e7844662cf6"
|
31
|
-
Cache-Control:
|
32
|
-
- max-age=0, private, must-revalidate
|
33
|
-
X-Request-Id:
|
34
|
-
- de8e2f1a-4eb7-44a5-9129-84df77d3b938
|
35
|
-
X-Runtime:
|
36
|
-
- '0.014508'
|
37
|
-
Transfer-Encoding:
|
38
|
-
- chunked
|
39
|
-
body:
|
40
|
-
encoding: UTF-8
|
41
|
-
string: '{"students":[{"id":1,"name":"Joe Blow","address":"2234 Bubble Gum Ave.
|
42
|
-
#127","city":"Sometown","state":"CA","zip":"90263","phone":"5558764566","email":"bubblegumbanshee987@bazooka.org","emergencyContactName":"Some
|
43
|
-
Guy","emergencyContactPhone":"5554339182","joinDate":"2017-10-14T00:00:00.000Z","lastAdvancementDate":"2017-10-14T00:00:00.000Z","waiverSigned":true,"created_at":"2017-10-14T21:46:42.826Z","updated_at":"2017-10-14T21:46:42.826Z","preferredContact":"phone","rank":"green"}]}'
|
44
|
-
http_version:
|
45
|
-
recorded_at: Sun, 15 Oct 2017 16:51:12 GMT
|
46
|
-
recorded_with: VCR 3.0.3
|
@@ -1,42 +0,0 @@
|
|
1
|
-
---
|
2
|
-
http_interactions:
|
3
|
-
- request:
|
4
|
-
method: get
|
5
|
-
uri: http://127.0.0.1:1234/version
|
6
|
-
body:
|
7
|
-
encoding: US-ASCII
|
8
|
-
string: ''
|
9
|
-
headers:
|
10
|
-
Accept:
|
11
|
-
- "*/*"
|
12
|
-
Accept-Encoding:
|
13
|
-
- gzip, deflate
|
14
|
-
User-Agent:
|
15
|
-
- rest-client/2.0.2 (darwin16.1.0 x86_64) ruby/2.4.0p0
|
16
|
-
Content-Type:
|
17
|
-
- application/json
|
18
|
-
Host:
|
19
|
-
- 127.0.0.1:1234
|
20
|
-
response:
|
21
|
-
status:
|
22
|
-
code: 200
|
23
|
-
message: OK
|
24
|
-
headers:
|
25
|
-
Content-Type:
|
26
|
-
- application/json; charset=utf-8
|
27
|
-
Etag:
|
28
|
-
- W/"939eb8a8e26be1cdc231493379b6d17c"
|
29
|
-
Cache-Control:
|
30
|
-
- max-age=0, private, must-revalidate
|
31
|
-
X-Request-Id:
|
32
|
-
- fab0bd54-b471-46d4-8f1f-9165c1549c8f
|
33
|
-
X-Runtime:
|
34
|
-
- '0.263693'
|
35
|
-
Transfer-Encoding:
|
36
|
-
- chunked
|
37
|
-
body:
|
38
|
-
encoding: UTF-8
|
39
|
-
string: '{"name":"Sinking Moon API","versionName":"2.0.0","versionCode":8,"deployDate":"2018-01-02T22:51:29-06:00"}'
|
40
|
-
http_version:
|
41
|
-
recorded_at: Wed, 03 Jan 2018 04:51:29 GMT
|
42
|
-
recorded_with: VCR 3.0.3
|