ocean-rails 1.14.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|