ocean-rails 3.6.1 → 3.7.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 +4 -4
- data/lib/ocean/api_remote_resource.rb +205 -40
- data/lib/ocean/ocean_application_controller.rb +0 -19
- data/lib/ocean/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77809e9f1822672d4c90aeb4a2409d237acab4c0
|
4
|
+
data.tar.gz: 83438c0ffeb2603067f0f05d8ffb0a29e8926d63
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f053a42f438109874ac1997c974b9528cada2c4e2bdb1bfe0bfe87ab9279f60eedfe046840704b27724836bf0df9fc559d5a0536f8c068cc490c7b98b29dc773
|
7
|
+
data.tar.gz: 4494b55bfe4aed431f141b5789eb68debb938b97c1269263096e4f377805529f791c76cabe05983c7b1bc36e24511f25359bfd232e4c9c36393c9410a2649787
|
@@ -48,19 +48,41 @@ class Api
|
|
48
48
|
# Api::RemoteResource.get!("foo") returns a new RemoteResource or raises an error
|
49
49
|
# Api::RemoteResource.new("foo").get! returns the RemoteResource or raises an error
|
50
50
|
#
|
51
|
-
# #post, #post!, #put, #put!, #delete, and #delete! are also available.
|
52
|
-
#
|
53
|
-
# If get or get! retrieve an Ocean collection, they will return an array of RemoteResources.
|
54
|
-
#
|
55
51
|
# +thing.get!+ returns the RemoteResource if successful. If not, raises an exception.
|
56
52
|
# +thing.get+ does the same, but always returns the RemoteResource. The remote resource
|
57
53
|
# can be examined to see its status.
|
58
54
|
#
|
55
|
+
# Hyperlinks can be specified to avoid hardcoding API URIs. This is essential. Simply specify
|
56
|
+
# the hyperlink as the first parameter to the instance versions of the HTTP accessors:
|
57
|
+
#
|
58
|
+
# x = Api::RemoteResource.new("http://foo.com/x")
|
59
|
+
# x.get(:creator)
|
60
|
+
# x.get!(:creator)['username']
|
61
|
+
# x.put(:confirm, body: {x:2, y:3})
|
62
|
+
# x.post(:bars, body: {username: "Bl0feld", password: "SPECTRE"})
|
63
|
+
# x.delete(:expired)
|
64
|
+
#
|
65
|
+
# Note the difference between:
|
66
|
+
#
|
67
|
+
# x.put(body: {x:2, y:3})
|
68
|
+
# x.put(:self, body: {x:2, y:3})
|
69
|
+
# x.put(:confirm, body: {x:2, y:3})
|
70
|
+
#
|
71
|
+
# The first two are logically equivalent: they both make a PUT request to x itself,
|
72
|
+
# though in the second case, the self hyperlink is explicit (the first arg defaults to
|
73
|
+
# :self). The third case sends a PUT request to x's hyperlink :confirm. From this follows
|
74
|
+
# that the following also are equivalent:
|
75
|
+
#
|
76
|
+
# x.get
|
77
|
+
# x.get(:self)
|
78
|
+
#
|
79
|
+
# #post, #post!, #put, #put!, #delete, and #delete! are also available.
|
80
|
+
#
|
59
81
|
# Exceptions:
|
60
82
|
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
83
|
+
# GetFailed raised when the GET to obtain the remote resource has failed.
|
84
|
+
# UnparseableJson raised when the response body doesn't parse as JSON.
|
85
|
+
# JsonIsNoResource raised when the structure of the parsed JSON body is not a resource.
|
64
86
|
#
|
65
87
|
# To parse as a resource, the JSON must be a wrapped Ocean resource of this format:
|
66
88
|
#
|
@@ -79,8 +101,12 @@ class Api
|
|
79
101
|
#
|
80
102
|
# To refresh a resource using a conditional GET:
|
81
103
|
#
|
82
|
-
#
|
83
|
-
#
|
104
|
+
# thing.refresh
|
105
|
+
# thing.refresh!
|
106
|
+
#
|
107
|
+
# #refresh returns the resource itself, so you can do
|
108
|
+
#
|
109
|
+
# thing.refresh['updated_at']
|
84
110
|
#
|
85
111
|
# NB: this class can also be used to fetch any JSON data. E.g.:
|
86
112
|
#
|
@@ -91,6 +117,9 @@ class Api
|
|
91
117
|
# +nil+, you can always chack +#status+, +#status_message+, and/or +#headers+ to determine what
|
92
118
|
# went wrong. After fetching non-resource data, +#present?+ will always be false.
|
93
119
|
#
|
120
|
+
# TODO: 1. If .get or .get! retrieve an Ocean collection, let them return an array of RemoteResources.
|
121
|
+
# 2. Implement refresh.
|
122
|
+
#
|
94
123
|
class RemoteResource
|
95
124
|
|
96
125
|
attr_reader :uri, :content_type, :retries, :backoff_time, :backoff_rate, :backoff_max
|
@@ -123,26 +152,6 @@ class Api
|
|
123
152
|
end
|
124
153
|
|
125
154
|
|
126
|
-
def self.get!(*args)
|
127
|
-
_retrieve new(*args)
|
128
|
-
end
|
129
|
-
|
130
|
-
def get!
|
131
|
-
RemoteResource._retrieve self unless present?
|
132
|
-
self
|
133
|
-
end
|
134
|
-
|
135
|
-
def self.get(*args)
|
136
|
-
rr = new(*args)
|
137
|
-
rr.get! rescue nil
|
138
|
-
rr
|
139
|
-
end
|
140
|
-
|
141
|
-
def get
|
142
|
-
get! rescue nil
|
143
|
-
self
|
144
|
-
end
|
145
|
-
|
146
155
|
def [](key)
|
147
156
|
get!
|
148
157
|
resource[key]
|
@@ -165,18 +174,108 @@ class Api
|
|
165
174
|
end
|
166
175
|
|
167
176
|
|
177
|
+
class PostFailed < StandardError; end
|
168
178
|
class GetFailed < StandardError; end
|
179
|
+
class PutFailed < StandardError; end
|
180
|
+
class DeleteFailed < StandardError; end
|
169
181
|
class UnparseableJson < StandardError; end
|
170
182
|
class JsonIsNoResource < StandardError; end
|
171
183
|
|
172
184
|
|
185
|
+
def self.get!(*args)
|
186
|
+
_retrieve new(*args)
|
187
|
+
end
|
188
|
+
|
189
|
+
def self.get(*args)
|
190
|
+
rr = new(*args)
|
191
|
+
rr.get! rescue nil
|
192
|
+
rr
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
def get!(hlink=nil)
|
197
|
+
RemoteResource._retrieve self unless present?
|
198
|
+
hlink = hlink.to_s if hlink
|
199
|
+
if !hlink || hlink == 'self'
|
200
|
+
self
|
201
|
+
else
|
202
|
+
RemoteResource.get!(hyperlink[hlink]['href'])
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def get(hlink=nil)
|
207
|
+
get! rescue nil
|
208
|
+
hlink = hlink.to_s if hlink
|
209
|
+
if !hlink || hlink == 'self'
|
210
|
+
self
|
211
|
+
else
|
212
|
+
RemoteResource.get(hyperlink[hlink]['href'])
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
def put!(hlink=nil, body: {})
|
218
|
+
get!
|
219
|
+
hlink = hlink.to_s if hlink
|
220
|
+
if !hlink || hlink == 'self'
|
221
|
+
_modify(body)
|
222
|
+
self
|
223
|
+
else
|
224
|
+
hl_res = RemoteResource.new(self.hyperlink[hlink]['href'])
|
225
|
+
hl_res.send :_modify, body
|
226
|
+
hl_res
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def put(hlink=nil, body: {})
|
231
|
+
get
|
232
|
+
hlink = hlink.to_s if hlink
|
233
|
+
if !hlink || hlink == 'self'
|
234
|
+
_modify(body) rescue nil
|
235
|
+
self
|
236
|
+
else
|
237
|
+
hl_res = RemoteResource.new(self.hyperlink[hlink]['href'])
|
238
|
+
hl_res.send :_modify, body
|
239
|
+
hl_res
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
|
244
|
+
def post!(hlink=nil, body: {})
|
245
|
+
get!
|
246
|
+
hlink = (hlink || 'self').to_s
|
247
|
+
_create(hyperlink[hlink]['href'], body)
|
248
|
+
end
|
249
|
+
|
250
|
+
def post(hlink=nil, body: {})
|
251
|
+
get
|
252
|
+
hlink = (hlink || 'self').to_s
|
253
|
+
_create(hyperlink[hlink]['href'], body) rescue nil
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
def delete!(hlink=nil)
|
258
|
+
get!
|
259
|
+
hlink = (hlink || 'self').to_s
|
260
|
+
_destroy(hyperlink[hlink]['href'])
|
261
|
+
self
|
262
|
+
end
|
263
|
+
|
264
|
+
def delete(hlink=nil)
|
265
|
+
get
|
266
|
+
hlink = (hlink || 'self').to_s
|
267
|
+
_destroy(hyperlink[hlink]['href']) rescue nil
|
268
|
+
self
|
269
|
+
end
|
270
|
+
|
271
|
+
|
173
272
|
private
|
174
273
|
|
175
274
|
attr_accessor :present
|
176
275
|
attr_writer :raw, :resource, :resource_type, :status, :headers, :status_message
|
177
276
|
|
178
277
|
|
179
|
-
def self.
|
278
|
+
def self._credentials(rr)
|
180
279
|
if rr.credentials.present? && rr.credentials != Api.credentials(API_USER, API_PASSWORD)
|
181
280
|
credentials = rr.credentials
|
182
281
|
token = rr.x_api_token
|
@@ -184,6 +283,12 @@ class Api
|
|
184
283
|
credentials = nil
|
185
284
|
token = rr.x_api_token || Api.service_token
|
186
285
|
end
|
286
|
+
[credentials, token]
|
287
|
+
end
|
288
|
+
|
289
|
+
|
290
|
+
def self._retrieve(rr)
|
291
|
+
credentials, token = _credentials(rr)
|
187
292
|
response = Api.request rr.uri, :get, headers: {}, credentials: credentials, x_api_token: token,
|
188
293
|
retries: rr.retries, backoff_time: rr.backoff_time, backoff_rate: rr.backoff_rate,
|
189
294
|
backoff_max: rr.backoff_max
|
@@ -203,16 +308,9 @@ class Api
|
|
203
308
|
|
204
309
|
def _setup(body, response)
|
205
310
|
self.raw = body
|
206
|
-
|
207
|
-
|
208
|
-
resource_type
|
209
|
-
raise JsonIsNoResource unless value.is_a? Hash
|
210
|
-
raise JsonIsNoResource unless value['_links'].is_a? Hash
|
211
|
-
raise JsonIsNoResource unless value['_links']['self'].is_a? Hash
|
212
|
-
raise JsonIsNoResource unless value['_links']['self']['href'].is_a? String
|
213
|
-
raise JsonIsNoResource unless value['_links']['self']['type'].is_a? String
|
214
|
-
self.resource = value
|
215
|
-
self.resource_type = resource_type
|
311
|
+
type, resource = verify_resource body
|
312
|
+
self.resource = resource
|
313
|
+
self.resource_type = type
|
216
314
|
self.status = response.status
|
217
315
|
self.status_message = response.message
|
218
316
|
self.headers = response.headers
|
@@ -220,5 +318,72 @@ class Api
|
|
220
318
|
end
|
221
319
|
|
222
320
|
|
321
|
+
def verify_resource(json)
|
322
|
+
raise JsonIsNoResource unless json.is_a? Hash
|
323
|
+
raise JsonIsNoResource unless json.size == 1
|
324
|
+
resource_type, attributes = json.to_a.first
|
325
|
+
raise JsonIsNoResource unless attributes.is_a? Hash
|
326
|
+
raise JsonIsNoResource unless attributes['_links'].is_a? Hash
|
327
|
+
raise JsonIsNoResource unless attributes['_links']['self'].is_a? Hash
|
328
|
+
raise JsonIsNoResource unless attributes['_links']['self']['href'].is_a? String
|
329
|
+
raise JsonIsNoResource unless attributes['_links']['self']['type'].is_a? String
|
330
|
+
[resource_type, attributes]
|
331
|
+
end
|
332
|
+
|
333
|
+
|
334
|
+
def _modify(body)
|
335
|
+
credentials, token = RemoteResource._credentials(self)
|
336
|
+
response = Api.request uri, :put, headers: {}, body: body,
|
337
|
+
credentials: credentials, x_api_token: token,
|
338
|
+
retries: retries, backoff_time: backoff_time, backoff_rate: backoff_rate,
|
339
|
+
backoff_max: backoff_max
|
340
|
+
raise PutFailed unless response.success?
|
341
|
+
begin
|
342
|
+
self.raw = response.body
|
343
|
+
rescue JSON::ParserError
|
344
|
+
raise UnparseableJson
|
345
|
+
end
|
346
|
+
type, resource = verify_resource(response.body) rescue return
|
347
|
+
return if resource_type && type != resource_type
|
348
|
+
self.resource = resource
|
349
|
+
self.resource_type = type
|
350
|
+
self.status = response.status
|
351
|
+
self.status_message = response.message
|
352
|
+
self.headers = response.headers
|
353
|
+
self.present = true
|
354
|
+
end
|
355
|
+
|
356
|
+
|
357
|
+
def _create(post_uri, body)
|
358
|
+
credentials, token = RemoteResource._credentials(self)
|
359
|
+
response = Api.request post_uri, :post, headers: {}, body: body,
|
360
|
+
credentials: credentials, x_api_token: token,
|
361
|
+
retries: retries, backoff_time: backoff_time, backoff_rate: backoff_rate,
|
362
|
+
backoff_max: backoff_max
|
363
|
+
raise PostFailed unless response.success?
|
364
|
+
begin
|
365
|
+
raw = response.body
|
366
|
+
rescue JSON::ParserError
|
367
|
+
raise UnparseableJson
|
368
|
+
end
|
369
|
+
type, resource = verify_resource(response.body)
|
370
|
+
created = RemoteResource.new(post_uri)
|
371
|
+
created.send :_setup, raw, response
|
372
|
+
created
|
373
|
+
end
|
374
|
+
|
375
|
+
|
376
|
+
def _destroy(delete_uri)
|
377
|
+
credentials, token = RemoteResource._credentials(self)
|
378
|
+
response = Api.request delete_uri, :delete, headers: {},
|
379
|
+
credentials: credentials, x_api_token: token,
|
380
|
+
retries: retries, backoff_time: backoff_time, backoff_rate: backoff_rate,
|
381
|
+
backoff_max: backoff_max
|
382
|
+
self.status = response.status
|
383
|
+
self.status_message = response.message
|
384
|
+
self.headers = response.headers
|
385
|
+
raise DeleteFailed unless response.success?
|
386
|
+
end
|
387
|
+
|
223
388
|
end
|
224
389
|
end
|
@@ -83,25 +83,6 @@ module OceanApplicationController
|
|
83
83
|
end
|
84
84
|
|
85
85
|
|
86
|
-
# #
|
87
|
-
# # Requires the request to be conditional: it must have an +If-None-Match+ and/or an
|
88
|
-
# # +If-Modified-Since+ HTTP header. If the request isn't conditional, a 428 error is
|
89
|
-
# # returned. The body will be a standard API error message, with two strings:
|
90
|
-
# # <tt>"Precondition Required"</tt> and <tt>"If-None-Match and/or If-Modified-Since missing"</tt>.
|
91
|
-
# #
|
92
|
-
# def require_conditional
|
93
|
-
# if request.headers['If-None-Match'].blank? && request.headers['If-Modified-Since'].blank?
|
94
|
-
# render_api_error 428, "Precondition Required",
|
95
|
-
# "If-None-Match and/or If-Modified-Since missing"
|
96
|
-
# #expires_in 0, must_revalidate: true
|
97
|
-
# expires_now
|
98
|
-
# false
|
99
|
-
# else
|
100
|
-
# true
|
101
|
-
# end
|
102
|
-
# end
|
103
|
-
|
104
|
-
|
105
86
|
#
|
106
87
|
# Updates +created_by+ and +updated_by+ to the ApiUser for which the current request
|
107
88
|
# is authorised. The attributes can be declared either String (recommended) or
|
data/lib/ocean/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ocean-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Bengtson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-10-
|
11
|
+
date: 2014-10-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: typhoeus
|