ocean-rails 1.14.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/MIT-LICENSE +20 -0
- data/README.rdoc +72 -0
- data/Rakefile +38 -0
- data/lib/generators/ocean_scaffold/USAGE +8 -0
- data/lib/generators/ocean_scaffold/ocean_scaffold_generator.rb +76 -0
- data/lib/generators/ocean_scaffold/templates/controller_specs/create_spec.rb +71 -0
- data/lib/generators/ocean_scaffold/templates/controller_specs/delete_spec.rb +47 -0
- data/lib/generators/ocean_scaffold/templates/controller_specs/index_spec.rb +45 -0
- data/lib/generators/ocean_scaffold/templates/controller_specs/show_spec.rb +43 -0
- data/lib/generators/ocean_scaffold/templates/controller_specs/update_spec.rb +85 -0
- data/lib/generators/ocean_scaffold/templates/model_spec.rb +76 -0
- data/lib/generators/ocean_scaffold/templates/resource_routing_spec.rb +27 -0
- data/lib/generators/ocean_scaffold/templates/view_specs/_resource_spec.rb +55 -0
- data/lib/generators/ocean_scaffold/templates/views/_resource.json.jbuilder +8 -0
- data/lib/generators/ocean_setup/USAGE +8 -0
- data/lib/generators/ocean_setup/ocean_setup_generator.rb +93 -0
- data/lib/generators/ocean_setup/templates/Gemfile +19 -0
- data/lib/generators/ocean_setup/templates/alive_controller.rb +18 -0
- data/lib/generators/ocean_setup/templates/alive_routing_spec.rb +11 -0
- data/lib/generators/ocean_setup/templates/alive_spec.rb +12 -0
- data/lib/generators/ocean_setup/templates/api_constants.rb +19 -0
- data/lib/generators/ocean_setup/templates/application_controller.rb +8 -0
- data/lib/generators/ocean_setup/templates/application_helper.rb +34 -0
- data/lib/generators/ocean_setup/templates/config.yml.example +57 -0
- data/lib/generators/ocean_setup/templates/errors_controller.rb +14 -0
- data/lib/generators/ocean_setup/templates/gitignore +37 -0
- data/lib/generators/ocean_setup/templates/hyperlinks.rb +22 -0
- data/lib/generators/ocean_setup/templates/ocean_constants.rb +36 -0
- data/lib/generators/ocean_setup/templates/routes.rb +8 -0
- data/lib/generators/ocean_setup/templates/spec_helper.rb +47 -0
- data/lib/generators/ocean_setup/templates/zeromq_logger.rb +15 -0
- data/lib/ocean-rails.rb +38 -0
- data/lib/ocean/api.rb +263 -0
- data/lib/ocean/api_resource.rb +135 -0
- data/lib/ocean/flooding.rb +29 -0
- data/lib/ocean/ocean_application_controller.rb +214 -0
- data/lib/ocean/ocean_resource_controller.rb +76 -0
- data/lib/ocean/ocean_resource_model.rb +61 -0
- data/lib/ocean/selective_rack_logger.rb +33 -0
- data/lib/ocean/version.rb +3 -0
- data/lib/ocean/zero_log.rb +184 -0
- data/lib/ocean/zeromq_logger.rb +42 -0
- data/lib/tasks/ocean_tasks.rake +4 -0
- data/lib/template.rb +31 -0
- data/lib/templates/rails/scaffold_controller/controller.rb +91 -0
- metadata +267 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
class ErrorsController < ApplicationController
|
2
|
+
|
3
|
+
skip_before_action :require_x_api_token
|
4
|
+
skip_before_action :authorize_action
|
5
|
+
|
6
|
+
|
7
|
+
def show
|
8
|
+
@exception = env['action_dispatch.exception']
|
9
|
+
@status_code = ActionDispatch::ExceptionWrapper.new(env, @exception).status_code
|
10
|
+
#@rescue_response = ActionDispatch::ExceptionWrapper.rescue_responses[@exception.class.name]
|
11
|
+
render_api_error @status_code, @exception.message
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
2
|
+
#
|
3
|
+
# If you find yourself ignoring temporary files generated by your text editor
|
4
|
+
# or operating system, you probably want to add a global ignore instead:
|
5
|
+
# git config --global core.excludesfile ~/.gitignore_global
|
6
|
+
|
7
|
+
# Ignore bundler config
|
8
|
+
/.bundle
|
9
|
+
|
10
|
+
# Ignore the default SQLite database.
|
11
|
+
/db/*.sqlite3
|
12
|
+
|
13
|
+
# Ignore all logfiles and tempfiles.
|
14
|
+
/log/*.log
|
15
|
+
/tmp
|
16
|
+
*.log
|
17
|
+
|
18
|
+
# Mac OS X
|
19
|
+
.DS_Store
|
20
|
+
|
21
|
+
# Ignore backup files
|
22
|
+
*~
|
23
|
+
|
24
|
+
# Ignore RubyMine local workspace settings
|
25
|
+
/.idea/workspace.xml
|
26
|
+
|
27
|
+
# Ignore any SQLite files
|
28
|
+
/db/*.sqlite3
|
29
|
+
|
30
|
+
# Ignore coverage artefacts
|
31
|
+
/coverage
|
32
|
+
|
33
|
+
# Ignore the tailored config.yml file
|
34
|
+
config/config.yml
|
35
|
+
|
36
|
+
# Ignore the Tork config dir
|
37
|
+
/.tork
|
@@ -0,0 +1,22 @@
|
|
1
|
+
RSpec::Matchers.define :be_hyperlinked do |link, regex, type="application/json"|
|
2
|
+
|
3
|
+
match do |hyperlinks|
|
4
|
+
(@h = hyperlinks[link]) &&
|
5
|
+
(@h['href'] =~ regex) &&
|
6
|
+
(@h['type'] == type)
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
failure_message_for_should do |actual|
|
11
|
+
"expected the resource representation to " + description
|
12
|
+
end
|
13
|
+
|
14
|
+
description do
|
15
|
+
result = "have a '#{link}' hyperlink"
|
16
|
+
result += " containing '#{regex.source}' in the URI" unless @h['href'] =~ regex
|
17
|
+
result += " leading to a resource of type '#{type}' (was '#{@h['type']}')" unless @h['type'] == type
|
18
|
+
result
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#
|
2
|
+
# This file will be replaced by an auto-generated one in deployment.
|
3
|
+
# YOU SHOULD NEVER CHANGE THE CONTENTS OF THIS FILE.
|
4
|
+
#
|
5
|
+
# Backend developers should never need to override the value here.
|
6
|
+
# The reason for this is that when developing services locally,
|
7
|
+
# their tests run in isolation with all external calls mocked away,
|
8
|
+
# and thus it doesn't matter what URLs a service generates when
|
9
|
+
# running tests locally on a developer's machine.
|
10
|
+
#
|
11
|
+
# If you're a frontend developer, however, the point of your testing
|
12
|
+
# is to exercise the entire SOA and thus you need access to a
|
13
|
+
# complete and fully functional system (which might or might not
|
14
|
+
# make calls to partners' systems, such as for hotel bookings).
|
15
|
+
#
|
16
|
+
# Thus, if you are a frontend developer, you override the string
|
17
|
+
# constant here to reflect the Chef environment (master, staging)
|
18
|
+
# you wish to run your local tests against by defining the environment
|
19
|
+
# variable OVERRIDE_OCEAN_API_HOST.
|
20
|
+
#
|
21
|
+
# When TeamCity runs its tests, it will set these constants to values
|
22
|
+
# appropriate for the Chef environment for which the tests are run.
|
23
|
+
# Thus, TeamCity will always run master branch frontend tests against
|
24
|
+
# the master Chef environment. However, you can run a personal build
|
25
|
+
# and specify the OCEAN_API_HOST value as an environment param in the
|
26
|
+
# build dialog.
|
27
|
+
#
|
28
|
+
|
29
|
+
OCEAN_API_HOST = (Rails.env == 'test' && "forbidden.#{BASE_DOMAIN}") ||
|
30
|
+
(ENV['OVERRIDE_OCEAN_API_HOST'] ||
|
31
|
+
ENV['OCEAN_API_HOST'] ||
|
32
|
+
"master-api.#{BASE_DOMAIN}").sub("<default>", "master")
|
33
|
+
|
34
|
+
OCEAN_API_URL = "https://#{OCEAN_API_HOST}"
|
35
|
+
|
36
|
+
INTERNAL_OCEAN_API_URL = OCEAN_API_URL.sub("https", "http").sub("api.", "lb.")
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start do
|
3
|
+
add_filter "/vendor/"
|
4
|
+
end
|
5
|
+
|
6
|
+
# This file is copied to spec/ when you run 'rails generate rspec:install'
|
7
|
+
ENV["RAILS_ENV"] ||= 'test'
|
8
|
+
require File.expand_path("../../config/environment", __FILE__)
|
9
|
+
require 'rspec/rails'
|
10
|
+
require 'rspec/autorun'
|
11
|
+
|
12
|
+
# Requires supporting ruby files with custom matchers and macros, etc,
|
13
|
+
# in spec/support/ and its subdirectories.
|
14
|
+
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
|
15
|
+
|
16
|
+
RSpec.configure do |config|
|
17
|
+
# ## Mock Framework
|
18
|
+
#
|
19
|
+
# If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
|
20
|
+
#
|
21
|
+
# config.mock_with :mocha
|
22
|
+
# config.mock_with :flexmock
|
23
|
+
# config.mock_with :rr
|
24
|
+
|
25
|
+
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
|
26
|
+
#config.fixture_path = "#{::Rails.root}/spec/fixtures"
|
27
|
+
|
28
|
+
# If you're not using ActiveRecord, or you'd prefer not to run each of your
|
29
|
+
# examples within a transaction, remove the following line or assign false
|
30
|
+
# instead of true.
|
31
|
+
config.use_transactional_fixtures = true
|
32
|
+
|
33
|
+
# If true, the base class of anonymous controllers will be inferred
|
34
|
+
# automatically. This will be the default behavior in future versions of
|
35
|
+
# rspec-rails.
|
36
|
+
config.infer_base_class_for_anonymous_controllers = false
|
37
|
+
|
38
|
+
# Run specs in random order to surface order dependencies. If you find an
|
39
|
+
# order dependency and want to debug it, you can fix the order by providing
|
40
|
+
# the seed, which is printed after each run.
|
41
|
+
# --seed 1234
|
42
|
+
config.order = "random"
|
43
|
+
|
44
|
+
# Make "FactoryGirl" superfluous
|
45
|
+
config.include FactoryGirl::Syntax::Methods
|
46
|
+
end
|
47
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
if Rails.env == 'production'
|
3
|
+
|
4
|
+
# Use a different logger for distributed setups
|
5
|
+
Rails.logger = ActiveSupport::TaggedLogging.new(ZeromqLogger.new)
|
6
|
+
Rails.logger.level = Logger::INFO
|
7
|
+
Rails.logger.log_tags = []
|
8
|
+
|
9
|
+
# Announce us
|
10
|
+
Rails.logger.info "Initialising"
|
11
|
+
|
12
|
+
# Make sure we log our exit
|
13
|
+
at_exit { Rails.logger.info "Exiting" }
|
14
|
+
|
15
|
+
end
|
data/lib/ocean-rails.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require "ocean/api"
|
2
|
+
require "ocean/api_resource"
|
3
|
+
require "ocean/ocean_resource_model" if defined? ActiveRecord
|
4
|
+
require "ocean/ocean_resource_controller" if defined? ActionController
|
5
|
+
require "ocean/ocean_application_controller"
|
6
|
+
require "ocean/zero_log"
|
7
|
+
require "ocean/zeromq_logger"
|
8
|
+
require "ocean/selective_rack_logger"
|
9
|
+
require "ocean/flooding"
|
10
|
+
|
11
|
+
INVALIDATE_MEMBER_DEFAULT = ["($|/|\\?)"]
|
12
|
+
INVALIDATE_COLLECTION_DEFAULT = ["($|\\?)"]
|
13
|
+
|
14
|
+
module Ocean
|
15
|
+
class Railtie < Rails::Railtie
|
16
|
+
# Silence the /alive action
|
17
|
+
initializer "ocean.swap_logging_middleware" do |app|
|
18
|
+
app.middleware.swap Rails::Rack::Logger, SelectiveRackLogger
|
19
|
+
end
|
20
|
+
# Make sure the generators use the gem's templates first
|
21
|
+
config.app_generators do |g|
|
22
|
+
g.templates.unshift File::expand_path('../templates', __FILE__)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
#
|
29
|
+
# For stubbing authorisation calls in RSpec
|
30
|
+
#
|
31
|
+
def permit_with(status, user_id: 123, creator_uri: "https://api.example.com/v1/api_users/#{user_id}")
|
32
|
+
Api.stub(:permitted?).
|
33
|
+
and_return(double(:status => status,
|
34
|
+
:body => {'authentication' =>
|
35
|
+
{'user_id' => user_id,
|
36
|
+
'_links' => { 'creator' => {'href' => creator_uri,
|
37
|
+
'type' => 'application/json'}}}}))
|
38
|
+
end
|
data/lib/ocean/api.rb
ADDED
@@ -0,0 +1,263 @@
|
|
1
|
+
#
|
2
|
+
# We need to monkey-patch Faraday to pull off PURGE and BAN
|
3
|
+
#
|
4
|
+
require 'faraday'
|
5
|
+
require 'faraday_middleware'
|
6
|
+
|
7
|
+
module Faraday #:nodoc: all
|
8
|
+
class Connection
|
9
|
+
|
10
|
+
METHODS << :purge
|
11
|
+
METHODS << :ban
|
12
|
+
|
13
|
+
# purge/ban(url, params, headers)
|
14
|
+
%w[purge ban].each do |method|
|
15
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
16
|
+
def #{method}(url = nil, params = nil, headers = nil)
|
17
|
+
run_request(:#{method}, url, nil, headers) { |request|
|
18
|
+
request.params.update(params) if params
|
19
|
+
yield request if block_given?
|
20
|
+
}
|
21
|
+
end
|
22
|
+
RUBY
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
#
|
30
|
+
# This class encapsulates all logic for calling other API services.
|
31
|
+
#
|
32
|
+
class Api
|
33
|
+
|
34
|
+
#
|
35
|
+
# When given a symbol or string naming a resource, returns a string
|
36
|
+
# such as +v1+ naming the latest version for the resource.
|
37
|
+
#
|
38
|
+
def self.version_for(resource_name)
|
39
|
+
API_VERSIONS[resource_name.to_s] || API_VERSIONS['_default']
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Given that this service has authenticated successfully with the Auth service,
|
44
|
+
# returns the token returned as part of the authentication response.
|
45
|
+
#
|
46
|
+
def self.token
|
47
|
+
@token
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
#
|
52
|
+
# Makes a HTTP request to +host_url+ using the HTTP method +method+. The +resource_name+
|
53
|
+
# is used to obtain the latest version string of the resource. The arg +path+ is the
|
54
|
+
# local path, +args+ is a hash of query args, and +headers+ a hash of extra HTTP headers.
|
55
|
+
#
|
56
|
+
# Returns the response in its entirety so that the caller can examine its status and body.
|
57
|
+
#
|
58
|
+
def self.call(host_url, http_method, resource_name, path, args={}, headers={})
|
59
|
+
# Set up the connection parameters
|
60
|
+
conn = Faraday.new(host_url) do |c|
|
61
|
+
c.response :json, :content_type => /\bjson$/ # Convert the response body to JSON
|
62
|
+
c.adapter Faraday.default_adapter # Use net-http
|
63
|
+
end
|
64
|
+
api_version = version_for resource_name
|
65
|
+
path = "/#{api_version}#{path}"
|
66
|
+
# Make the call. TODO: retries?
|
67
|
+
response = conn.send(http_method, path, args, headers) do |request|
|
68
|
+
request.headers['Accept'] = 'application/json'
|
69
|
+
request.headers['Content-Type'] = 'application/json'
|
70
|
+
end
|
71
|
+
response
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# Convenience method to make an internal GET request to the Ocean Api. The +resource_name+
|
76
|
+
# is used to obtain the latest version string of the resource. The arg +path+ is the
|
77
|
+
# local path, +args+ is a hash of query args, and +headers+ a hash of extra HTTP headers.
|
78
|
+
#
|
79
|
+
# Returns the response in its entirety so that the caller can examine its status and body.
|
80
|
+
#
|
81
|
+
#
|
82
|
+
def self.get(*args) call(INTERNAL_OCEAN_API_URL, :get, *args); end
|
83
|
+
|
84
|
+
#
|
85
|
+
# Convenience method to make an internal POST request to the Ocean Api. The +resource_name+
|
86
|
+
# is used to obtain the latest version string of the resource. The arg +path+ is the
|
87
|
+
# local path, +args+ is a hash of query args, and +headers+ a hash of extra HTTP headers.
|
88
|
+
#
|
89
|
+
# Returns the response in its entirety so that the caller can examine its status and body.
|
90
|
+
#
|
91
|
+
#
|
92
|
+
def self.post(*args) call(INTERNAL_OCEAN_API_URL, :post, *args); end
|
93
|
+
|
94
|
+
#
|
95
|
+
# Convenience method to make an internal PUT request to the Ocean Api. The +resource_name+
|
96
|
+
# is used to obtain the latest version string of the resource. The arg +path+ is the
|
97
|
+
# local path, +args+ is a hash of query args, and +headers+ a hash of extra HTTP headers.
|
98
|
+
#
|
99
|
+
# Returns the response in its entirety so that the caller can examine its status and body.
|
100
|
+
#
|
101
|
+
#
|
102
|
+
def self.put(*args) call(INTERNAL_OCEAN_API_URL, :put, *args); end
|
103
|
+
|
104
|
+
#
|
105
|
+
# Convenience method to make an internal DELETE request to the Ocean Api. The +resource_name+
|
106
|
+
# is used to obtain the latest version string of the resource. The arg +path+ is the
|
107
|
+
# local path, +args+ is a hash of query args, and +headers+ a hash of extra HTTP headers.
|
108
|
+
#
|
109
|
+
# Returns the response in its entirety so that the caller can examine its status and body.
|
110
|
+
#
|
111
|
+
#
|
112
|
+
def self.delete(*args) call(INTERNAL_OCEAN_API_URL, :delete, *args); end
|
113
|
+
|
114
|
+
|
115
|
+
#
|
116
|
+
# Like Api.call, but makes the requests in parallel. (Parallel calls not implemented yet.)
|
117
|
+
#
|
118
|
+
def self.call_p(url, http_method, path, args={}, headers={})
|
119
|
+
conn = Faraday.new(url) do |c|
|
120
|
+
c.adapter Faraday.default_adapter # Use net-http
|
121
|
+
end
|
122
|
+
conn.send(http_method, path, args, headers)
|
123
|
+
end
|
124
|
+
|
125
|
+
#
|
126
|
+
# Makes an internal PURGE call to all Varnish instances. The call is made in parallel.
|
127
|
+
# Varnish will only accept PURGE requests coming from the local network.
|
128
|
+
#
|
129
|
+
def self.purge(*args)
|
130
|
+
LOAD_BALANCERS.each do |host|
|
131
|
+
call_p("http://#{host}", :purge, *args)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
#
|
136
|
+
# Makes an internal BAN call to all Varnish instances. The call is made in parallel.
|
137
|
+
# Varnish will only accept PURGE requests coming from the local network.
|
138
|
+
#
|
139
|
+
def self.ban(path)
|
140
|
+
LOAD_BALANCERS.each do |host|
|
141
|
+
call_p("http://#{host}", :ban, path)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
#
|
147
|
+
# Authenticates against the Auth service (which must be deployed and running) with
|
148
|
+
# a given +username+ and +password+. If successful, the authentication token is returned. The
|
149
|
+
# token is also assigned to the instance variable @token. If not successful, +nil+ is returned.
|
150
|
+
#
|
151
|
+
def self.authenticate(username=API_USER, password=API_PASSWORD)
|
152
|
+
response = Api.post(:auth, "/authentications", nil,
|
153
|
+
{'X-API-Authenticate' => encode_credentials(username, password)})
|
154
|
+
case response.status
|
155
|
+
when 201
|
156
|
+
@token = response.body['authentication']['token']
|
157
|
+
when 400
|
158
|
+
# Malformed credentials. Don't repeat the request.
|
159
|
+
nil
|
160
|
+
when 403
|
161
|
+
# Does not authenticate. Don't repeat the request.
|
162
|
+
nil
|
163
|
+
when 500
|
164
|
+
# Error. Don't repeat.
|
165
|
+
nil
|
166
|
+
else
|
167
|
+
# Should never end up here.
|
168
|
+
raise "Authentication weirdness"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
#
|
174
|
+
# Encodes a username and password for authentication in the format used for standard HTTP
|
175
|
+
# authentication. The encoding can be reversed and is intended only to lightly mask the
|
176
|
+
# credentials so that they're not immediately apparent when reading logs.
|
177
|
+
#
|
178
|
+
def self.encode_credentials(username, password)
|
179
|
+
::Base64.strict_encode64 "#{username}:#{password}"
|
180
|
+
end
|
181
|
+
|
182
|
+
#
|
183
|
+
# Takes encoded credentials (e.g. by Api.encode_credentials) and returns a two-element array
|
184
|
+
# where the first element is the username and the second is the password. If the encoded
|
185
|
+
# credentials are missing or can't be decoded properly, ["", ""] is returned. This allows
|
186
|
+
# you to write:
|
187
|
+
#
|
188
|
+
# un, pw = Api.decode_credentials(creds)
|
189
|
+
# raise "Please supply your username and password" if un.blank? || pw.blank?
|
190
|
+
#
|
191
|
+
def self.decode_credentials(encoded)
|
192
|
+
return ["", ""] unless encoded
|
193
|
+
username, password = ::Base64.decode64(encoded).split(':', 2)
|
194
|
+
[username || "", password || ""]
|
195
|
+
end
|
196
|
+
|
197
|
+
#
|
198
|
+
# Performs authorisation against the Auth service. The +token+ must be a token received as a
|
199
|
+
# result of a prior authentication operation. The args should be in the form
|
200
|
+
#
|
201
|
+
# query: "service:controller:hyperlink:verb:app:context"
|
202
|
+
#
|
203
|
+
# e.g.
|
204
|
+
#
|
205
|
+
# Api.permitted?(@token, query: "cms:texts:self:GET:*:*")
|
206
|
+
#
|
207
|
+
# Api.authorization_string can be used to produce the query string.
|
208
|
+
#
|
209
|
+
# Returns the HTTP response as-is, allowing the caller to examine the status code and
|
210
|
+
# messages, and also the body.
|
211
|
+
#
|
212
|
+
def self.permitted?(token, args={})
|
213
|
+
raise unless token
|
214
|
+
response = Api.get(:auth, "/authentications/#{token}", args)
|
215
|
+
response
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
#
|
220
|
+
# Returns an authorisation string suitable for use in calls to Api.permitted?.
|
221
|
+
# The +extra_actions+ arg holds the extra actions as defined in the Ocean controller; it must
|
222
|
+
# be included here so that actions can be mapped to the proper hyperlink and verb.
|
223
|
+
# The +controller+ and +action+ args are mandatory. The +app+ and +context+ args are optional and will
|
224
|
+
# default to "*". The last arg, +service+, defaults to the name of the service itself.
|
225
|
+
#
|
226
|
+
def self.authorization_string(extra_actions, controller, action, app="*", context="*", service=APP_NAME)
|
227
|
+
app = '*' if app.blank?
|
228
|
+
context = '*' if context.blank?
|
229
|
+
hyperlink, verb = Api.map_authorization(extra_actions, controller, action)
|
230
|
+
"#{service}:#{controller}:#{hyperlink}:#{verb}:#{app}:#{context}"
|
231
|
+
end
|
232
|
+
|
233
|
+
|
234
|
+
#
|
235
|
+
# These are the default controller actions. The purpose of this constant is to map action
|
236
|
+
# names to hyperlink and HTTP method (for authorisation purposes). Don't be alarmed by the
|
237
|
+
# non-standard GET* - it's purely symbolic and is never used as an actual HTTP method.
|
238
|
+
# We need it to differentiate between a GET of a member and a GET of a collection of members.
|
239
|
+
# The +extra_actions+ keyword in +ocean_resource_controller+ follows the same format.
|
240
|
+
#
|
241
|
+
DEFAULT_ACTIONS = {
|
242
|
+
'show' => ['self', 'GET'],
|
243
|
+
'index' => ['self', 'GET*'],
|
244
|
+
'create' => ['self', 'POST'],
|
245
|
+
'update' => ['self', 'PUT'],
|
246
|
+
'destroy' => ['self', 'DELETE'],
|
247
|
+
'connect' => ['connect', 'PUT'],
|
248
|
+
'disconnect' => ['connect', 'DELETE']
|
249
|
+
}
|
250
|
+
|
251
|
+
#
|
252
|
+
# Returns the hyperlink and HTTP method to use for an +action+ in a certain +controller+.
|
253
|
+
# First, the +DEFAULT_ACTIONS+ are searched, then any extra actions defined for the
|
254
|
+
# controller. Raises an exception if the action can't be found.
|
255
|
+
#
|
256
|
+
def self.map_authorization(extra_actions, controller, action)
|
257
|
+
DEFAULT_ACTIONS[action] ||
|
258
|
+
extra_actions[controller][action] ||
|
259
|
+
raise #"The #{controller} lacks an extra_action declaration for #{action}"
|
260
|
+
end
|
261
|
+
|
262
|
+
|
263
|
+
end
|