alpha_api 0.1.0 → 0.1.1
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/Gemfile +0 -7
- data/Gemfile.lock +55 -5
- data/alpha_api.gemspec +6 -1
- data/lib/alpha_api/application.rb +2 -2
- data/lib/alpha_api/base_controller.rb +186 -0
- data/lib/alpha_api/concerns/actionable.rb +19 -17
- data/lib/alpha_api/exceptions.rb +16 -0
- data/lib/alpha_api/logger.rb +13 -0
- data/lib/alpha_api/version.rb +1 -1
- data/lib/alpha_api.rb +14 -0
- data/lib/generators/install/install_generator.rb +48 -3
- data/lib/generators/install/templates/add_denylisted_token.rb +11 -0
- data/lib/generators/install/templates/create_devise_users.rb +45 -0
- data/lib/generators/install/templates/denylisted_token.rb.erb +4 -0
- data/lib/generators/resource/templates/controller.rb.erb +1 -1
- metadata +37 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 21aff10319f03b60f38c86b3f9655710d6d2a4e19914edd5e1ccd1c547f8fce7
|
|
4
|
+
data.tar.gz: 2856e2ef1792a1820adcb1c900004bad978b4d655f1a4383f4636973a5162f1a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6039fa1d787a8411148fe07a79bfcba4cd1b18423865a6dfffc60ae7398a04c945fc34112cba9d25e8c9a06b3032865582ff1857a2fd10031a64cd88f7a93f5f
|
|
7
|
+
data.tar.gz: fb6560099437d58ae4d247aba933dbc4c8e963d659c59dc15c8de7408d283c3a86aa3ee0267a0c2bd0bbf803d682315b431232a04935673db6aead6994e9e4ae
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
alpha_api (0.1.
|
|
5
|
-
activesupport
|
|
4
|
+
alpha_api (0.1.1)
|
|
6
5
|
api-pagination
|
|
6
|
+
cancancan
|
|
7
|
+
devise
|
|
8
|
+
devise-jwt
|
|
7
9
|
fast_jsonapi
|
|
8
10
|
kaminari
|
|
9
11
|
|
|
10
12
|
GEM
|
|
11
13
|
remote: https://rubygems.org/
|
|
12
14
|
specs:
|
|
15
|
+
actionpack (6.1.4.1)
|
|
16
|
+
actionview (= 6.1.4.1)
|
|
17
|
+
activesupport (= 6.1.4.1)
|
|
18
|
+
rack (~> 2.0, >= 2.0.9)
|
|
19
|
+
rack-test (>= 0.6.3)
|
|
20
|
+
rails-dom-testing (~> 2.0)
|
|
21
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
|
13
22
|
actionview (6.1.4.1)
|
|
14
23
|
activesupport (= 6.1.4.1)
|
|
15
24
|
builder (~> 3.1)
|
|
@@ -29,14 +38,36 @@ GEM
|
|
|
29
38
|
zeitwerk (~> 2.3)
|
|
30
39
|
api-pagination (4.8.2)
|
|
31
40
|
ast (2.4.2)
|
|
41
|
+
bcrypt (3.1.16)
|
|
32
42
|
builder (3.2.4)
|
|
43
|
+
cancancan (3.3.0)
|
|
33
44
|
concurrent-ruby (1.1.9)
|
|
34
45
|
crass (1.0.6)
|
|
46
|
+
devise (4.8.0)
|
|
47
|
+
bcrypt (~> 3.0)
|
|
48
|
+
orm_adapter (~> 0.1)
|
|
49
|
+
railties (>= 4.1.0)
|
|
50
|
+
responders
|
|
51
|
+
warden (~> 1.2.3)
|
|
52
|
+
devise-jwt (0.8.1)
|
|
53
|
+
devise (~> 4.0)
|
|
54
|
+
warden-jwt_auth (~> 0.5)
|
|
55
|
+
dry-auto_inject (0.7.0)
|
|
56
|
+
dry-container (>= 0.3.4)
|
|
57
|
+
dry-configurable (0.12.1)
|
|
58
|
+
concurrent-ruby (~> 1.0)
|
|
59
|
+
dry-core (~> 0.5, >= 0.5.0)
|
|
60
|
+
dry-container (0.7.2)
|
|
61
|
+
concurrent-ruby (~> 1.0)
|
|
62
|
+
dry-configurable (~> 0.1, >= 0.1.3)
|
|
63
|
+
dry-core (0.6.0)
|
|
64
|
+
concurrent-ruby (~> 1.0)
|
|
35
65
|
erubi (1.10.0)
|
|
36
66
|
fast_jsonapi (1.5)
|
|
37
67
|
activesupport (>= 4.2)
|
|
38
68
|
i18n (1.8.10)
|
|
39
69
|
concurrent-ruby (~> 1.0)
|
|
70
|
+
jwt (2.2.3)
|
|
40
71
|
kaminari (1.2.1)
|
|
41
72
|
activesupport (>= 4.1.0)
|
|
42
73
|
kaminari-actionview (= 1.2.1)
|
|
@@ -52,21 +83,35 @@ GEM
|
|
|
52
83
|
loofah (2.12.0)
|
|
53
84
|
crass (~> 1.0.2)
|
|
54
85
|
nokogiri (>= 1.5.9)
|
|
86
|
+
method_source (1.0.0)
|
|
55
87
|
minitest (5.14.4)
|
|
56
88
|
nokogiri (1.12.4-x86_64-darwin)
|
|
57
89
|
racc (~> 1.4)
|
|
90
|
+
orm_adapter (0.5.0)
|
|
58
91
|
parallel (1.20.1)
|
|
59
92
|
parser (3.0.2.0)
|
|
60
93
|
ast (~> 2.4.1)
|
|
61
94
|
racc (1.5.2)
|
|
95
|
+
rack (2.2.3)
|
|
96
|
+
rack-test (1.1.0)
|
|
97
|
+
rack (>= 1.0, < 3)
|
|
62
98
|
rails-dom-testing (2.0.3)
|
|
63
99
|
activesupport (>= 4.2.0)
|
|
64
100
|
nokogiri (>= 1.6)
|
|
65
101
|
rails-html-sanitizer (1.4.2)
|
|
66
102
|
loofah (~> 2.3)
|
|
103
|
+
railties (6.1.4.1)
|
|
104
|
+
actionpack (= 6.1.4.1)
|
|
105
|
+
activesupport (= 6.1.4.1)
|
|
106
|
+
method_source
|
|
107
|
+
rake (>= 0.13)
|
|
108
|
+
thor (~> 1.0)
|
|
67
109
|
rainbow (3.0.0)
|
|
68
110
|
rake (13.0.6)
|
|
69
111
|
regexp_parser (2.1.1)
|
|
112
|
+
responders (3.0.1)
|
|
113
|
+
actionpack (>= 5.0)
|
|
114
|
+
railties (>= 5.0)
|
|
70
115
|
rexml (3.2.5)
|
|
71
116
|
rubocop (0.93.1)
|
|
72
117
|
parallel (~> 1.10)
|
|
@@ -80,9 +125,17 @@ GEM
|
|
|
80
125
|
rubocop-ast (1.11.0)
|
|
81
126
|
parser (>= 3.0.1.1)
|
|
82
127
|
ruby-progressbar (1.11.0)
|
|
128
|
+
thor (1.1.0)
|
|
83
129
|
tzinfo (2.0.4)
|
|
84
130
|
concurrent-ruby (~> 1.0)
|
|
85
131
|
unicode-display_width (1.7.0)
|
|
132
|
+
warden (1.2.9)
|
|
133
|
+
rack (>= 2.0.9)
|
|
134
|
+
warden-jwt_auth (0.5.0)
|
|
135
|
+
dry-auto_inject (~> 0.6)
|
|
136
|
+
dry-configurable (~> 0.9)
|
|
137
|
+
jwt (~> 2.1)
|
|
138
|
+
warden (~> 1.2)
|
|
86
139
|
zeitwerk (2.4.2)
|
|
87
140
|
|
|
88
141
|
PLATFORMS
|
|
@@ -90,9 +143,6 @@ PLATFORMS
|
|
|
90
143
|
|
|
91
144
|
DEPENDENCIES
|
|
92
145
|
alpha_api!
|
|
93
|
-
api-pagination
|
|
94
|
-
fast_jsonapi
|
|
95
|
-
kaminari
|
|
96
146
|
rake (~> 13.0)
|
|
97
147
|
rubocop (~> 0.80)
|
|
98
148
|
|
data/alpha_api.gemspec
CHANGED
|
@@ -29,9 +29,14 @@ Gem::Specification.new do |spec|
|
|
|
29
29
|
|
|
30
30
|
# Uncomment to register a new dependency of your gem
|
|
31
31
|
# spec.add_dependency "example-gem", "~> 1.0"
|
|
32
|
-
spec.add_dependency '
|
|
32
|
+
spec.add_dependency 'devise-jwt'
|
|
33
|
+
|
|
33
34
|
spec.add_dependency 'fast_jsonapi'
|
|
35
|
+
spec.add_dependency 'devise'
|
|
36
|
+
spec.add_dependency 'cancancan'
|
|
37
|
+
# For actual pagination
|
|
34
38
|
spec.add_dependency 'kaminari'
|
|
39
|
+
# For rest pagination, using kaminari automatically
|
|
35
40
|
spec.add_dependency 'api-pagination'
|
|
36
41
|
|
|
37
42
|
# For more information and examples about making a new gem, checkout our
|
|
@@ -43,7 +43,7 @@ module AlphaApi
|
|
|
43
43
|
|
|
44
44
|
# Runs before the app's initializer
|
|
45
45
|
def before_initializer!
|
|
46
|
-
puts 'before initializer'
|
|
46
|
+
# puts 'before initializer'
|
|
47
47
|
ApiPagination.configure do |config|
|
|
48
48
|
config.page_param do |params|
|
|
49
49
|
if params[:page].is_a?(ActionController::Parameters) && params[:page].include?(:number)
|
|
@@ -65,7 +65,7 @@ module AlphaApi
|
|
|
65
65
|
|
|
66
66
|
# Runs after the app's initializer
|
|
67
67
|
def after_initializer!
|
|
68
|
-
puts 'after initializer'
|
|
68
|
+
# puts 'after initializer'
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
private
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
module AlphaApi
|
|
2
|
+
class BaseController < ActionController::API
|
|
3
|
+
before_action :verify_auth_token
|
|
4
|
+
|
|
5
|
+
rescue_from StandardError do |exception|
|
|
6
|
+
AlphaApi.logger.error(exception.message)
|
|
7
|
+
AlphaApi.logger.error(exception.backtrace.join("\n"))
|
|
8
|
+
|
|
9
|
+
error = error_generator(
|
|
10
|
+
'Internal Server Error',
|
|
11
|
+
'Internal server error.'
|
|
12
|
+
)
|
|
13
|
+
render json: { errors: [error] }, status: :internal_server_error
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
rescue_from ActiveRecord::DeleteRestrictionError do |exception|
|
|
17
|
+
error = error_generator(
|
|
18
|
+
'Bad Request',
|
|
19
|
+
exception.message
|
|
20
|
+
)
|
|
21
|
+
render json: { errors: [error] }, status: :bad_request
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
rescue_from ActionController::ParameterMissing do |exception|
|
|
25
|
+
error = error_generator(
|
|
26
|
+
'Bad Request',
|
|
27
|
+
"Required parameter '#{exception.param}' is missing."
|
|
28
|
+
)
|
|
29
|
+
render json: { errors: [error] }, status: :bad_request
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
rescue_from ActionController::UnpermittedParameters do |exception|
|
|
33
|
+
errors = exception.params.map do |param|
|
|
34
|
+
error_generator(
|
|
35
|
+
'Bad Request',
|
|
36
|
+
"Parameter '#{param}' is not permitted."
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
render json: { errors: errors }, status: :bad_request
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
rescue_from ActiveRecord::RecordNotFound, with: :not_found
|
|
43
|
+
|
|
44
|
+
rescue_from CanCan::AccessDenied, with: :deny_access
|
|
45
|
+
|
|
46
|
+
rescue_from AlphaApi::Exceptions::MethodNotAllowed, with: :method_not_allowed
|
|
47
|
+
|
|
48
|
+
rescue_from AlphaApi::Exceptions::InvalidRequest do |exception|
|
|
49
|
+
error = error_generator('Bad Request', exception.message)
|
|
50
|
+
render json: { errors: [error] }, status: :bad_request
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
rescue_from AlphaApi::Exceptions::ValidationErrors, with: :render_validation_errors
|
|
54
|
+
|
|
55
|
+
attr_reader :current_auth_token, :current_auth_token_payload, :current_user
|
|
56
|
+
|
|
57
|
+
def error_generator(title, detail, source = nil)
|
|
58
|
+
error = { title: title, detail: detail }
|
|
59
|
+
if source
|
|
60
|
+
key_mapper = {
|
|
61
|
+
organisation: 'organisation_id',
|
|
62
|
+
splash: 'splash_id',
|
|
63
|
+
box_hardware: 'box_hardware_id'
|
|
64
|
+
}
|
|
65
|
+
error[:source] = { pointer: "/data/attributes/#{key_mapper[source] || source}" }
|
|
66
|
+
end
|
|
67
|
+
error
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def unsupported_media_type
|
|
71
|
+
head :unsupported_media_type
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def not_found
|
|
75
|
+
error = error_generator(
|
|
76
|
+
'Not Found',
|
|
77
|
+
"Resource #{request.path} is not found"
|
|
78
|
+
)
|
|
79
|
+
render json: { errors: [error] }, status: :not_found
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def method_not_allowed
|
|
83
|
+
error = error_generator(
|
|
84
|
+
'Method Not Allowed',
|
|
85
|
+
"Method #{request.method} is not allowed on #{request.path}"
|
|
86
|
+
)
|
|
87
|
+
render json: { errors: [error] }, status: :method_not_allowed
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
alias create method_not_allowed
|
|
91
|
+
alias destroy method_not_allowed
|
|
92
|
+
alias index method_not_allowed
|
|
93
|
+
alias update method_not_allowed
|
|
94
|
+
alias show not_found
|
|
95
|
+
|
|
96
|
+
def version
|
|
97
|
+
render json: Constants::VERSION
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
protected
|
|
101
|
+
|
|
102
|
+
def bad_request(reason)
|
|
103
|
+
error = error_generator('Bad Request', reason)
|
|
104
|
+
render json: { errors: [error] }, status: :bad_request
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def forbidden(reason)
|
|
108
|
+
error = error_generator('Forbidden', reason)
|
|
109
|
+
render json: { errors: [error] }, status: :forbidden
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def set_content_type_jsonapi
|
|
113
|
+
response.headers['Content-Type'] = 'application/vnd.api+json; charset=utf-8'
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def set_content_type_json
|
|
117
|
+
response.headers['Content-Type'] = 'application/json; charset=utf-8'
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def unauthorized(reason)
|
|
121
|
+
error = error_generator('Unauthorized', reason)
|
|
122
|
+
render json: { errors: [error] }, status: :unauthorized
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def unprocessable_entity(errors)
|
|
126
|
+
errors = errors.map do |attr, message|
|
|
127
|
+
error_generator('Validation Error', message, attr)
|
|
128
|
+
end
|
|
129
|
+
render json: { errors: errors }, status: :unprocessable_entity
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def verify_auth_token
|
|
133
|
+
# Check for HTTP Authorization header
|
|
134
|
+
authorization = request.headers['Authorization']
|
|
135
|
+
AlphaApi.logger.warn "verify_auth_token: authorization #{authorization}"
|
|
136
|
+
return unauthorized '缺少Authorization头文件' unless authorization.present?
|
|
137
|
+
|
|
138
|
+
# Extract token from header
|
|
139
|
+
matches = authorization.match(/^Bearer (.+)/)
|
|
140
|
+
token = matches.captures[0] if matches
|
|
141
|
+
return unauthorized 'Authorization头文件错误' unless token.present?
|
|
142
|
+
|
|
143
|
+
check_auth_token(token) do |user, payload|
|
|
144
|
+
@current_auth_token = token
|
|
145
|
+
@current_auth_token_payload = payload
|
|
146
|
+
@current_user = user
|
|
147
|
+
nil
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
private
|
|
152
|
+
|
|
153
|
+
def check_auth_token(token)
|
|
154
|
+
# Attempt to decode the auth token payload
|
|
155
|
+
payload = nil
|
|
156
|
+
begin
|
|
157
|
+
payload = Warden::JWTAuth::TokenDecoder.new.call token
|
|
158
|
+
rescue JWT::ExpiredSignature
|
|
159
|
+
payload = JWT.decode(token, nil, false)[0]
|
|
160
|
+
unless payload['aud'] == 'mobile'
|
|
161
|
+
return unauthorized '验证信息已经过期,请重新登陆.'
|
|
162
|
+
end
|
|
163
|
+
rescue StandardError => e
|
|
164
|
+
AlphaApi.logger.warn "Failed to decode authentication token: #{e.message}, token: #{token}"
|
|
165
|
+
return unauthorized '验证信息错误'
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
return unauthorized '验证信息错误' unless payload
|
|
169
|
+
|
|
170
|
+
# Check that token is still valid
|
|
171
|
+
user = User.find_by(id: payload['user_id'])
|
|
172
|
+
return unauthorized '验证信息错误' unless user
|
|
173
|
+
return unauthorized '验证信息已无效' if DenylistedToken.jwt_revoked?(payload, user)
|
|
174
|
+
|
|
175
|
+
yield user, payload
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def render_validation_errors(exception)
|
|
179
|
+
unprocessable_entity(exception.errors)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def deny_access
|
|
183
|
+
forbidden '没有权限访问该资源'
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
require
|
|
1
|
+
require 'active_support/concern'
|
|
2
|
+
require 'active_record'
|
|
3
|
+
require_relative '../exceptions'
|
|
2
4
|
|
|
3
5
|
module AlphaApi
|
|
4
6
|
module Concerns
|
|
@@ -14,7 +16,7 @@ module AlphaApi
|
|
|
14
16
|
render status: :created, json: resource_serializer.new(new_resource).serializable_hash
|
|
15
17
|
else
|
|
16
18
|
errors = reformat_validation_error(new_resource)
|
|
17
|
-
raise Exceptions::ValidationErrors.new(errors), 'Validation Errors'
|
|
19
|
+
raise AlphaApi::Exceptions::ValidationErrors.new(errors), 'Validation Errors'
|
|
18
20
|
end
|
|
19
21
|
end
|
|
20
22
|
|
|
@@ -50,7 +52,7 @@ module AlphaApi
|
|
|
50
52
|
render json: resource_serializer.new(updated_resource, options).serializable_hash
|
|
51
53
|
else
|
|
52
54
|
errors = reformat_validation_error(resource)
|
|
53
|
-
raise Exceptions::ValidationErrors.new(errors), 'Validation Errors'
|
|
55
|
+
raise AlphaApi::Exceptions::ValidationErrors.new(errors), 'Validation Errors'
|
|
54
56
|
end
|
|
55
57
|
end
|
|
56
58
|
|
|
@@ -61,10 +63,10 @@ module AlphaApi
|
|
|
61
63
|
if resource.destroy
|
|
62
64
|
head :no_content
|
|
63
65
|
else
|
|
64
|
-
raise Exceptions::ValidationErrors.new(resource.errors), 'Validation Errors'
|
|
66
|
+
raise AlphaApi::Exceptions::ValidationErrors.new(resource.errors), 'Validation Errors'
|
|
65
67
|
end
|
|
66
68
|
else
|
|
67
|
-
raise Exceptions::MethodNotAllowed, 'Method Not Allowed'
|
|
69
|
+
raise AlphaApi::Exceptions::MethodNotAllowed, 'Method Not Allowed'
|
|
68
70
|
end
|
|
69
71
|
end
|
|
70
72
|
|
|
@@ -92,7 +94,7 @@ module AlphaApi
|
|
|
92
94
|
# @override customised filters
|
|
93
95
|
def apply_filter(query)
|
|
94
96
|
if filterable_fields.empty?
|
|
95
|
-
raise Exceptions::InvalidFilter, 'Filters are not supported for this resource type'
|
|
97
|
+
raise AlphaApi::Exceptions::InvalidFilter, 'Filters are not supported for this resource type'
|
|
96
98
|
else
|
|
97
99
|
query
|
|
98
100
|
end
|
|
@@ -128,7 +130,7 @@ module AlphaApi
|
|
|
128
130
|
elsif valid_boolean?(field, value)
|
|
129
131
|
query = query.where(field => value == 'true')
|
|
130
132
|
else
|
|
131
|
-
raise Exceptions::InvalidFilter, 'Only type of string and uuid fields are supported'
|
|
133
|
+
raise AlphaApi::Exceptions::InvalidFilter, 'Only type of string and uuid fields are supported'
|
|
132
134
|
end
|
|
133
135
|
end
|
|
134
136
|
query
|
|
@@ -153,10 +155,10 @@ module AlphaApi
|
|
|
153
155
|
if allow_all && page_size == -1
|
|
154
156
|
params[:page] = nil
|
|
155
157
|
else
|
|
156
|
-
raise Exceptions::InvalidRequest, 'Page number must be positive' unless page_number.positive?
|
|
157
|
-
raise Exceptions::InvalidRequest, 'Page offset must be non-negative' if page_offset.negative?
|
|
158
|
-
raise Exceptions::InvalidRequest, 'Page size must be positive' unless page_size.positive?
|
|
159
|
-
raise Exceptions::InvalidRequest, 'Page size cannot be greater than 100' if page_size > 100
|
|
158
|
+
raise AlphaApi::Exceptions::InvalidRequest, 'Page number must be positive' unless page_number.positive?
|
|
159
|
+
raise AlphaApi::Exceptions::InvalidRequest, 'Page offset must be non-negative' if page_offset.negative?
|
|
160
|
+
raise AlphaApi::Exceptions::InvalidRequest, 'Page size must be positive' unless page_size.positive?
|
|
161
|
+
raise AlphaApi::Exceptions::InvalidRequest, 'Page size cannot be greater than 100' if page_size > 100
|
|
160
162
|
|
|
161
163
|
params[:page] = {
|
|
162
164
|
number: page_number,
|
|
@@ -173,13 +175,13 @@ module AlphaApi
|
|
|
173
175
|
def apply_sorting(query)
|
|
174
176
|
sort_params = params['sort']
|
|
175
177
|
return query.order(default_sorting) unless sort_params.present?
|
|
176
|
-
raise Exceptions::InvalidRequest, 'Sort parameter must be a string' unless sort_params.is_a? String
|
|
178
|
+
raise AlphaApi::Exceptions::InvalidRequest, 'Sort parameter must be a string' unless sort_params.is_a? String
|
|
177
179
|
sorting = []
|
|
178
180
|
|
|
179
181
|
sorts = sort_params.split(',').map(&:strip).map do |sort|
|
|
180
182
|
is_desc = sort.start_with?('-')
|
|
181
183
|
sort = is_desc ? sort[1..-1] : sort
|
|
182
|
-
raise Exceptions::InvalidRequest, "Sorting by #{sort} is not allowed" unless allowed_sortings.include?(sort.to_sym)
|
|
184
|
+
raise AlphaApi::Exceptions::InvalidRequest, "Sorting by #{sort} is not allowed" unless allowed_sortings.include?(sort.to_sym)
|
|
183
185
|
sort = association_mapper(sort)
|
|
184
186
|
|
|
185
187
|
# have to includes the association to be able to sort on
|
|
@@ -213,7 +215,7 @@ module AlphaApi
|
|
|
213
215
|
def reconcile_nested_attributes(existing_items, items_in_update)
|
|
214
216
|
item_ids_in_update = items_in_update.map { |item| item['id'] }.compact
|
|
215
217
|
if item_ids_in_update.uniq.length != item_ids_in_update.length
|
|
216
|
-
raise(Exceptions::InvalidRequest, 'Nested attribute IDs must be unique')
|
|
218
|
+
raise(AlphaApi::Exceptions::InvalidRequest, 'Nested attribute IDs must be unique')
|
|
217
219
|
end
|
|
218
220
|
|
|
219
221
|
nested_attributes = []
|
|
@@ -246,7 +248,7 @@ module AlphaApi
|
|
|
246
248
|
invalid_resources = []
|
|
247
249
|
nested_resources.each { |res| invalid_resources.push(res) unless allowed_associations.include?(res.to_sym) }
|
|
248
250
|
unless invalid_resources.empty?
|
|
249
|
-
raise Exceptions::InvalidArgument, "Invalid value for include: #{invalid_resources.join(', ')}"
|
|
251
|
+
raise AlphaApi::Exceptions::InvalidArgument, "Invalid value for include: #{invalid_resources.join(', ')}"
|
|
250
252
|
end
|
|
251
253
|
|
|
252
254
|
nested_resources
|
|
@@ -291,7 +293,7 @@ module AlphaApi
|
|
|
291
293
|
def reconcile_nested_attributes(existing_items, items_in_update)
|
|
292
294
|
item_ids_in_update = items_in_update.map { |item| item['id'] }.compact
|
|
293
295
|
if item_ids_in_update.uniq.length != item_ids_in_update.length
|
|
294
|
-
raise(Exceptions::InvalidRequest, 'Nested attribute IDs must be unique')
|
|
296
|
+
raise(AlphaApi::Exceptions::InvalidRequest, 'Nested attribute IDs must be unique')
|
|
295
297
|
end
|
|
296
298
|
|
|
297
299
|
nested_attributes = []
|
|
@@ -336,7 +338,7 @@ module AlphaApi
|
|
|
336
338
|
"#{association.table_name}.#{attr_name} #{order}"
|
|
337
339
|
else
|
|
338
340
|
# could potencially support that as well by includes deeply nested associations
|
|
339
|
-
raise Exceptions::InvalidRequest, 'Sorting on deeply nested association is not supported'
|
|
341
|
+
raise AlphaApi::Exceptions::InvalidRequest, 'Sorting on deeply nested association is not supported'
|
|
340
342
|
end
|
|
341
343
|
end
|
|
342
344
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module AlphaApi
|
|
2
|
+
module Exceptions
|
|
3
|
+
class InvalidRequest < StandardError; end
|
|
4
|
+
class InvalidFilter < InvalidRequest; end
|
|
5
|
+
class InvalidArgument < InvalidRequest; end
|
|
6
|
+
class MethodNotAllowed < StandardError; end
|
|
7
|
+
class ValidationErrors < StandardError
|
|
8
|
+
attr_reader :errors
|
|
9
|
+
|
|
10
|
+
def initialize(errors = [])
|
|
11
|
+
super
|
|
12
|
+
@errors = errors
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/alpha_api/version.rb
CHANGED
data/lib/alpha_api.rb
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'active_support'
|
|
4
|
+
require 'action_pack'
|
|
5
|
+
require 'fast_jsonapi'
|
|
6
|
+
require 'devise'
|
|
7
|
+
require 'devise/jwt'
|
|
8
|
+
require 'cancancan'
|
|
9
|
+
require 'kaminari'
|
|
10
|
+
require 'api-pagination'
|
|
11
|
+
require 'active_record'
|
|
12
|
+
require 'active_record/associations'
|
|
13
|
+
|
|
3
14
|
require_relative "alpha_api/version"
|
|
4
15
|
require_relative "alpha_api/application"
|
|
5
16
|
require_relative "generators/resource/resource_generator"
|
|
6
17
|
require_relative "generators/install/install_generator"
|
|
7
18
|
require_relative 'alpha_api/application_settings'
|
|
8
19
|
require_relative 'alpha_api/concerns/actionable'
|
|
20
|
+
require_relative 'alpha_api/base_controller'
|
|
9
21
|
require_relative 'alpha_api/serializers/application_record_serializer'
|
|
22
|
+
require_relative 'alpha_api/exceptions'
|
|
10
23
|
|
|
11
24
|
module AlphaApi
|
|
12
25
|
class Error < StandardError; end
|
|
@@ -30,3 +43,4 @@ module AlphaApi
|
|
|
30
43
|
end
|
|
31
44
|
end
|
|
32
45
|
end
|
|
46
|
+
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
require 'rails/generators/active_record'
|
|
4
|
+
|
|
3
5
|
module AlphaApi
|
|
4
6
|
module Generators
|
|
5
7
|
class InstallGenerator < Rails::Generators::Base
|
|
8
|
+
include Rails::Generators::Migration
|
|
6
9
|
desc "Installs AlphaApi"
|
|
7
10
|
|
|
8
11
|
source_root File.expand_path("templates", __dir__)
|
|
@@ -11,8 +14,50 @@ module AlphaApi
|
|
|
11
14
|
template "alpha_api.rb.erb", "config/initializers/alpha_api.rb"
|
|
12
15
|
end
|
|
13
16
|
|
|
14
|
-
def
|
|
15
|
-
puts '
|
|
17
|
+
def add_device_initializer
|
|
18
|
+
puts 'Setup device'
|
|
19
|
+
system 'bundle exec rails generate devise:install'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def create_user_model
|
|
23
|
+
migration_template "create_devise_users.rb", "db/migrate/create_devise_users.rb", migration_version: migration_version
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def add_denylisted_token_model
|
|
27
|
+
template "denylisted_token.rb.erb", "app/models/denylisted_token.rb"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def add_denylisted_token_migration
|
|
31
|
+
migration_template "add_denylisted_token.rb", "db/migrate/add_denylisted_token.rb", migration_version: migration_version
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def append_jwt_devise_initializer
|
|
35
|
+
inject_into_file 'config/initializers/devise.rb', after: "Devise.setup do |config|\n" do
|
|
36
|
+
<<~HEREDOC
|
|
37
|
+
|
|
38
|
+
config.jwt do |jwt|
|
|
39
|
+
jwt.secret = Rails.application.secrets.secret_key_base
|
|
40
|
+
jwt.expiration_time = 24.hours.to_i
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
HEREDOC
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def initialize_cancancan
|
|
48
|
+
puts 'Setup Cancancan ability class'
|
|
49
|
+
system 'bundle exec rails generate cancan:ability'
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def self.next_migration_number(dirname)
|
|
55
|
+
next_migration_number = current_migration_number(dirname) + 1
|
|
56
|
+
ActiveRecord::Migration.next_migration_number(next_migration_number)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def migration_version
|
|
60
|
+
"[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
|
|
16
61
|
end
|
|
17
62
|
end
|
|
18
63
|
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class AddDenylistedToken < ActiveRecord::Migration<%= migration_version %>
|
|
4
|
+
def change
|
|
5
|
+
create_table :denylisted_tokens do |t|
|
|
6
|
+
t.string :jti, null: false
|
|
7
|
+
t.datetime :exp, null: false
|
|
8
|
+
end
|
|
9
|
+
add_index :denylisted_tokens, :jti
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateDeviseUsers < ActiveRecord::Migration<%= migration_version %>
|
|
4
|
+
def change
|
|
5
|
+
create_table :users do |t|
|
|
6
|
+
## Database authenticatable
|
|
7
|
+
t.string :email, null: false, default: ''
|
|
8
|
+
t.string :encrypted_password, null: false, default: ''
|
|
9
|
+
|
|
10
|
+
## Recoverable
|
|
11
|
+
t.string :reset_password_token
|
|
12
|
+
t.datetime :reset_password_sent_at
|
|
13
|
+
|
|
14
|
+
## Rememberable
|
|
15
|
+
t.datetime :remember_created_at
|
|
16
|
+
|
|
17
|
+
## Trackable
|
|
18
|
+
# t.integer :sign_in_count, default: 0, null: false
|
|
19
|
+
# t.datetime :current_sign_in_at
|
|
20
|
+
# t.datetime :last_sign_in_at
|
|
21
|
+
# t.inet :current_sign_in_ip
|
|
22
|
+
# t.inet :last_sign_in_ip
|
|
23
|
+
|
|
24
|
+
## Confirmable
|
|
25
|
+
# t.string :confirmation_token
|
|
26
|
+
# t.datetime :confirmed_at
|
|
27
|
+
# t.datetime :confirmation_sent_at
|
|
28
|
+
# t.string :unconfirmed_email # Only if using reconfirmable
|
|
29
|
+
|
|
30
|
+
## Lockable
|
|
31
|
+
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
|
|
32
|
+
# t.string :unlock_token # Only if unlock strategy is :email or :both
|
|
33
|
+
# t.datetime :locked_at
|
|
34
|
+
|
|
35
|
+
t.timestamps null: false
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
add_index :users, :email, unique: true
|
|
39
|
+
add_index :users, :reset_password_token, unique: true
|
|
40
|
+
# add_index :users, :confirmation_token, unique: true
|
|
41
|
+
# add_index :users, :unlock_token, unique: true
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
class <%= @boilerplate.module_name %>::<%= class_name.pluralize %>Controller <
|
|
1
|
+
class <%= @boilerplate.module_name %>::<%= class_name.pluralize %>Controller < AlphaApi::BaseController
|
|
2
2
|
include AlphaApi::Concerns::Actionable
|
|
3
3
|
|
|
4
4
|
protected
|
metadata
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: alpha_api
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alba Hoo
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-09-
|
|
11
|
+
date: 2021-09-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
14
|
+
name: devise-jwt
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
@@ -38,6 +38,34 @@ dependencies:
|
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: devise
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: cancancan
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
41
69
|
- !ruby/object:Gem::Dependency
|
|
42
70
|
name: kaminari
|
|
43
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -88,16 +116,22 @@ files:
|
|
|
88
116
|
- lib/alpha_api.rb
|
|
89
117
|
- lib/alpha_api/application.rb
|
|
90
118
|
- lib/alpha_api/application_settings.rb
|
|
119
|
+
- lib/alpha_api/base_controller.rb
|
|
91
120
|
- lib/alpha_api/concerns/actionable.rb
|
|
92
121
|
- lib/alpha_api/dynamic_setting.rb
|
|
93
122
|
- lib/alpha_api/dynamic_settings_node.rb
|
|
123
|
+
- lib/alpha_api/exceptions.rb
|
|
124
|
+
- lib/alpha_api/logger.rb
|
|
94
125
|
- lib/alpha_api/namespace_settings.rb
|
|
95
126
|
- lib/alpha_api/resource_collection.rb
|
|
96
127
|
- lib/alpha_api/serializers/application_record_serializer.rb
|
|
97
128
|
- lib/alpha_api/settings_node.rb
|
|
98
129
|
- lib/alpha_api/version.rb
|
|
99
130
|
- lib/generators/install/install_generator.rb
|
|
131
|
+
- lib/generators/install/templates/add_denylisted_token.rb
|
|
100
132
|
- lib/generators/install/templates/alpha_api.rb.erb
|
|
133
|
+
- lib/generators/install/templates/create_devise_users.rb
|
|
134
|
+
- lib/generators/install/templates/denylisted_token.rb.erb
|
|
101
135
|
- lib/generators/resource/resource_generator.rb
|
|
102
136
|
- lib/generators/resource/templates/controller.rb.erb
|
|
103
137
|
- lib/generators/resource/templates/serializer.rb.erb
|