shamu 0.0.9 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- 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
|