ocean-rails 7.0.6 → 7.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.rdoc +1 -2
- data/lib/generators/ocean_setup/ocean_setup_generator.rb +1 -1
- data/lib/generators/ocean_setup/templates/Gemfile +1 -1
- data/lib/generators/ocean_setup/templates/spec_helper.rb +5 -5
- data/lib/ocean/api.rb +56 -56
- data/lib/ocean/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 430b30591740120543dc702fc4d8913714ef05278b5643bcdb30cf6013aa8ca6
|
4
|
+
data.tar.gz: e83f2f1be2cfcfe3624ad84646fb7a01115e47991fabd846a940784c289afb16
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a3da705e606f4cf37f3bde01a75cefe0be33e5e3d15cd0d684ff0800967e7f566d7cc334ecf6493a0729d614fd81845eb33849573c4a9a66a0a03a6f60930a3b
|
7
|
+
data.tar.gz: e4eeed6ef60805e502daf224489dcdc597330b4ba34eed5bf00715a316e9c6abe2ec57ccb4b12e1b5ddd866f808b8361369ad6bab659700e93bc3480254444fc
|
data/README.rdoc
CHANGED
@@ -10,13 +10,12 @@ Ocean requires Ruby 2.2 and Ruby on Rails 4.2.0 or later.
|
|
10
10
|
=== What does ocean-rails provide?
|
11
11
|
|
12
12
|
* base classes for Ocean controllers and models (both SQL and DynamoDB).
|
13
|
-
* the Api class, which encapsulates all logic for calling other API services. This includes authentication, authorisation, support for BAN and PURGE, making HTTP requests with retries and backoff, sequentially and in parallel, helpers for running AsyncJobs, sending mail,
|
13
|
+
* the Api class, which encapsulates all logic for calling other API services. This includes authentication, authorisation, support for BAN and PURGE, making HTTP requests with retries and backoff, sequentially and in parallel, helpers for running AsyncJobs, sending mail, and much more. Many of these are very useful even outside of the Ocean context.
|
14
14
|
* the RemoteResource class, which allows you to use a remote Ocean resource as if it were local, with full abstraction of hyperlinks and attributes.
|
15
15
|
* classes which implement Ocean's aggressive caching for controllers and models, and which provide abstractions for collections, matching, searches, grouping, and paging.
|
16
16
|
* controller renderers for collections and errors of various kinds.
|
17
17
|
* the AliveController, which is inherited by all Ocean services. Its only action, +index+, is called by Varnish to determine that the service is operational. Fail-over is performed automatically as a result.
|
18
18
|
* the ErrorController, which intercepts all errors and converts them to the standard Ocean JSON format in the response body.
|
19
|
-
* the logger for the aggregated ZeroMQ Ocean log.
|
20
19
|
* templates and generators to set up new Ocean applications and Ocean resources.
|
21
20
|
|
22
21
|
Thus, the +ocean-rails+ gem provides all the abstractions you need for creating very terse and powerful controller and model code. It's a good idea to browse the documentation.
|
@@ -16,9 +16,9 @@ Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
|
|
16
16
|
# DynamoDB table cleaner
|
17
17
|
OCEAN_ENV = "dev" unless defined?(OCEAN_ENV)
|
18
18
|
regexp = Regexp.new("^.+_#{OCEAN_ENV}_[0-9]{1,3}-[0-9]{1,3}-[0-9]{1,3}-[0-9]{1,3}_(test|dev)$")
|
19
|
-
cleaner = lambda {
|
19
|
+
cleaner = lambda {
|
20
20
|
c = Aws::DynamoDB::Client.new
|
21
|
-
c.list_tables.table_names.each do |t|
|
21
|
+
c.list_tables.table_names.each do |t|
|
22
22
|
begin
|
23
23
|
c.delete_table({table_name: t}) if t =~ regexp
|
24
24
|
rescue Aws::DynamoDB::Errors::LimitExceededException
|
@@ -55,9 +55,9 @@ RSpec.configure do |config|
|
|
55
55
|
# the seed, which is printed after each run.
|
56
56
|
# --seed 1234
|
57
57
|
config.order = "random"
|
58
|
-
|
59
|
-
# Make "
|
60
|
-
config.include
|
58
|
+
|
59
|
+
# Make "FactoryBot" superfluous
|
60
|
+
config.include FactoryBot::Syntax::Methods
|
61
61
|
|
62
62
|
# Uncomment the following two lines if you're using DynamoDB tables
|
63
63
|
#config.before(:suite) { cleaner.call }
|
data/lib/ocean/api.rb
CHANGED
@@ -10,32 +10,32 @@ class Api
|
|
10
10
|
# in master and staging, as the https URIs aren't available for CronJobs,
|
11
11
|
# etc.
|
12
12
|
#
|
13
|
-
def self.internalize_uri(uri
|
13
|
+
def self.internalize_uri(uri) #, ocean_env=OCEAN_ENV)
|
14
14
|
uri.sub(OCEAN_API_URL, INTERNAL_OCEAN_API_URL)
|
15
15
|
end
|
16
16
|
|
17
|
-
|
17
|
+
|
18
18
|
#
|
19
|
-
# When given a symbol or string naming a resource, returns a string
|
19
|
+
# When given a symbol or string naming a resource, returns a string
|
20
20
|
# such as +v1+ naming the latest version for the resource.
|
21
21
|
#
|
22
22
|
def self.version_for(resource_name)
|
23
23
|
API_VERSIONS[resource_name.to_s] || API_VERSIONS['_default']
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
|
27
27
|
#
|
28
28
|
# Adds environment info to the basename, so that testing and execution in various combinations
|
29
|
-
# of the Rails env and the Ocean environment can be done without collision.
|
29
|
+
# of the Rails env and the Ocean environment can be done without collision.
|
30
30
|
#
|
31
|
-
# The ocean_env will always be appended to the basename, since we never want to share queues
|
32
|
-
# between different Ocean environments.
|
31
|
+
# The ocean_env will always be appended to the basename, since we never want to share queues
|
32
|
+
# between different Ocean environments.
|
33
33
|
#
|
34
34
|
# If the ocean_env is 'dev' or 'ci', we must separate things as much as
|
35
|
-
# possible: therefore, we add the local IP number and the Rails environment.
|
35
|
+
# possible: therefore, we add the local IP number and the Rails environment.
|
36
36
|
#
|
37
|
-
# We also add the same information if by any chance the Rails environment isn't 'production'.
|
38
|
-
# This is a precaution; in staging and prod apps should always run in Rails production mode,
|
37
|
+
# We also add the same information if by any chance the Rails environment isn't 'production'.
|
38
|
+
# This is a precaution; in staging and prod apps should always run in Rails production mode,
|
39
39
|
# but if by mistake they don't, we must prevent the production queues from being touched.
|
40
40
|
#
|
41
41
|
# If +suffix_only+ is true, the basename will be excluded from the returned string.
|
@@ -64,7 +64,7 @@ class Api
|
|
64
64
|
# in order to provide lazy evaluation of headers and JSON decoding of the body.
|
65
65
|
#
|
66
66
|
class Response
|
67
|
-
|
67
|
+
|
68
68
|
def initialize(response)
|
69
69
|
@response = response
|
70
70
|
@headers = nil
|
@@ -163,23 +163,23 @@ class Api
|
|
163
163
|
# Automatic retries for GET requests are available:
|
164
164
|
#
|
165
165
|
# +retries+, if given and > 0, specifies the number of retries to perform for GET requests. Defaults to 0, meaning no retries.
|
166
|
-
# +backoff_time+ (default 1) the initial time to wait between retries.
|
166
|
+
# +backoff_time+ (default 1) the initial time to wait between retries.
|
167
167
|
# +backoff_rate+ (default 0.9) the rate at which the time is increased.
|
168
168
|
# +backoff_max+ (default 30) the maximum time to wait between retries.
|
169
169
|
#
|
170
|
-
# The backoff time is increased after each wait period by the product of itself and
|
170
|
+
# The backoff time is increased after each wait period by the product of itself and
|
171
171
|
# backoff_rate. The backoff time is capped by backoff_max. The default time and rate
|
172
|
-
# settings will generate the progression 1, 1.9, 3.61, 6.859, 13.0321, 24.76099, 30, 30,
|
172
|
+
# settings will generate the progression 1, 1.9, 3.61, 6.859, 13.0321, 24.76099, 30, 30,
|
173
173
|
# 30, etc. To disable waiting between retries entirely, set +backoff_time+ to zero.
|
174
174
|
#
|
175
175
|
# If a +postprocessor+ block is given, it will be called with the result of the response;
|
176
176
|
# whatever it returns will be returned as the result of the Api request.
|
177
|
-
#
|
177
|
+
#
|
178
178
|
# +Api.request+ can be called inside a +simultaneously+ block.
|
179
179
|
#
|
180
180
|
def self.request(url, http_method, args: nil, headers: {}, body: nil,
|
181
181
|
credentials: nil,
|
182
|
-
x_api_token: headers['X-API-Token'],
|
182
|
+
x_api_token: headers['X-API-Token'],
|
183
183
|
reauthentication: true,
|
184
184
|
ssl_verifypeer: true, ssl_verifyhost: 2,
|
185
185
|
retries: 0, backoff_time: 1, backoff_rate: 0.9, backoff_max: 30,
|
@@ -198,7 +198,7 @@ class Api
|
|
198
198
|
|
199
199
|
@hydra ||= Typhoeus::Hydra.hydra
|
200
200
|
|
201
|
-
request = nil
|
201
|
+
request = nil
|
202
202
|
response = nil
|
203
203
|
response_getter = lambda { response }
|
204
204
|
|
@@ -208,10 +208,10 @@ class Api
|
|
208
208
|
enqueue_request = lambda do
|
209
209
|
# First construct a request. It will not be sent yet.
|
210
210
|
request = Typhoeus::Request.new(url,
|
211
|
-
method: http_method,
|
211
|
+
method: http_method,
|
212
212
|
headers: headers,
|
213
|
-
params: args,
|
214
|
-
body: body,
|
213
|
+
params: args,
|
214
|
+
body: body,
|
215
215
|
ssl_verifypeer: ssl_verifypeer,
|
216
216
|
ssl_verifyhost: ssl_verifyhost,
|
217
217
|
timeout: timeout)
|
@@ -243,7 +243,7 @@ class Api
|
|
243
243
|
enqueue_request.call
|
244
244
|
else
|
245
245
|
nil # Done, fail
|
246
|
-
end
|
246
|
+
end
|
247
247
|
when 400..499
|
248
248
|
nil # Done, fail
|
249
249
|
else
|
@@ -280,7 +280,7 @@ class Api
|
|
280
280
|
end
|
281
281
|
|
282
282
|
|
283
|
-
#
|
283
|
+
#
|
284
284
|
# Api.simultaneously is used for making requests in parallel. For example:
|
285
285
|
#
|
286
286
|
# results = Api.simultaneously do |r|
|
@@ -298,11 +298,11 @@ class Api
|
|
298
298
|
#
|
299
299
|
def self.simultaneously (&block)
|
300
300
|
raise "block required" unless block
|
301
|
-
@inside_simultaneously = true
|
301
|
+
@inside_simultaneously = true
|
302
302
|
results = []
|
303
|
-
@hydra = nil
|
303
|
+
@hydra = nil
|
304
304
|
block.call(results)
|
305
|
-
Typhoeus::Config.memoize = true
|
305
|
+
Typhoeus::Config.memoize = true
|
306
306
|
@hydra.run if @hydra
|
307
307
|
results.map(&:call)
|
308
308
|
ensure
|
@@ -323,9 +323,9 @@ class Api
|
|
323
323
|
# Makes an internal +PURGE+ call to all Varnish instances. The call is made in parallel.
|
324
324
|
# Varnish will only accept +PURGE+ requests coming from the local network.
|
325
325
|
#
|
326
|
-
def self.purge(*args)
|
326
|
+
def self.purge(*args)
|
327
327
|
hydra = Typhoeus::Hydra.hydra
|
328
|
-
LOAD_BALANCERS.each do |host|
|
328
|
+
LOAD_BALANCERS.each do |host|
|
329
329
|
url = "http://#{host}#{path}"
|
330
330
|
request = Typhoeus::Request.new(url, method: :purge, headers: {})
|
331
331
|
hydra.queue request
|
@@ -338,10 +338,10 @@ class Api
|
|
338
338
|
# Makes an internal +BAN+ call to all Varnish instances. The call is made in parallel.
|
339
339
|
# Varnish will only accept +BAN+ requests coming from the local network.
|
340
340
|
#
|
341
|
-
def self.ban(path)
|
341
|
+
def self.ban(path)
|
342
342
|
hydra = Typhoeus::Hydra.hydra
|
343
343
|
escaped_path = escape(path)
|
344
|
-
LOAD_BALANCERS.each do |host|
|
344
|
+
LOAD_BALANCERS.each do |host|
|
345
345
|
url = "http://#{host}#{escaped_path}"
|
346
346
|
request = Typhoeus::Request.new(url, method: :ban, headers: {})
|
347
347
|
hydra.queue request
|
@@ -374,17 +374,17 @@ class Api
|
|
374
374
|
@service_token = nil
|
375
375
|
end
|
376
376
|
|
377
|
-
|
377
|
+
|
378
378
|
#
|
379
379
|
# Authenticates against the Auth service (which must be deployed and running) with
|
380
380
|
# a given +username+ and +password+. If successful, the authentication token is returned.
|
381
|
-
# If the credentials match the service's own, the token is also assigned to the instance variable @service_token.
|
381
|
+
# If the credentials match the service's own, the token is also assigned to the instance variable @service_token.
|
382
382
|
# If not successful, +nil+ is returned.
|
383
383
|
#
|
384
384
|
def self.authenticate(username=API_USER, password=API_PASSWORD)
|
385
385
|
attempts = 3
|
386
386
|
loop do
|
387
|
-
response = Typhoeus.post "#{INTERNAL_OCEAN_API_URL}/v1/authentications",
|
387
|
+
response = Typhoeus.post "#{INTERNAL_OCEAN_API_URL}/v1/authentications",
|
388
388
|
body: "", headers: {'X-API-Authenticate' => credentials(username, password)}
|
389
389
|
case response.code
|
390
390
|
when 100..199
|
@@ -397,7 +397,7 @@ class Api
|
|
397
397
|
return token
|
398
398
|
when 403
|
399
399
|
# Does not authenticate. Don't repeat the request.
|
400
|
-
return nil
|
400
|
+
return nil
|
401
401
|
when 400
|
402
402
|
# Malformed credentials. Don't repeat the request.
|
403
403
|
return nil
|
@@ -405,7 +405,7 @@ class Api
|
|
405
405
|
# Client error. Don't repeat the request.
|
406
406
|
return nil
|
407
407
|
when 500
|
408
|
-
# Error. Don't repeat.
|
408
|
+
# Error. Don't repeat.
|
409
409
|
return nil if attempts < 1
|
410
410
|
attempts -= 1
|
411
411
|
next
|
@@ -415,10 +415,10 @@ class Api
|
|
415
415
|
end
|
416
416
|
end
|
417
417
|
end
|
418
|
-
|
418
|
+
|
419
419
|
|
420
420
|
#
|
421
|
-
# Encodes a username and password for authentication in the format used for standard HTTP
|
421
|
+
# Encodes a username and password for authentication in the format used for standard HTTP
|
422
422
|
# authentication. The encoding can be reversed and is intended only to lightly mask the
|
423
423
|
# credentials so that they're not immediately apparent when reading logs.
|
424
424
|
#
|
@@ -428,14 +428,14 @@ class Api
|
|
428
428
|
password ||= API_PASSWORD
|
429
429
|
::Base64.strict_encode64 "#{username}:#{password}"
|
430
430
|
end
|
431
|
-
|
431
|
+
|
432
432
|
|
433
433
|
#
|
434
434
|
# Takes encoded credentials (e.g. by Api.encode_credentials) and returns a two-element array
|
435
435
|
# where the first element is the username and the second is the password. If the encoded
|
436
436
|
# credentials are missing or can't be decoded properly, ["", ""] is returned. This allows
|
437
437
|
# you to write:
|
438
|
-
#
|
438
|
+
#
|
439
439
|
# un, pw = Api.decode_credentials(creds)
|
440
440
|
# raise "Please supply your username and password" if un.blank? || pw.blank?
|
441
441
|
#
|
@@ -444,10 +444,10 @@ class Api
|
|
444
444
|
username, password = ::Base64.decode64(encoded).split(':', 2)
|
445
445
|
[username || "", password || ""]
|
446
446
|
end
|
447
|
-
|
447
|
+
|
448
448
|
|
449
449
|
#
|
450
|
-
# Performs authorisation against the Auth service. The +token+ must be a token received as a
|
450
|
+
# Performs authorisation against the Auth service. The +token+ must be a token received as a
|
451
451
|
# result of a prior authentication operation. The args should be in the form
|
452
452
|
#
|
453
453
|
# query: "service:controller:hyperlink:verb:app:context"
|
@@ -457,18 +457,18 @@ class Api
|
|
457
457
|
# Api.permitted?(@service_token, query: "cms:texts:self:GET:*:*")
|
458
458
|
#
|
459
459
|
# Api.authorization_string can be used to produce the query string.
|
460
|
-
#
|
460
|
+
#
|
461
461
|
# Returns the HTTP response as-is, allowing the caller to examine the status code and
|
462
462
|
# messages, and also the body.
|
463
463
|
#
|
464
464
|
def self.permitted?(token, args={})
|
465
465
|
raise unless token
|
466
466
|
Api.request "/v1/authentications/#{token}", :get, args: args
|
467
|
-
end
|
467
|
+
end
|
468
468
|
|
469
469
|
|
470
470
|
#
|
471
|
-
# Returns an authorisation string suitable for use in calls to Api.permitted?.
|
471
|
+
# Returns an authorisation string suitable for use in calls to Api.permitted?.
|
472
472
|
# The +extra_actions+ arg holds the extra actions as defined in the Ocean controller; it must
|
473
473
|
# be included here so that actions can be mapped to the proper hyperlink and verb.
|
474
474
|
# The +controller+ and +action+ args are mandatory. The +app+ and +context+ args are optional and will
|
@@ -485,8 +485,8 @@ class Api
|
|
485
485
|
#
|
486
486
|
# These are the default controller actions. The purpose of this constant is to map action
|
487
487
|
# names to hyperlink and HTTP method (for authorisation purposes). Don't be alarmed by the
|
488
|
-
# non-standard GET* - it's purely symbolic and is never used as an actual HTTP method.
|
489
|
-
# We need it to differentiate between a +GET+ of a member and a +GET+ of a collection of members.
|
488
|
+
# non-standard GET* - it's purely symbolic and is never used as an actual HTTP method.
|
489
|
+
# We need it to differentiate between a +GET+ of a member and a +GET+ of a collection of members.
|
490
490
|
# The +extra_actions+ keyword in +ocean_resource_controller+ follows the same format.
|
491
491
|
#
|
492
492
|
DEFAULT_ACTIONS = {
|
@@ -515,16 +515,16 @@ class Api
|
|
515
515
|
#
|
516
516
|
# Send an email asynchronously. The Mailer role is required.
|
517
517
|
#
|
518
|
-
def self.send_mail(from: "nobody@#{BASE_DOMAIN}", to: nil,
|
519
|
-
subject: nil,
|
518
|
+
def self.send_mail(from: "nobody@#{BASE_DOMAIN}", to: nil,
|
519
|
+
subject: nil,
|
520
520
|
plaintext: nil, html: nil,
|
521
521
|
plaintext_url: nil, html_url: nil, substitutions: nil)
|
522
|
-
Api.request "/v1/mails", :post,
|
522
|
+
Api.request "/v1/mails", :post,
|
523
523
|
x_api_token: Api.service_token, credentials: Api.credentials,
|
524
524
|
body: {
|
525
|
-
from: from, to: to, subject: subject,
|
525
|
+
from: from, to: to, subject: subject,
|
526
526
|
plaintext: plaintext, html: html,
|
527
|
-
plaintext_url: plaintext_url, html_url: html_url,
|
527
|
+
plaintext_url: plaintext_url, html_url: html_url,
|
528
528
|
substitutions: substitutions
|
529
529
|
}.to_json
|
530
530
|
end
|
@@ -542,14 +542,14 @@ class Api
|
|
542
542
|
# Api.async_body steps: [{"url" => "#{INTERNAL_OCEAN_API_URL}/v1/foos"}
|
543
543
|
# "method" => "PUT",
|
544
544
|
# "body" => {}}]
|
545
|
-
#
|
545
|
+
#
|
546
546
|
# A URL not starting with a / will be internalized.
|
547
547
|
#
|
548
548
|
def self.async_job_body(href=nil, method=:get, body: {},
|
549
|
-
credentials: nil,
|
549
|
+
credentials: nil,
|
550
550
|
token: nil,
|
551
551
|
steps: nil,
|
552
|
-
default_step_time: nil,
|
552
|
+
default_step_time: nil,
|
553
553
|
default_poison_limit: nil,
|
554
554
|
max_seconds_in_queue: nil,
|
555
555
|
poison_email: nil)
|
@@ -558,7 +558,7 @@ class Api
|
|
558
558
|
method = method && method.to_s.upcase
|
559
559
|
href = href && (href.first == "/" ? "#{INTERNAL_OCEAN_API_URL}#{href}" : Api.internalize_uri(href))
|
560
560
|
steps = if href && method
|
561
|
-
if ["POST","PUT"].include? method
|
561
|
+
if ["POST","PUT"].include? method
|
562
562
|
[{"url" => href, "method" => method, "body" => body}]
|
563
563
|
else
|
564
564
|
[{"url" => href, "method" => method}]
|
@@ -587,11 +587,11 @@ class Api
|
|
587
587
|
# A block may be given. It will be called with any +TimeoutError+ or +NoResponseError+,
|
588
588
|
# which allows +Api.run_async_job+ to be used very tersely in controllers, e.g.:
|
589
589
|
#
|
590
|
-
# Api.run_async_job(...) do |e|
|
590
|
+
# Api.run_async_job(...) do |e|
|
591
591
|
# render_api_error 422, e.message
|
592
592
|
# return
|
593
593
|
# end
|
594
|
-
#
|
594
|
+
#
|
595
595
|
def self.run_async_job(href=nil, method=nil, job: nil, **keywords, &block)
|
596
596
|
job ||= async_job_body(href, method, **keywords)
|
597
597
|
Api.request "/v1/async_jobs", :post,
|
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: 7.0
|
4
|
+
version: 7.1.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:
|
11
|
+
date: 2018-04-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: typhoeus
|
@@ -151,7 +151,7 @@ dependencies:
|
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: '0'
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
|
-
name:
|
154
|
+
name: factory_bot_rails
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
156
156
|
requirements:
|
157
157
|
- - "~>"
|
@@ -270,7 +270,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
270
270
|
version: '0'
|
271
271
|
requirements: []
|
272
272
|
rubyforge_project:
|
273
|
-
rubygems_version: 2.6
|
273
|
+
rubygems_version: 2.7.6
|
274
274
|
signing_key:
|
275
275
|
specification_version: 4
|
276
276
|
summary: This gem implements common Ocean behaviour for Ruby and Ruby on Rails.
|