shamu 0.0.9 → 0.0.11
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/.ruby-version +1 -1
- data/Gemfile +5 -3
- data/bin/rake +17 -0
- data/bin/rspec +17 -0
- data/lib/shamu/attributes.rb +3 -1
- data/lib/shamu/events/active_record/migration.rb +6 -6
- data/lib/shamu/json_api/builder_methods/identifier.rb +18 -4
- data/lib/shamu/json_api/context.rb +3 -1
- data/lib/shamu/json_api/error.rb +7 -1
- data/lib/shamu/json_api/presenter.rb +23 -1
- data/lib/shamu/json_api/rails/controller.rb +195 -62
- data/lib/shamu/locale/en.yml +3 -1
- data/lib/shamu/rails/controller.rb +5 -2
- data/lib/shamu/rails/entity.rb +29 -15
- data/lib/shamu/rails/railtie.rb +12 -7
- data/lib/shamu/services/active_record.rb +2 -2
- data/lib/shamu/services/active_record_crud.rb +36 -38
- data/lib/shamu/services/error.rb +11 -1
- data/lib/shamu/services/lazy_transform.rb +9 -4
- data/lib/shamu/services/request_support.rb +5 -2
- data/lib/shamu/services/result.rb +40 -7
- data/lib/shamu/services/service.rb +17 -8
- data/lib/shamu/services/service_call_failed_error.rb +4 -0
- data/lib/shamu/version.rb +2 -2
- data/shamu.gemspec +4 -4
- data/spec/lib/shamu/json_api/builder_methods/identifier_spec.rb +45 -0
- data/spec/lib/shamu/json_api/rails/controller_spec.rb +141 -7
- data/spec/lib/shamu/json_api/rails/responder_spec.rb +9 -9
- data/spec/lib/shamu/rails/controller_spec.rb +4 -4
- data/spec/lib/shamu/rails/entity_spec.rb +34 -16
- data/spec/lib/shamu/rails/features_spec.rb +6 -6
- data/spec/lib/shamu/services/active_record_crud_spec.rb +12 -7
- data/spec/lib/shamu/services/lazy_transform_spec.rb +23 -14
- data/spec/lib/shamu/services/request_support_spec.rb +15 -1
- data/spec/lib/shamu/services/result_spec.rb +37 -1
- data/spec/lib/shamu/services/service_spec.rb +25 -14
- data/spec/spec_helper.rb +1 -1
- metadata +23 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 202b665e0d910a1c418ca7a5ea4bb35d441c16c7
|
4
|
+
data.tar.gz: 8089bef31c11ba75389f8c6b417b2b8f1c34e9dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ab5be942fca9ff72725316a71a84761a28a954c3cb4445073eb94126556569ada8747795c5788811349740e828d83dfabf516fd1b9851c0ac296b993e95c5075
|
7
|
+
data.tar.gz: cce3f9ff5242932c85e3dfc92977ff0a4469439a865213aeb264ee67cde98f3731aeea157651dbbc60fd18c12acff8d938e1eed42b9b2e35750cb00ba7e00496
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.4.0
|
data/Gemfile
CHANGED
@@ -3,9 +3,11 @@ source "https://rubygems.org"
|
|
3
3
|
# Specify your gem"s dependencies in shamu.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
+
gem 'rake'
|
7
|
+
|
6
8
|
group :test do
|
7
|
-
gem "activerecord", "~>
|
8
|
-
gem "actionpack", "~>
|
9
|
+
gem "activerecord", "~> 5.0"
|
10
|
+
gem "actionpack", "~> 5.0"
|
9
11
|
gem "responders", "~> 2.1.2"
|
10
12
|
gem "kaminari", "~> 0.16.3", require: false
|
11
13
|
|
@@ -30,4 +32,4 @@ group :test do
|
|
30
32
|
|
31
33
|
gem "codeclimate-test-reporter", group: :test, require: nil
|
32
34
|
gem "rspec_junit_formatter", "~> 0.2.2", platforms: :mri
|
33
|
-
end
|
35
|
+
end
|
data/bin/rake
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
#
|
4
|
+
# This file was generated by Bundler.
|
5
|
+
#
|
6
|
+
# The application 'rake' is installed as part of a gem, and
|
7
|
+
# this file is here to facilitate running it.
|
8
|
+
#
|
9
|
+
|
10
|
+
require "pathname"
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
12
|
+
Pathname.new(__FILE__).realpath)
|
13
|
+
|
14
|
+
require "rubygems"
|
15
|
+
require "bundler/setup"
|
16
|
+
|
17
|
+
load Gem.bin_path("rake", "rake")
|
data/bin/rspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
#
|
4
|
+
# This file was generated by Bundler.
|
5
|
+
#
|
6
|
+
# The application 'rspec' is installed as part of a gem, and
|
7
|
+
# this file is here to facilitate running it.
|
8
|
+
#
|
9
|
+
|
10
|
+
require "pathname"
|
11
|
+
ENV["BUNDLE_GEMFILE"] = File.expand_path("../../Gemfile",
|
12
|
+
Pathname.new(__FILE__).realpath)
|
13
|
+
|
14
|
+
require "rubygems"
|
15
|
+
require "bundler/setup"
|
16
|
+
|
17
|
+
load Gem.bin_path("rspec-core", "rspec")
|
data/lib/shamu/attributes.rb
CHANGED
@@ -121,6 +121,8 @@ module Shamu
|
|
121
121
|
# Allow protected attributes to be used without explicitly being set.
|
122
122
|
# All 'Attributes' classes are them selves the explicit set of permitted
|
123
123
|
# attributes.
|
124
|
+
elsif attributes.respond_to?( :to_unsafe_h )
|
125
|
+
attributes.to_unsafe_h
|
124
126
|
elsif attributes.respond_to?( :to_hash )
|
125
127
|
attributes.to_hash.symbolize_keys
|
126
128
|
elsif attributes.respond_to?( :to_h )
|
@@ -270,4 +272,4 @@ module Shamu
|
|
270
272
|
end
|
271
273
|
|
272
274
|
end
|
273
|
-
end
|
275
|
+
end
|
@@ -3,14 +3,14 @@ module Shamu
|
|
3
3
|
module ActiveRecord
|
4
4
|
|
5
5
|
# Prepare the database for storing event messages.
|
6
|
-
class Migration < ::ActiveRecord::Migration
|
6
|
+
class Migration < ::ActiveRecord::Migration[5.0]
|
7
7
|
|
8
8
|
self.verbose = false
|
9
9
|
|
10
10
|
# rubocop:disable Metrics/MethodLength
|
11
11
|
|
12
12
|
def up
|
13
|
-
return if
|
13
|
+
return if data_source_exists? Message.table_name
|
14
14
|
|
15
15
|
# TODO: Need to provide a means for using 64-bit primary keys in
|
16
16
|
# databases that support it. Otherwise limited to 4B events.
|
@@ -38,12 +38,12 @@ module Shamu
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def down
|
41
|
-
drop_table Message.table_name if
|
42
|
-
drop_table Channel.table_name if
|
43
|
-
drop_table Runner.table_name if
|
41
|
+
drop_table Message.table_name if data_source_exists? Message.table_name
|
42
|
+
drop_table Channel.table_name if data_source_exists? Channel.table_name
|
43
|
+
drop_table Runner.table_name if data_source_exists? Runner.table_name
|
44
44
|
end
|
45
45
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
end
|
49
|
-
end
|
49
|
+
end
|
@@ -7,9 +7,15 @@ module Shamu
|
|
7
7
|
# @param [String] type of the resource.
|
8
8
|
# @param [Object] id of the resource.
|
9
9
|
# @return [self]
|
10
|
-
def identifier( type, id =
|
11
|
-
output[:type] = @type = type
|
12
|
-
|
10
|
+
def identifier( type, id = :not_set )
|
11
|
+
output[:type] = @type = json_type( type )
|
12
|
+
|
13
|
+
output[:id] =
|
14
|
+
if id == :not_set
|
15
|
+
type.id if type.respond_to?( :id )
|
16
|
+
else
|
17
|
+
id.to_s
|
18
|
+
end
|
13
19
|
|
14
20
|
self
|
15
21
|
end
|
@@ -24,6 +30,14 @@ module Shamu
|
|
24
30
|
|
25
31
|
attr_reader :type
|
26
32
|
|
33
|
+
def json_type( type )
|
34
|
+
type = type.json_type if type.respond_to?( :json_type )
|
35
|
+
type = type.model_name.element if type.respond_to?( :model_name )
|
36
|
+
type = type.name.demodulize.underscore if type.is_a?( Module )
|
37
|
+
|
38
|
+
type
|
39
|
+
end
|
40
|
+
|
27
41
|
def require_identifier!
|
28
42
|
fail IncompleteResourceError unless type
|
29
43
|
end
|
@@ -31,4 +45,4 @@ module Shamu
|
|
31
45
|
end
|
32
46
|
end
|
33
47
|
end
|
34
|
-
end
|
48
|
+
end
|
@@ -114,6 +114,8 @@ module Shamu
|
|
114
114
|
def parse_fields( raw )
|
115
115
|
return {} unless raw
|
116
116
|
|
117
|
+
raw = raw.to_unsafe_hash if raw.respond_to?( :to_unsafe_hash )
|
118
|
+
|
117
119
|
raw.each_with_object( {} ) do |(type, fields), parsed|
|
118
120
|
fields = fields.split( "," ) if fields.is_a?( String )
|
119
121
|
|
@@ -146,4 +148,4 @@ module Shamu
|
|
146
148
|
|
147
149
|
end
|
148
150
|
end
|
149
|
-
end
|
151
|
+
end
|
data/lib/shamu/json_api/error.rb
CHANGED
@@ -45,6 +45,28 @@ module Shamu
|
|
45
45
|
attr_reader :resource
|
46
46
|
attr_reader :builder
|
47
47
|
|
48
|
+
|
49
|
+
# Present all the named attributes of the {#resource}.
|
50
|
+
# @param [Array<Symbol>] names of the resource to present.
|
51
|
+
def resource_attributes( *names )
|
52
|
+
names.map do |name|
|
53
|
+
attribute name, attribute_value( resource.send( name ) )
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Get a JSON API safe version of the value.
|
58
|
+
# @param [Object] value the value to be coerced.
|
59
|
+
# @return [Object]
|
60
|
+
def attribute_value( value )
|
61
|
+
case value
|
62
|
+
when Date, DateTime then
|
63
|
+
value.to_date.to_time.iso8601
|
64
|
+
when Time, ActiveSupport::TimeWithZone then
|
65
|
+
value.iso8601
|
66
|
+
else value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
48
70
|
end
|
49
71
|
end
|
50
|
-
end
|
72
|
+
end
|
@@ -8,68 +8,122 @@ module Shamu
|
|
8
8
|
module Controller
|
9
9
|
extend ActiveSupport::Concern
|
10
10
|
|
11
|
+
# Pattern to identify request params that hold 'ids'
|
12
|
+
ID_PATTERN = /\A(id|.+_id)\z/
|
13
|
+
|
11
14
|
included do
|
12
15
|
before_action do
|
13
16
|
render json: json_error( "The 'include' parameter is not supported" ), status: :bad_request if params[:include] # rubocop:disable Metrics/LineLength
|
14
17
|
request.formats = [ :json_api, :json ]
|
15
18
|
end
|
16
|
-
end
|
17
19
|
|
18
|
-
|
19
|
-
# # If no format has been specfied, default to json_api
|
20
|
-
# request.parameters[:format] ||= "json_api"
|
21
|
-
# super
|
22
|
-
# end
|
23
|
-
|
24
|
-
# Builds a well-formed JSON API response for a single resource.
|
25
|
-
#
|
26
|
-
# @param [Object] resource to present as JSON.
|
27
|
-
# @param [Class] presenter {Presenter} class to use when building the
|
28
|
-
# response for the given resource. If not given, attempts to find a
|
29
|
-
# presenter by calling {Context#find_presenter}.
|
30
|
-
# @param (see #json_context)
|
31
|
-
# @yield (response) write additional top-level links and meta
|
32
|
-
# information.
|
33
|
-
# @yieldparam [JsonApi::Response] response
|
34
|
-
# @return [JsonApi::Response] the presented JSON response.
|
35
|
-
def json_resource( resource, presenter = nil, **context, &block )
|
36
|
-
response = build_json_response( context )
|
37
|
-
response.resource resource, presenter
|
38
|
-
yield response if block_given?
|
39
|
-
response.to_json
|
20
|
+
rescue_from Exception, with: :render_unhandled_exception unless ::Rails.env.test?
|
40
21
|
end
|
41
22
|
|
42
|
-
|
43
|
-
#
|
44
|
-
# @param [Enumerable<Object>] resources to present as a JSON array.
|
45
|
-
# @param [Class] presenter {Presenter} class to use when building the
|
46
|
-
# response for each of the resources. If not given, attempts to find
|
47
|
-
# a presenter by calling {Context#find_presenter}
|
48
|
-
# @param (see #json_context)
|
49
|
-
# @yield (response) write additional top-level links and meta
|
50
|
-
# information.
|
51
|
-
# @yieldparam [JsonApi::Response] response
|
52
|
-
# @return [JsonApi::Response] the presented JSON response.
|
53
|
-
def json_collection( resources, presenter = nil, pagination: :auto, **context, &block )
|
54
|
-
response = build_json_response( context )
|
55
|
-
response.collection resources, presenter
|
56
|
-
json_paginate_resources response, resources, pagination
|
57
|
-
yield response if block_given?
|
58
|
-
response.to_json
|
59
|
-
end
|
23
|
+
private
|
60
24
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
25
|
+
# @!visibility public
|
26
|
+
#
|
27
|
+
# Builds a well-formed JSON API response for a single resource.
|
28
|
+
#
|
29
|
+
# @param [Object] resource to present as JSON.
|
30
|
+
# @param [Class] presenter {Presenter} class to use when building the
|
31
|
+
# response for the given resource. If not given, attempts to find a
|
32
|
+
# presenter by calling {Context#find_presenter}.
|
33
|
+
# @param (see #json_context)
|
34
|
+
# @yield (response) write additional top-level links and meta
|
35
|
+
# information.
|
36
|
+
# @yieldparam [JsonApi::Response] response
|
37
|
+
# @return [JsonApi::Response] the presented JSON response.
|
38
|
+
def json_resource( resource, presenter = nil, **context, &block )
|
39
|
+
response = build_json_response( context )
|
40
|
+
response.resource resource, presenter
|
41
|
+
yield response if block_given?
|
42
|
+
response.as_json
|
43
|
+
end
|
44
|
+
|
45
|
+
# @!visibility public
|
46
|
+
#
|
47
|
+
# Present the `resource` as json and render it adding appropriate
|
48
|
+
# HTTP response codes and headers for standard JSON API actions.
|
49
|
+
#
|
50
|
+
# @param [Symbol,Number] status the HTTP status code.
|
51
|
+
# @param (see #json_resource)
|
52
|
+
def render_resource( resource, presenter: nil, status: nil, location: nil, **context, &block )
|
53
|
+
json = json_resource( resource, presenter, **context, &block )
|
54
|
+
|
55
|
+
# Include canonical url to resource if present
|
56
|
+
if data = json[ "data" ]
|
57
|
+
if links = data[ "links" ]
|
58
|
+
location ||= links[ "self" ] if links[ "self" ]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
render json: json, status: status, location: location
|
63
|
+
end
|
64
|
+
|
65
|
+
# @!visibility public
|
66
|
+
#
|
67
|
+
# Renders a {Shamu::Services::Result} presenting either the
|
68
|
+
# validation errors or the entity.
|
69
|
+
#
|
70
|
+
# @param [Shamu::Services::Result] result of a service call
|
71
|
+
# @param (see #json_resource)
|
72
|
+
def render_result( result, presenter: nil, status: nil, **context, &block )
|
73
|
+
if result.valid?
|
74
|
+
if result.entity
|
75
|
+
status ||= case request.method
|
76
|
+
when 'POST' then :created
|
77
|
+
when 'DELETE' then :no_content
|
78
|
+
else :ok
|
79
|
+
end
|
80
|
+
|
81
|
+
render_resource result.entity, presenter: presenter, status: status, **context, &block
|
82
|
+
else
|
83
|
+
head status || :no_content
|
84
|
+
end
|
85
|
+
else
|
86
|
+
render json: json_validation_errors( result.errors, **context ), status: :unprocessable_entity
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Builds a well-formed JSON API response for a collection of resources.
|
91
|
+
#
|
92
|
+
# @param [Enumerable<Object>] resources to present as a JSON array.
|
93
|
+
# @param [Class] presenter {Presenter} class to use when building the
|
94
|
+
# response for each of the resources. If not given, attempts to find
|
95
|
+
# a presenter by calling {Context#find_presenter}
|
96
|
+
# @param (see #json_context)
|
97
|
+
# @yield (response) write additional top-level links and meta
|
98
|
+
# information.
|
99
|
+
# @yieldparam [JsonApi::Response] response
|
100
|
+
# @return [JsonApi::Response] the presented JSON response.
|
101
|
+
def json_collection( resources, presenter = nil, pagination: :auto, **context, &block )
|
102
|
+
response = build_json_response( context )
|
103
|
+
response.collection resources, presenter
|
104
|
+
json_paginate_resources response, resources, pagination
|
105
|
+
yield response if block_given?
|
106
|
+
response.as_json
|
107
|
+
end
|
108
|
+
|
109
|
+
# Present the resources as json and render it adding appropriate HTTP
|
110
|
+
# response codes and headers.
|
111
|
+
def render_collection( resources, presenter: nil, pagination: :auto, **context, &block )
|
112
|
+
render json: json_collection( resources, presenter, pagination: pagination, **context, &block )
|
113
|
+
end
|
114
|
+
|
115
|
+
# Write all the validation errors from a record to the response.
|
116
|
+
#
|
117
|
+
# @param (see Shamu::JsonApi::Response#validation_errors)
|
118
|
+
# @yield (builder, attr, message)
|
119
|
+
# @yieldparam (see Shamu::JsonApi::Response#validation_errors)
|
120
|
+
# @return [JsonApi::Response] the presented JSON response.
|
121
|
+
def json_validation_errors( errors, **context, &block )
|
122
|
+
response = build_json_response( context )
|
123
|
+
response.validation_errors errors, &block
|
124
|
+
|
125
|
+
response.as_json
|
126
|
+
end
|
73
127
|
|
74
128
|
private
|
75
129
|
|
@@ -93,12 +147,15 @@ module Shamu
|
|
93
147
|
end
|
94
148
|
end
|
95
149
|
|
96
|
-
|
97
|
-
|
98
|
-
|
150
|
+
def json_page_parameter( page_param_name, param, value )
|
151
|
+
params = self.params
|
152
|
+
params = params.to_unsafe_hash if params.respond_to?( :to_unsafe_hash )
|
99
153
|
|
100
|
-
|
101
|
-
|
154
|
+
page_params = params.reverse_merge page_param_name => {}
|
155
|
+
page_params[page_param_name][param] = value
|
156
|
+
|
157
|
+
page_params
|
158
|
+
end
|
102
159
|
|
103
160
|
# @!visibility public
|
104
161
|
#
|
@@ -127,12 +184,29 @@ module Shamu
|
|
127
184
|
|
128
185
|
response.error error do |builder|
|
129
186
|
builder.http_status json_http_status_code_from_error( error )
|
187
|
+
annotate_json_error( error, builder )
|
130
188
|
yield builder if block_given?
|
131
189
|
end
|
132
190
|
|
133
191
|
response.to_json
|
134
192
|
end
|
135
193
|
|
194
|
+
def render_unhandled_exception( exception )
|
195
|
+
render json: json_error( exception ), status: :internal_server_error
|
196
|
+
end
|
197
|
+
|
198
|
+
# @!visibility public
|
199
|
+
#
|
200
|
+
# Annotate an exception that is being rendered to the browser - for
|
201
|
+
# example to add current user or security information if available.
|
202
|
+
def annotate_json_error( error, builder )
|
203
|
+
if ::Rails.env.development?
|
204
|
+
builder.meta :type, error.class.to_s
|
205
|
+
builder.meta :backtrace, error.backtrace
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
|
136
210
|
JSON_CONTEXT_KEYWORDS = [ :fields, :namespaces, :presenters ].freeze
|
137
211
|
|
138
212
|
# @!visibility public
|
@@ -157,8 +231,67 @@ module Shamu
|
|
157
231
|
# parameters sent by the client.
|
158
232
|
def json_context( fields: :not_set, namespaces: :not_set, presenters: :not_set )
|
159
233
|
Shamu::JsonApi::Context.new fields: fields == :not_set ? json_context_fields : fields,
|
160
|
-
|
161
|
-
|
234
|
+
namespaces: namespaces == :not_set ? json_context_namespaces : namespaces,
|
235
|
+
presenters: presenters == :not_set ? json_context_presenters : presenters
|
236
|
+
end
|
237
|
+
|
238
|
+
|
239
|
+
# See (Shamu::Rails::Entity#request_params)
|
240
|
+
def request_params( param_key )
|
241
|
+
if relationships = json_request_payload[ :relationships ]
|
242
|
+
return map_json_resource_payload( relationships[ param_key ][ :data ] ) if relationships.key?( param_key )
|
243
|
+
end
|
244
|
+
|
245
|
+
payload = map_json_resource_payload( json_request_payload )
|
246
|
+
|
247
|
+
request.params.each do |key, value|
|
248
|
+
if ID_PATTERN =~ key
|
249
|
+
payload[ key.to_sym ] ||= value
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
payload
|
254
|
+
end
|
255
|
+
|
256
|
+
def map_json_resource_payload( resource )
|
257
|
+
payload = resource[ :attributes ] ? resource[ :attributes ].dup : {}
|
258
|
+
payload[ :id ] = resource[ :id ] if resource.key?( :id )
|
259
|
+
|
260
|
+
if relationships = resource[ :relationships ]
|
261
|
+
relationships.each do |key, value|
|
262
|
+
attr_key = "#{ key.to_s.singularize }_id"
|
263
|
+
|
264
|
+
if value[ :data ].is_a?( Array )
|
265
|
+
attr_key += 's' if value[ :data ].is_a?( Array )
|
266
|
+
|
267
|
+
payload[ attr_key.to_sym ] = value[ :data ].map { |d| d[ :id ] }
|
268
|
+
payload[ key ] = value[ :data ].map { |d| map_json_resource_payload( d ) }
|
269
|
+
else
|
270
|
+
payload[ attr_key.to_sym ] = value[ :data ][ :id ]
|
271
|
+
payload[ key ] = map_json_resource_payload( value[ :data ] )
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
payload
|
277
|
+
end
|
278
|
+
|
279
|
+
# @!visibility public
|
280
|
+
#
|
281
|
+
# Map a JSON body to a hash.
|
282
|
+
# @return [Hash] the parsed JSON payload.
|
283
|
+
def json_request_payload
|
284
|
+
@json_request_payload ||=
|
285
|
+
begin
|
286
|
+
body = request.body.read || "{}"
|
287
|
+
json = JSON.parse( body, symbolize_names: true )
|
288
|
+
|
289
|
+
unless json.blank?
|
290
|
+
fail NoJsonBodyError unless json[ :data ]
|
291
|
+
end
|
292
|
+
|
293
|
+
json ? json[ :data ] : {}
|
294
|
+
end
|
162
295
|
end
|
163
296
|
|
164
297
|
|
@@ -167,7 +300,7 @@ module Shamu
|
|
167
300
|
end
|
168
301
|
|
169
302
|
def json_context_namespaces
|
170
|
-
name = self.class.name.sub /Controller$/, ""
|
303
|
+
name = self.class.name.sub( /Controller$/, "" )
|
171
304
|
namespaces = [ name.pluralize ]
|
172
305
|
loop do
|
173
306
|
name = name.deconstantize
|
@@ -217,4 +350,4 @@ module Shamu
|
|
217
350
|
end
|
218
351
|
end
|
219
352
|
end
|
220
|
-
end
|
353
|
+
end
|
data/lib/shamu/locale/en.yml
CHANGED
@@ -8,8 +8,9 @@ en:
|
|
8
8
|
|
9
9
|
services:
|
10
10
|
errors:
|
11
|
-
active_record_crud_missing_resource: The resource has not been defined. Add `resource entity_class, model_class` to {
|
11
|
+
active_record_crud_missing_resource: "The resource has not been defined. Add `resource entity_class, model_class` to %{service}."
|
12
12
|
incomplete_setup: The service has not been setup. See included modules documentation for details.
|
13
|
+
service_request_failed: 'The service call failed with: %{errors}.'
|
13
14
|
|
14
15
|
|
15
16
|
security:
|
@@ -30,3 +31,4 @@ en:
|
|
30
31
|
errors:
|
31
32
|
incomplete_resource: "`identifier` was not called to define the type and id of the resource."
|
32
33
|
no_presenter: No presenter available for %{class} objects. Looked in %{namespaces}.
|
34
|
+
no_json_body: "Missing `data` node for JSON API body. Override `json_request_payload` if no body is expected."
|
@@ -16,8 +16,11 @@ module Shamu
|
|
16
16
|
included do
|
17
17
|
include Scorpion::Rails::Controller
|
18
18
|
|
19
|
-
helper_method
|
20
|
-
helper_method
|
19
|
+
# ActionController::API does not have #helper_method
|
20
|
+
if respond_to?( :helper_method )
|
21
|
+
helper_method :permit?
|
22
|
+
helper_method :current_user
|
23
|
+
end
|
21
24
|
end
|
22
25
|
|
23
26
|
private
|
data/lib/shamu/rails/entity.rb
CHANGED
@@ -9,7 +9,7 @@ module Shamu
|
|
9
9
|
private
|
10
10
|
|
11
11
|
def fetch_entity( service, param )
|
12
|
-
service.find( params[ param ] )
|
12
|
+
service.find( params[ param ] ) if params.key?( param )
|
13
13
|
end
|
14
14
|
|
15
15
|
def fetch_entities( service, param )
|
@@ -22,16 +22,24 @@ module Shamu
|
|
22
22
|
return unless request = service.request_for( action, entity )
|
23
23
|
|
24
24
|
param_key ||= entity.model_name.param_key
|
25
|
+
request.assign_attributes( request_params( param_key ) )
|
25
26
|
|
27
|
+
service.authorize!( action, entity, request ) if service.respond_to?( :authorize! )
|
28
|
+
request
|
29
|
+
end
|
30
|
+
|
31
|
+
# @!visibility public
|
32
|
+
#
|
33
|
+
# Get the raw request hash params for the given parameter key.
|
34
|
+
# @param [Symbol] param_key key of the entity params to fetch.
|
35
|
+
# @return [Hash] the params
|
36
|
+
def request_params( param_key )
|
26
37
|
strong_param = :"#{ param_key }_params"
|
27
38
|
if respond_to?( strong_param, true )
|
28
|
-
|
39
|
+
send( strong_param )
|
29
40
|
else
|
30
|
-
|
41
|
+
params[ param_key ]
|
31
42
|
end
|
32
|
-
|
33
|
-
service.authorize!( action, entity, request ) if service.respond_to?( :authorize! )
|
34
|
-
request
|
35
43
|
end
|
36
44
|
|
37
45
|
def load_entity( method:, list_method:, action: nil, only: nil, except: nil )
|
@@ -44,11 +52,16 @@ module Shamu
|
|
44
52
|
def matching_entity_action?( action, only:, except: )
|
45
53
|
return if only.present? && !only.include?( action )
|
46
54
|
return if except.present? && except.include?( action )
|
47
|
-
|
55
|
+
|
56
|
+
!create_action?( action )
|
57
|
+
end
|
58
|
+
|
59
|
+
def list_action?( action = params[ :action ] )
|
60
|
+
action.to_sym == :index
|
48
61
|
end
|
49
62
|
|
50
|
-
def
|
51
|
-
|
63
|
+
def create_action?( action = params[ :action ] )
|
64
|
+
[ :new, :create ].include?( action.to_sym )
|
52
65
|
end
|
53
66
|
|
54
67
|
class_methods do
|
@@ -101,7 +114,7 @@ module Shamu
|
|
101
114
|
# not being modified in an :update request.
|
102
115
|
def entity( entity_class, through: nil, as: nil, list: nil, only: nil, except: nil, param: :id, list_param: nil, action: nil, param_key: nil ) # rubocop:disable Metrics/LineLength
|
103
116
|
as ||= entity_as_name( entity_class )
|
104
|
-
through ||= :"#{ as }_service"
|
117
|
+
through ||= :"#{ as.to_s.pluralize }_service"
|
105
118
|
list ||= as.to_s.pluralize.to_sym
|
106
119
|
|
107
120
|
define_entity_method( as, through, param )
|
@@ -132,7 +145,7 @@ module Shamu
|
|
132
145
|
@#{ as } = fetch_entity( #{ through }, :#{ param } ) # @entity = fetch_entity( entity_service, :id )
|
133
146
|
end # end
|
134
147
|
|
135
|
-
helper_method :#{ as }
|
148
|
+
helper_method :#{ as } if respond_to?( :helper_method )
|
136
149
|
RUBY
|
137
150
|
end
|
138
151
|
|
@@ -145,7 +158,7 @@ module Shamu
|
|
145
158
|
@#{ as } = fetch_entities( #{ through }, #{ param ? ":#{ param }" : 'nil' } ) # @entities = fetch_entities( entity_service, nil )
|
146
159
|
end # end
|
147
160
|
|
148
|
-
helper_method :#{ as }
|
161
|
+
helper_method :#{ as } if respond_to?( :helper_method )
|
149
162
|
RUBY
|
150
163
|
end
|
151
164
|
|
@@ -155,14 +168,15 @@ module Shamu
|
|
155
168
|
|
156
169
|
def #{ as }_request # def entity_request
|
157
170
|
return @#{ as }_request if defined? @#{ as }_request # return @entity_request if defined? @entity_request
|
158
|
-
|
171
|
+
_entity = #{ as } unless create_action? # _entity = entity unless create_action?
|
172
|
+
@#{ as }_request = fetch_entity_request( #{ through }, _entity, :#{ as } ) # @entity_request = fetch_entity_request( entity_service, _entity, :entity )
|
159
173
|
end # end
|
160
174
|
|
161
|
-
helper_method :#{ as }_request
|
175
|
+
helper_method :#{ as }_request if respond_to?( :helper_method )
|
162
176
|
RUBY
|
163
177
|
end
|
164
178
|
|
165
179
|
end
|
166
180
|
end
|
167
181
|
end
|
168
|
-
end
|
182
|
+
end
|