jpie 0.4.4 → 1.0.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 +4 -4
- data/.gitignore +21 -0
- data/.rspec +3 -0
- data/.rubocop.yml +35 -110
- data/.travis.yml +7 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +312 -0
- data/README.md +2072 -140
- data/Rakefile +3 -14
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/jpie.gemspec +18 -35
- data/kiln/app/resources/user_message_resource.rb +2 -0
- data/lib/jpie.rb +3 -28
- data/lib/json_api/active_storage/deserialization.rb +106 -0
- data/lib/json_api/active_storage/detection.rb +74 -0
- data/lib/json_api/active_storage/serialization.rb +32 -0
- data/lib/json_api/configuration.rb +58 -0
- data/lib/json_api/controllers/base_controller.rb +26 -0
- data/lib/json_api/controllers/concerns/controller_helpers.rb +223 -0
- data/lib/json_api/controllers/concerns/resource_actions.rb +657 -0
- data/lib/json_api/controllers/relationships_controller.rb +504 -0
- data/lib/json_api/controllers/resources_controller.rb +6 -0
- data/lib/json_api/errors/parameter_not_allowed.rb +19 -0
- data/lib/json_api/railtie.rb +75 -0
- data/lib/json_api/resources/active_storage_blob_resource.rb +11 -0
- data/lib/json_api/resources/resource.rb +238 -0
- data/lib/json_api/resources/resource_loader.rb +35 -0
- data/lib/json_api/routing.rb +72 -0
- data/lib/json_api/serialization/deserializer.rb +362 -0
- data/lib/json_api/serialization/serializer.rb +320 -0
- data/lib/json_api/support/active_storage_support.rb +85 -0
- data/lib/json_api/support/collection_query.rb +406 -0
- data/lib/json_api/support/instrumentation.rb +42 -0
- data/lib/json_api/support/param_helpers.rb +51 -0
- data/lib/json_api/support/relationship_guard.rb +16 -0
- data/lib/json_api/support/relationship_helpers.rb +74 -0
- data/lib/json_api/support/resource_identifier.rb +87 -0
- data/lib/json_api/support/responders.rb +100 -0
- data/lib/json_api/support/response_helpers.rb +10 -0
- data/lib/json_api/support/sort_parsing.rb +21 -0
- data/lib/json_api/support/type_conversion.rb +21 -0
- data/lib/json_api/testing/test_helper.rb +76 -0
- data/lib/json_api/testing.rb +3 -0
- data/lib/{jpie → json_api}/version.rb +2 -2
- data/lib/json_api.rb +50 -0
- data/lib/rubocop/cop/custom/hash_value_omission.rb +53 -0
- metadata +50 -167
- data/.cursor/rules/dependencies.mdc +0 -19
- data/.cursor/rules/examples.mdc +0 -16
- data/.cursor/rules/git.mdc +0 -14
- data/.cursor/rules/project_structure.mdc +0 -30
- data/.cursor/rules/security.mdc +0 -14
- data/.cursor/rules/style.mdc +0 -15
- data/.cursor/rules/testing.mdc +0 -16
- data/.overcommit.yml +0 -35
- data/CHANGELOG.md +0 -164
- data/LICENSE.txt +0 -21
- data/examples/basic_example.md +0 -146
- data/examples/including_related_resources.md +0 -491
- data/examples/pagination.md +0 -303
- data/examples/relationships.md +0 -114
- data/examples/resource_attribute_configuration.md +0 -147
- data/examples/resource_meta_configuration.md +0 -244
- data/examples/single_table_inheritance.md +0 -160
- data/lib/jpie/configuration.rb +0 -12
- data/lib/jpie/controller/crud_actions.rb +0 -141
- data/lib/jpie/controller/error_handling/handler_setup.rb +0 -124
- data/lib/jpie/controller/error_handling/handlers.rb +0 -109
- data/lib/jpie/controller/error_handling.rb +0 -23
- data/lib/jpie/controller/json_api_validation.rb +0 -193
- data/lib/jpie/controller/parameter_parsing.rb +0 -78
- data/lib/jpie/controller/related_actions.rb +0 -45
- data/lib/jpie/controller/relationship_actions.rb +0 -291
- data/lib/jpie/controller/relationship_validation.rb +0 -117
- data/lib/jpie/controller/rendering.rb +0 -154
- data/lib/jpie/controller.rb +0 -45
- data/lib/jpie/deserializer.rb +0 -110
- data/lib/jpie/errors.rb +0 -117
- data/lib/jpie/generators/resource_generator.rb +0 -116
- data/lib/jpie/generators/templates/resource.rb.erb +0 -31
- data/lib/jpie/railtie.rb +0 -42
- data/lib/jpie/resource/attributable.rb +0 -112
- data/lib/jpie/resource/inferrable.rb +0 -43
- data/lib/jpie/resource/sortable.rb +0 -93
- data/lib/jpie/resource.rb +0 -147
- data/lib/jpie/routing.rb +0 -59
- data/lib/jpie/rspec.rb +0 -71
- data/lib/jpie/serializer.rb +0 -205
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module JPie
|
|
4
|
-
module Controller
|
|
5
|
-
module ErrorHandling
|
|
6
|
-
module Handlers
|
|
7
|
-
extend ActiveSupport::Concern
|
|
8
|
-
|
|
9
|
-
private
|
|
10
|
-
|
|
11
|
-
# Handle JPie-specific errors
|
|
12
|
-
def handle_jpie_error(error)
|
|
13
|
-
render_json_api_error(
|
|
14
|
-
status: error.status,
|
|
15
|
-
title: error.title,
|
|
16
|
-
detail: error.detail
|
|
17
|
-
)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# Handle ActiveRecord::RecordNotFound
|
|
21
|
-
def handle_record_not_found(error)
|
|
22
|
-
render_json_api_error(
|
|
23
|
-
status: 404,
|
|
24
|
-
title: 'Not Found',
|
|
25
|
-
detail: error.message
|
|
26
|
-
)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Handle ActiveRecord::RecordInvalid
|
|
30
|
-
def handle_record_invalid(error)
|
|
31
|
-
errors = error.record.errors.full_messages.map do |message|
|
|
32
|
-
{
|
|
33
|
-
status: '422',
|
|
34
|
-
title: 'Validation Error',
|
|
35
|
-
detail: message
|
|
36
|
-
}
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
render json: { errors: errors }, status: :unprocessable_content
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# Render a single JSON:API error
|
|
43
|
-
def render_json_api_error(status:, title:, detail:)
|
|
44
|
-
render json: {
|
|
45
|
-
errors: [{
|
|
46
|
-
status: status.to_s,
|
|
47
|
-
title: title,
|
|
48
|
-
detail: detail
|
|
49
|
-
}]
|
|
50
|
-
}, status: status
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
# Handle JSON:API compliance errors
|
|
54
|
-
def handle_invalid_json_api_request(error)
|
|
55
|
-
render_json_api_error(
|
|
56
|
-
status: error.status,
|
|
57
|
-
title: error.title || 'Invalid JSON:API Request',
|
|
58
|
-
detail: error.detail
|
|
59
|
-
)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def handle_unsupported_include(error)
|
|
63
|
-
render_json_api_error(
|
|
64
|
-
status: error.status,
|
|
65
|
-
title: error.title || 'Unsupported Include',
|
|
66
|
-
detail: error.detail
|
|
67
|
-
)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def handle_unsupported_sort_field(error)
|
|
71
|
-
render_json_api_error(
|
|
72
|
-
status: error.status,
|
|
73
|
-
title: error.title || 'Unsupported Sort Field',
|
|
74
|
-
detail: error.detail
|
|
75
|
-
)
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def handle_invalid_sort_parameter(error)
|
|
79
|
-
render_json_api_error(
|
|
80
|
-
status: error.status,
|
|
81
|
-
title: error.title || 'Invalid Sort Parameter',
|
|
82
|
-
detail: error.detail
|
|
83
|
-
)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def handle_invalid_include_parameter(error)
|
|
87
|
-
render_json_api_error(
|
|
88
|
-
status: error.status,
|
|
89
|
-
title: error.title || 'Invalid Include Parameter',
|
|
90
|
-
detail: error.detail
|
|
91
|
-
)
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# Backward compatibility aliases
|
|
95
|
-
alias jpie_handle_error handle_jpie_error
|
|
96
|
-
alias jpie_handle_not_found handle_record_not_found
|
|
97
|
-
alias jpie_handle_invalid handle_record_invalid
|
|
98
|
-
|
|
99
|
-
# Legacy method name aliases
|
|
100
|
-
alias render_jpie_error handle_jpie_error
|
|
101
|
-
alias render_jpie_not_found_error handle_record_not_found
|
|
102
|
-
alias render_jpie_validation_error handle_record_invalid
|
|
103
|
-
alias render_jsonapi_error handle_jpie_error
|
|
104
|
-
alias render_not_found_error handle_record_not_found
|
|
105
|
-
alias render_validation_error handle_record_invalid
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
end
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'error_handling/handler_setup'
|
|
4
|
-
require_relative 'error_handling/handlers'
|
|
5
|
-
|
|
6
|
-
module JPie
|
|
7
|
-
module Controller
|
|
8
|
-
module ErrorHandling
|
|
9
|
-
extend ActiveSupport::Concern
|
|
10
|
-
|
|
11
|
-
include HandlerSetup
|
|
12
|
-
include Handlers
|
|
13
|
-
|
|
14
|
-
included do
|
|
15
|
-
# Use class_attribute to allow easy overriding
|
|
16
|
-
class_attribute :jpie_error_handlers_enabled, default: true
|
|
17
|
-
|
|
18
|
-
# Set up default handlers unless explicitly disabled
|
|
19
|
-
setup_jpie_error_handlers if jpie_error_handlers_enabled
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module JPie
|
|
4
|
-
module Controller
|
|
5
|
-
module JsonApiValidation
|
|
6
|
-
extend ActiveSupport::Concern
|
|
7
|
-
|
|
8
|
-
private
|
|
9
|
-
|
|
10
|
-
# Validate that the request is JSON:API compliant
|
|
11
|
-
def validate_json_api_request
|
|
12
|
-
validate_content_type if request.post? || request.patch? || request.put?
|
|
13
|
-
validate_json_api_structure if request.post? || request.patch? || request.put?
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# Validate Content-Type header for write operations
|
|
17
|
-
def validate_content_type
|
|
18
|
-
# Only validate content type for write operations
|
|
19
|
-
return unless request.post? || request.patch? || request.put?
|
|
20
|
-
|
|
21
|
-
content_type = request.content_type
|
|
22
|
-
return if content_type&.include?('application/vnd.api+json')
|
|
23
|
-
|
|
24
|
-
raise JPie::Errors::InvalidJsonApiRequestError.new(
|
|
25
|
-
detail: 'Content-Type must be application/vnd.api+json for JSON:API requests'
|
|
26
|
-
)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Validate basic JSON:API request structure
|
|
30
|
-
def validate_json_api_structure
|
|
31
|
-
request_body = read_request_body
|
|
32
|
-
return if request_body.blank?
|
|
33
|
-
|
|
34
|
-
parsed_body = parse_json_body(request_body)
|
|
35
|
-
validate_top_level_structure(parsed_body)
|
|
36
|
-
validate_data_structure(parsed_body['data'])
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def read_request_body
|
|
40
|
-
request_body = request.body.read
|
|
41
|
-
request.body.rewind # Reset for later reading
|
|
42
|
-
request_body
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def parse_json_body(request_body)
|
|
46
|
-
JSON.parse(request_body)
|
|
47
|
-
rescue JSON::ParserError => e
|
|
48
|
-
raise JPie::Errors::InvalidJsonApiRequestError.new(
|
|
49
|
-
detail: "Invalid JSON: #{e.message}"
|
|
50
|
-
)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def validate_top_level_structure(parsed_body)
|
|
54
|
-
return if parsed_body.is_a?(Hash) && parsed_body.key?('data')
|
|
55
|
-
|
|
56
|
-
raise JPie::Errors::InvalidJsonApiRequestError.new(
|
|
57
|
-
detail: 'JSON:API request must have a top-level "data" member'
|
|
58
|
-
)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# Validate the structure of the data member
|
|
62
|
-
def validate_data_structure(data)
|
|
63
|
-
if data.is_a?(Array)
|
|
64
|
-
data.each { |item| validate_resource_object(item) }
|
|
65
|
-
elsif data.is_a?(Hash)
|
|
66
|
-
validate_resource_object(data)
|
|
67
|
-
elsif !data.nil?
|
|
68
|
-
raise JPie::Errors::InvalidJsonApiRequestError.new(
|
|
69
|
-
detail: 'Data member must be an object, array, or null'
|
|
70
|
-
)
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
# Validate individual resource object structure
|
|
75
|
-
def validate_resource_object(resource)
|
|
76
|
-
unless resource.is_a?(Hash)
|
|
77
|
-
raise JPie::Errors::InvalidJsonApiRequestError.new(
|
|
78
|
-
detail: 'Resource objects must be JSON objects'
|
|
79
|
-
)
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
unless resource.key?('type')
|
|
83
|
-
raise JPie::Errors::InvalidJsonApiRequestError.new(
|
|
84
|
-
detail: 'Resource objects must have a "type" member'
|
|
85
|
-
)
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
# ID is required for updates but not for creates
|
|
89
|
-
return unless request.patch? || request.put?
|
|
90
|
-
return if resource.key?('id')
|
|
91
|
-
|
|
92
|
-
raise JPie::Errors::InvalidJsonApiRequestError.new(
|
|
93
|
-
detail: 'Resource objects must have an "id" member for updates'
|
|
94
|
-
)
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Validate include parameters against supported includes
|
|
98
|
-
def validate_include_params
|
|
99
|
-
return if params[:include].blank?
|
|
100
|
-
|
|
101
|
-
include_paths = params[:include].to_s.split(',').map(&:strip)
|
|
102
|
-
supported_includes = resource_class.supported_includes
|
|
103
|
-
|
|
104
|
-
include_paths.each do |include_path|
|
|
105
|
-
next if include_path.blank?
|
|
106
|
-
|
|
107
|
-
validate_include_path(include_path, supported_includes)
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
# Validate a single include path
|
|
112
|
-
def validate_include_path(include_path, supported_includes)
|
|
113
|
-
path_parts = include_path.split('.')
|
|
114
|
-
current_level = supported_includes
|
|
115
|
-
|
|
116
|
-
path_parts.each_with_index do |part, index|
|
|
117
|
-
validate_include_part(part, current_level, path_parts, index)
|
|
118
|
-
current_level = move_to_next_include_level(part, current_level)
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def validate_include_part(part, current_level, path_parts, index)
|
|
123
|
-
return if include_part_supported?(part, current_level)
|
|
124
|
-
|
|
125
|
-
current_path = path_parts[0..index].join('.')
|
|
126
|
-
available_at_level = extract_available_includes(current_level)
|
|
127
|
-
|
|
128
|
-
raise JPie::Errors::UnsupportedIncludeError.new(
|
|
129
|
-
include_path: current_path,
|
|
130
|
-
supported_includes: available_at_level.map(&:to_s)
|
|
131
|
-
)
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def include_part_supported?(part, current_level)
|
|
135
|
-
current_level.include?(part.to_sym) || current_level.include?(part)
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
def extract_available_includes(current_level)
|
|
139
|
-
current_level.is_a?(Hash) ? current_level.keys : current_level
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def move_to_next_include_level(part, current_level)
|
|
143
|
-
return current_level unless current_level.is_a?(Hash)
|
|
144
|
-
|
|
145
|
-
current_level[part.to_sym] if current_level[part.to_sym].is_a?(Hash)
|
|
146
|
-
current_level[part] if current_level[part].is_a?(Hash)
|
|
147
|
-
current_level
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
# Validate sort parameters against supported fields
|
|
151
|
-
def validate_sort_params
|
|
152
|
-
return if params[:sort].blank?
|
|
153
|
-
|
|
154
|
-
sort_fields = params[:sort].to_s.split(',').map(&:strip)
|
|
155
|
-
supported_fields = resource_class.supported_sort_fields
|
|
156
|
-
|
|
157
|
-
sort_fields.each do |sort_field|
|
|
158
|
-
next if sort_field.blank?
|
|
159
|
-
|
|
160
|
-
validate_sort_field(sort_field, supported_fields)
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
# Validate a single sort field
|
|
165
|
-
def validate_sort_field(sort_field, supported_fields)
|
|
166
|
-
# Remove leading minus for descending sort
|
|
167
|
-
field_name = sort_field.start_with?('-') ? sort_field[1..] : sort_field
|
|
168
|
-
|
|
169
|
-
# Validate field name format
|
|
170
|
-
unless field_name.match?(/\A[a-zA-Z][a-zA-Z0-9_]*\z/)
|
|
171
|
-
raise JPie::Errors::InvalidSortParameterError.new(
|
|
172
|
-
detail: "Invalid sort field format: '#{sort_field}'. " \
|
|
173
|
-
'Field names must start with a letter and contain only letters, numbers, and underscores'
|
|
174
|
-
)
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
return if supported_fields.include?(field_name.to_sym) || supported_fields.include?(field_name)
|
|
178
|
-
|
|
179
|
-
raise JPie::Errors::UnsupportedSortFieldError.new(
|
|
180
|
-
sort_field: field_name,
|
|
181
|
-
supported_fields: supported_fields.map(&:to_s)
|
|
182
|
-
)
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
# Validate all JSON:API request aspects
|
|
186
|
-
def validate_json_api_compliance
|
|
187
|
-
validate_json_api_request
|
|
188
|
-
validate_include_params
|
|
189
|
-
validate_sort_params
|
|
190
|
-
end
|
|
191
|
-
end
|
|
192
|
-
end
|
|
193
|
-
end
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module JPie
|
|
4
|
-
module Controller
|
|
5
|
-
module ParameterParsing
|
|
6
|
-
def parse_include_params
|
|
7
|
-
params[:include]&.split(',')&.map(&:strip) || []
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def parse_sort_params
|
|
11
|
-
params[:sort]&.split(',')&.map(&:strip) || []
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def parse_pagination_params
|
|
15
|
-
page_params = params[:page] || {}
|
|
16
|
-
per_page_param = params[:per_page]
|
|
17
|
-
|
|
18
|
-
{
|
|
19
|
-
page: extract_page_number(page_params, per_page_param),
|
|
20
|
-
per_page: extract_per_page_size(page_params, per_page_param)
|
|
21
|
-
}
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def deserialize_params
|
|
25
|
-
deserializer.deserialize(request.body.read, context)
|
|
26
|
-
rescue JSON::ParserError => e
|
|
27
|
-
raise JPie::Errors::BadRequestError.new(detail: "Invalid JSON: #{e.message}")
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def context
|
|
31
|
-
@context ||= build_context
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
private
|
|
35
|
-
|
|
36
|
-
def build_context
|
|
37
|
-
{
|
|
38
|
-
current_user: try(:current_user),
|
|
39
|
-
controller: self,
|
|
40
|
-
action: action_name
|
|
41
|
-
}
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def extract_page_number(page_params, per_page_param)
|
|
45
|
-
page_number = determine_page_number(page_params)
|
|
46
|
-
page_number = '1' if page_number.nil? && per_page_param.present?
|
|
47
|
-
return 1 if page_number.blank?
|
|
48
|
-
|
|
49
|
-
parsed_page = page_number.to_i
|
|
50
|
-
parsed_page.positive? ? parsed_page : 1
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def extract_per_page_size(page_params, per_page_param)
|
|
54
|
-
per_page_size = determine_per_page_size(page_params, per_page_param)
|
|
55
|
-
return nil if per_page_size.blank?
|
|
56
|
-
|
|
57
|
-
parsed_size = per_page_size.to_i
|
|
58
|
-
parsed_size.positive? ? parsed_size : nil
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def determine_page_number(page_params)
|
|
62
|
-
if page_params.is_a?(String) || page_params.is_a?(Integer)
|
|
63
|
-
page_params
|
|
64
|
-
else
|
|
65
|
-
page_params[:number] || page_params['number']
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def determine_per_page_size(page_params, per_page_param)
|
|
70
|
-
if page_params.is_a?(String) || page_params.is_a?(Integer)
|
|
71
|
-
per_page_param
|
|
72
|
-
else
|
|
73
|
-
page_params[:size] || page_params['size'] || per_page_param
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
end
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module JPie
|
|
4
|
-
module Controller
|
|
5
|
-
module RelatedActions
|
|
6
|
-
extend ActiveSupport::Concern
|
|
7
|
-
|
|
8
|
-
# GET /resources/:id/:relationship_name
|
|
9
|
-
# Returns the related resources themselves (not just linkage)
|
|
10
|
-
def related_show
|
|
11
|
-
validate_relationship_exists
|
|
12
|
-
validate_include_params
|
|
13
|
-
resource = find_resource
|
|
14
|
-
related_resources = get_related_resources(resource)
|
|
15
|
-
render_jsonapi(related_resources)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
private
|
|
19
|
-
|
|
20
|
-
def validate_relationship_exists
|
|
21
|
-
relationship_name = params[:relationship_name]
|
|
22
|
-
return unless relationship_name # Skip validation if no relationship_name param
|
|
23
|
-
|
|
24
|
-
return if resource_class._relationships.key?(relationship_name.to_sym)
|
|
25
|
-
|
|
26
|
-
raise JPie::Errors::NotFoundError.new(
|
|
27
|
-
detail: "Relationship '#{relationship_name}' does not exist for #{resource_class.name}"
|
|
28
|
-
)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def find_resource
|
|
32
|
-
resource_class.scope(context).find(params[:id])
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def relationship_name
|
|
36
|
-
@relationship_name ||= params[:relationship_name].to_sym
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def get_related_resources(resource)
|
|
40
|
-
relationship_method = relationship_name
|
|
41
|
-
resource.send(relationship_method)
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|