bullet_train-api 1.0.16 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/api/controllers/base.rb +69 -0
  3. data/app/controllers/concerns/api/v1/teams/controller_base.rb +49 -0
  4. data/app/controllers/concerns/api/v1/users/controller_base.rb +42 -0
  5. data/app/views/api/v1/teams/index.json.jbuilder +5 -0
  6. data/app/views/api/v1/teams/show.json.jbuilder +1 -0
  7. data/app/views/api/v1/users/index.json.jbuilder +5 -0
  8. data/app/views/api/v1/users/show.json.jbuilder +1 -0
  9. data/config/routes.rb +15 -1
  10. data/lib/bullet_train/api/engine.rb +0 -13
  11. data/lib/bullet_train/api/version.rb +1 -1
  12. data/lib/bullet_train/api.rb +2 -7
  13. metadata +12 -108
  14. data/app/controllers/api/base.rb +0 -31
  15. data/app/controllers/api/model_parser.rb +0 -42
  16. data/app/controllers/api/v1/defaults.rb +0 -36
  17. data/app/controllers/api/v1/exceptions_handler.rb +0 -50
  18. data/app/controllers/api/v1/loads_and_authorizes_api_resource.rb +0 -42
  19. data/app/controllers/api/v1/me_endpoint.rb +0 -9
  20. data/app/controllers/api/v1/teams_endpoint.rb +0 -9
  21. data/app/controllers/api/v1.rb +0 -4
  22. data/app/controllers/api.rb +0 -96
  23. data/app/controllers/concerns/api/v1/base.rb +0 -37
  24. data/app/controllers/concerns/api/v1/teams/endpoint_base.rb +0 -87
  25. data/app/serializers/api/v1/invitation_serializer.rb +0 -8
  26. data/app/serializers/api/v1/membership_serializer.rb +0 -8
  27. data/app/serializers/api/v1/team_serializer.rb +0 -8
  28. data/app/serializers/api/v1/user_serializer.rb +0 -8
  29. data/app/serializers/concerns/api/v1/invitations/serializer_base.rb +0 -17
  30. data/app/serializers/concerns/api/v1/memberships/serializer_base.rb +0 -24
  31. data/app/serializers/concerns/api/v1/teams/serializer_base.rb +0 -16
  32. data/app/serializers/concerns/api/v1/users/serializer_base.rb +0 -22
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d0157f585b4b46527c96b9e0fd98d29b2a86c39df341e8158e0ba4b0843a415f
4
- data.tar.gz: a930f8262c209e4707eb7f5edf14f3c3e1537416359387337b5645356222584e
3
+ metadata.gz: ad13ea6c52199631e43bc3ef4367f886d64568faea6d88dd3f5f1857bb0416e2
4
+ data.tar.gz: 90deafe4bb3b6680f0ccaa79e715e66cfcadf247053c651badf489f4ab845e95
5
5
  SHA512:
6
- metadata.gz: 32183bf8e9287a558bcb9b9982140c1b050370709f551f9fe6bc8a47f012acd51f98a99f70962e4e71a2f40b23fc86406f5f20f5570ef3af4bb6fa1678bc1ec5
7
- data.tar.gz: 6fcce88fc1035aa09f4f55f9ac380ae63de0acc4bb01f3661d9e8a38cd07bc5cfc7a5dd14f41cff082b840251947ddba13d13946c7f96e16fa0315a6a1033b53
6
+ metadata.gz: a254ff4f0174985973a4ac4c1b28d4d6d20cc547633a34da3277393d46470485f623126f5560425b5e1e124a8832a360e9a4f9ab343a6d8704bb9669553edaed
7
+ data.tar.gz: 3a726a97a5070fdfbb092b6437e90c7019de0fd9a256bcffa035cbdcb5496ee5ac9e2212ef499297686fed843d2fba87cc47984c60199253ca685d1b96d91cfe
@@ -0,0 +1,69 @@
1
+ require "pagy_cursor/pagy/extras/cursor"
2
+ require "pagy_cursor/pagy/extras/uuid_cursor"
3
+
4
+ module Api::Controllers::Base
5
+ extend ActiveSupport::Concern
6
+
7
+ # TODO Why doesn't `before_action :doorkeeper_authorize!` throw an exception?
8
+ class NotAuthenticatedError < StandardError
9
+ end
10
+
11
+ included do
12
+ include ActionController::Helpers
13
+ helper ApplicationHelper
14
+
15
+ include LoadsAndAuthorizesResource
16
+ include Pagy::Backend
17
+
18
+ before_action :set_default_response_format
19
+ before_action :doorkeeper_authorize!
20
+
21
+ rescue_from CanCan::AccessDenied, ActiveRecord::RecordNotFound do |exception|
22
+ render json: {error: "Not found"}, status: :not_found
23
+ end
24
+
25
+ rescue_from NotAuthenticatedError do |exception|
26
+ render json: {error: "Invalid token"}, status: :unauthorized
27
+ end
28
+
29
+ before_action :apply_pagination, only: [:index]
30
+ end
31
+
32
+ def permitted_fields
33
+ []
34
+ end
35
+
36
+ def permitted_arrays
37
+ {}
38
+ end
39
+
40
+ def process_params(strong_params)
41
+ end
42
+
43
+ def current_user
44
+ raise NotAuthenticatedError unless doorkeeper_token.present?
45
+ @current_user ||= User.find_by(id: doorkeeper_token[:resource_owner_id])
46
+ end
47
+
48
+ def current_team
49
+ # Application agents are users but only have one team.
50
+ current_user&.teams&.first
51
+ end
52
+
53
+ def apply_pagination
54
+ collection_variable = "@#{self.class.name.split("::").last.gsub("Controller", "").underscore}"
55
+ collection = instance_variable_get collection_variable
56
+ @pagy, collection = pagy_cursor collection
57
+ instance_variable_set collection_variable, collection
58
+ end
59
+
60
+ def set_default_response_format
61
+ request.format = :json
62
+ end
63
+
64
+ class_methods do
65
+ def regex_to_remove_controller_namespace
66
+ /^#{name.split("::").first(2).join("::") + "::"}/
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,49 @@
1
+ module Api::V1::Teams::ControllerBase
2
+ extend ActiveSupport::Concern
3
+
4
+ module StrongParameters
5
+ # Only allow a list of trusted parameters through.
6
+ def team_params
7
+ strong_params = params.require(:team).permit(
8
+ *permitted_fields,
9
+ :name,
10
+ :time_zone,
11
+ :locale,
12
+ # 🚅 super scaffolding will insert new fields above this line.
13
+ *permitted_arrays,
14
+ # 🚅 super scaffolding will insert new arrays above this line.
15
+ )
16
+
17
+ process_params(strong_params)
18
+
19
+ strong_params
20
+ end
21
+ end
22
+
23
+ included do
24
+ load_and_authorize_resource :team, class: "Team", prepend: true,
25
+ member_actions: (defined?(MEMBER_ACTIONS) ? MEMBER_ACTIONS : []),
26
+ collection_actions: (defined?(COLLECTION_ACTIONS) ? COLLECTION_ACTIONS : [])
27
+
28
+ private
29
+
30
+ include StrongParameters
31
+ end
32
+
33
+ # GET /api/v1/teams
34
+ def index
35
+ end
36
+
37
+ # GET /api/v1/teams/:id
38
+ def show
39
+ end
40
+
41
+ # PATCH/PUT /api/v1/teams/:id
42
+ def update
43
+ if @team.update(team_params)
44
+ render :show
45
+ else
46
+ render json: @team.errors, status: :unprocessable_entity
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,42 @@
1
+ module Api::V1::Users::ControllerBase
2
+ extend ActiveSupport::Concern
3
+
4
+ module StrongParameters
5
+ # Only allow a list of trusted parameters through.
6
+ def user_params
7
+ strong_params = params.require(:user).permit(
8
+ *permitted_fields,
9
+ :email,
10
+ :first_name,
11
+ :last_name,
12
+ :time_zone,
13
+ :locale,
14
+ # 🚅 super scaffolding will insert new fields above this line.
15
+ *permitted_arrays,
16
+ # 🚅 super scaffolding will insert new arrays above this line.
17
+ )
18
+
19
+ process_params(strong_params)
20
+
21
+ strong_params
22
+ end
23
+ end
24
+
25
+ included do
26
+ load_and_authorize_resource :user, class: "User", prepend: true,
27
+ member_actions: (defined?(MEMBER_ACTIONS) ? MEMBER_ACTIONS : []),
28
+ collection_actions: (defined?(COLLECTION_ACTIONS) ? COLLECTION_ACTIONS : [])
29
+
30
+ private
31
+
32
+ include StrongParameters
33
+ end
34
+
35
+ # GET /api/v1/users
36
+ def index
37
+ end
38
+
39
+ # GET /api/v1/users/:id
40
+ def show
41
+ end
42
+ end
@@ -0,0 +1,5 @@
1
+ json.data do
2
+ json.array! @teams, partial: "api/v1/teams/team", as: :team
3
+ end
4
+
5
+ render_pagination(json)
@@ -0,0 +1 @@
1
+ json.partial! "api/v1/teams/team", team: @team
@@ -0,0 +1,5 @@
1
+ json.data do
2
+ json.array! @users, partial: "api/v1/users/user", as: :user
3
+ end
4
+
5
+ render_pagination(json)
@@ -0,0 +1 @@
1
+ json.partial! "api/v1/users/user", user: @user
data/config/routes.rb CHANGED
@@ -1,4 +1,18 @@
1
1
  Rails.application.routes.draw do
2
2
  use_doorkeeper
3
- mount Api::Base, at: "/api"
3
+
4
+ namespace :api do
5
+ namespace :v1 do
6
+ shallow do
7
+ resources :users
8
+ resources :teams do
9
+ resources :invitations
10
+ resources :memberships
11
+ namespace :platform do
12
+ resources :applications
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
4
18
  end
@@ -1,22 +1,9 @@
1
- require "grape"
2
- require "grape-cancan"
3
- require "grape_jsonapi"
4
- require "grape-swagger"
5
- require "grape_on_rails_routes"
6
- # require "wine_bouncer"
7
1
  require "pagy"
8
- require "api-pagination"
9
2
  require "rack/cors"
10
3
 
11
4
  module BulletTrain
12
5
  module Api
13
6
  class Engine < ::Rails::Engine
14
- initializer "bullet_train.api.register_api_endpoints" do |app|
15
- if BulletTrain::Api
16
- BulletTrain::Api.endpoints << "Api::V1::MeEndpoint"
17
- BulletTrain::Api.endpoints << "Api::V1::TeamsEndpoint"
18
- end
19
- end
20
7
  end
21
8
  end
22
9
  end
@@ -1,5 +1,5 @@
1
1
  module BulletTrain
2
2
  module Api
3
- VERSION = "1.0.16"
3
+ VERSION = "1.1.1"
4
4
  end
5
5
  end
@@ -1,20 +1,15 @@
1
1
  require "bullet_train/api/version"
2
2
  require "bullet_train/api/engine"
3
3
 
4
- require "grape"
5
- require "grape-cancan"
6
- require "grape_jsonapi"
7
- require "grape-swagger"
8
- require "grape_on_rails_routes"
9
4
  # require "wine_bouncer"
10
5
  require "pagy"
11
- require "api-pagination"
6
+ require "pagy_cursor"
12
7
  require "rack/cors"
13
- require "jsonapi/serializer"
14
8
  require "doorkeeper"
15
9
 
16
10
  module BulletTrain
17
11
  module Api
18
12
  mattr_accessor :endpoints, default: []
13
+ mattr_accessor :current_version, default: "v1"
19
14
  end
20
15
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bullet_train-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.16
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Culver
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-05-07 00:00:00.000000000 Z
11
+ date: 2022-09-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: standard
@@ -30,84 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 7.0.0
33
+ version: 6.0.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 7.0.0
41
- - !ruby/object:Gem::Dependency
42
- name: grape
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: 1.6.0
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: 1.6.0
55
- - !ruby/object:Gem::Dependency
56
- name: grape-cancan
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'
69
- - !ruby/object:Gem::Dependency
70
- name: grape-jsonapi
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - '='
74
- - !ruby/object:Gem::Version
75
- version: 1.0.0
76
- type: :runtime
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - '='
81
- - !ruby/object:Gem::Version
82
- version: 1.0.0
83
- - !ruby/object:Gem::Dependency
84
- name: grape-swagger
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :runtime
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
- - !ruby/object:Gem::Dependency
98
- name: grape_on_rails_routes
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: '0'
104
- type: :runtime
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - ">="
109
- - !ruby/object:Gem::Version
110
- version: '0'
40
+ version: 6.0.0
111
41
  - !ruby/object:Gem::Dependency
112
42
  name: pagy
113
43
  requirement: !ruby/object:Gem::Requirement
@@ -123,7 +53,7 @@ dependencies:
123
53
  - !ruby/object:Gem::Version
124
54
  version: '0'
125
55
  - !ruby/object:Gem::Dependency
126
- name: api-pagination
56
+ name: pagy_cursor
127
57
  requirement: !ruby/object:Gem::Requirement
128
58
  requirements:
129
59
  - - ">="
@@ -150,20 +80,6 @@ dependencies:
150
80
  - - ">="
151
81
  - !ruby/object:Gem::Version
152
82
  version: '0'
153
- - !ruby/object:Gem::Dependency
154
- name: jsonapi-serializer
155
- requirement: !ruby/object:Gem::Requirement
156
- requirements:
157
- - - ">="
158
- - !ruby/object:Gem::Version
159
- version: '0'
160
- type: :runtime
161
- prerelease: false
162
- version_requirements: !ruby/object:Gem::Requirement
163
- requirements:
164
- - - ">="
165
- - !ruby/object:Gem::Version
166
- version: '0'
167
83
  - !ruby/object:Gem::Dependency
168
84
  name: doorkeeper
169
85
  requirement: !ruby/object:Gem::Requirement
@@ -204,26 +120,10 @@ files:
204
120
  - Rakefile
205
121
  - app/assets/config/bullet_train_api_manifest.js
206
122
  - app/controllers/account/platform/applications_controller.rb
207
- - app/controllers/api.rb
208
- - app/controllers/api/base.rb
209
- - app/controllers/api/model_parser.rb
210
- - app/controllers/api/v1.rb
211
- - app/controllers/api/v1/defaults.rb
212
- - app/controllers/api/v1/exceptions_handler.rb
213
- - app/controllers/api/v1/loads_and_authorizes_api_resource.rb
214
- - app/controllers/api/v1/me_endpoint.rb
215
- - app/controllers/api/v1/teams_endpoint.rb
216
- - app/controllers/concerns/api/v1/base.rb
217
- - app/controllers/concerns/api/v1/teams/endpoint_base.rb
123
+ - app/controllers/concerns/api/controllers/base.rb
124
+ - app/controllers/concerns/api/v1/teams/controller_base.rb
125
+ - app/controllers/concerns/api/v1/users/controller_base.rb
218
126
  - app/models/platform/application.rb
219
- - app/serializers/api/v1/invitation_serializer.rb
220
- - app/serializers/api/v1/membership_serializer.rb
221
- - app/serializers/api/v1/team_serializer.rb
222
- - app/serializers/api/v1/user_serializer.rb
223
- - app/serializers/concerns/api/v1/invitations/serializer_base.rb
224
- - app/serializers/concerns/api/v1/memberships/serializer_base.rb
225
- - app/serializers/concerns/api/v1/teams/serializer_base.rb
226
- - app/serializers/concerns/api/v1/users/serializer_base.rb
227
127
  - app/views/account/platform/applications/_application.json.jbuilder
228
128
  - app/views/account/platform/applications/_breadcrumbs.html.erb
229
129
  - app/views/account/platform/applications/_form.html.erb
@@ -235,6 +135,10 @@ files:
235
135
  - app/views/account/platform/applications/new.html.erb
236
136
  - app/views/account/platform/applications/show.html.erb
237
137
  - app/views/account/platform/applications/show.json.jbuilder
138
+ - app/views/api/v1/teams/index.json.jbuilder
139
+ - app/views/api/v1/teams/show.json.jbuilder
140
+ - app/views/api/v1/users/index.json.jbuilder
141
+ - app/views/api/v1/users/show.json.jbuilder
238
142
  - config/locales/en/api.en.yml
239
143
  - config/locales/en/me.en.yml
240
144
  - config/locales/en/platform/applications.en.yml
@@ -1,31 +0,0 @@
1
- require "grape-swagger"
2
- require_relative "../api"
3
-
4
- class Api::Base < Grape::API
5
- content_type :jsonapi, "application/vnd.api+json"
6
- formatter :json, Grape::Formatter::Jsonapi
7
- formatter :jsonapi, Grape::Formatter::Jsonapi
8
- format :jsonapi
9
- default_error_formatter :json
10
-
11
- mount Api::V1::Root
12
-
13
- # Swagger docs are available at `/api/docs/swagger.json`.
14
- add_swagger_documentation \
15
- api_version: "v1",
16
- array_use_braces: true,
17
- base_path: "/api",
18
- doc_version: "1.0",
19
- endpoint_auth_wrapper: ::WineBouncer::OAuth2,
20
- hide_documentation_path: true,
21
- info: {
22
- title: I18n.t("application.name"),
23
- description: I18n.t("application.description")
24
- },
25
- mount_path: "/docs/swagger"
26
-
27
- # TODO Reintroduce this once we've got `context` in current attributes.
28
- # before do
29
- # Current.context = :api
30
- # end
31
- end
@@ -1,42 +0,0 @@
1
- class Api::ModelParser < GrapeSwagger::Jsonapi::Parser
2
- attr_reader :model, :endpoint
3
-
4
- alias_method :schema, :call
5
-
6
- def initialize(model, endpoint)
7
- @model = model
8
- @endpoint = endpoint
9
- end
10
-
11
- def call
12
- # first let's grab the schema generated by the JSON:API parser
13
- schema_json = schema.to_json
14
-
15
- # From Nick Schneble:
16
-
17
- # we can easily override these types for our API endpoints in the documentation
18
- # but we can't do the same thing for the relationship objects that are auto-generated
19
- # thus the fancy affair below
20
-
21
- # if you want to learn more about what's happening here, read these:
22
- # https://stackoverflow.com/a/17918118/1322386
23
- # https://swagger.io/docs/specification/data-models/data-types/
24
-
25
- # Swagger 3.0 only supports a subset of Ruby data types
26
- schema_json.gsub!("\"type\":\"binary\"", "\"type\":\"string\", \"format\":\"binary\"")
27
- schema_json.gsub!("\"type\":\"date\"", "\"type\":\"string\", \"format\":\"date\"")
28
- schema_json.gsub!("\"type\":\"datetime\"", "\"type\":\"string\", \"format\":\"date-time\"")
29
- schema_json.gsub!("\"type\":\"decimal\"", "\"type\":\"number\", \"format\":\"double\"")
30
- schema_json.gsub!("\"type\":\"float\"", "\"type\":\"number\", \"format\":\"float\"")
31
- schema_json.gsub!("\"type\":\"bigint\"", "\"type\":\"integer\", \"format\":\"int64\"")
32
- schema_json.gsub!("\"type\":\"primary_key\"", "\"type\":\"integer\", \"format\":\"int64\"")
33
- schema_json.gsub!("\"type\":\"references\"", "\"type\":\"object\"")
34
- schema_json.gsub!("\"type\":\"text\"", "\"type\":\"string\"")
35
- schema_json.gsub!("\"type\":\"time\"", "\"type\":\"string\", \"format\":\"time\"")
36
- schema_json.gsub!("\"type\":\"timestamp\"", "\"type\":\"string\", \"format\":\"timestamp\"")
37
- schema_json.gsub!("\"type\":\"json\"", "\"type\":\"array\", \"items\":{\"type\":\"string\"}")
38
-
39
- # returns a Hash as if nothing fancy happened
40
- JSON.parse(schema_json)
41
- end
42
- end
@@ -1,36 +0,0 @@
1
- module Api::V1::Defaults
2
- extend ActiveSupport::Concern
3
-
4
- included do
5
- before do
6
- header["Access-Control-Allow-Origin"] = "*"
7
- header["Access-Control-Request-Method"] = "*"
8
- end
9
-
10
- helpers do
11
- def current_token
12
- doorkeeper_access_token
13
- end
14
-
15
- def current_user
16
- resource_owner
17
- end
18
-
19
- def current_scopes
20
- current_token.scopes
21
- end
22
-
23
- def current_team
24
- current_user.current_team
25
- end
26
-
27
- def current_membership
28
- current_user.memberships.where(team: current_team).first
29
- end
30
-
31
- def current_locale
32
- current_user.locale || current_team.locale || "en"
33
- end
34
- end
35
- end
36
- end
@@ -1,50 +0,0 @@
1
- module Api::V1::ExceptionsHandler
2
- extend ActiveSupport::Concern
3
-
4
- included do
5
- helpers do
6
- def record_not_saved(model)
7
- handle_api_error(ActiveRecord::RecordNotSaved.new(model.errors.full_messages.first), :unprocessable_entity)
8
- end
9
-
10
- def handle_api_error(error, status = nil)
11
- error!("Something rather unexpected has occurred", 500) unless error
12
-
13
- error_class = error.class.name
14
- status_code = convert_status_symbol_to_integer(status) if status # allows an explicit status to be defined
15
-
16
- message = error.message # works fine for most errors
17
- message = "OAuth error: #{error}" if /WineBouncer::Errors/.match?(error_class)
18
- message = "Route error: #{error}" if /CanCan::AccessDenied/.match?(error_class)
19
-
20
- if /OAuthUnauthorizedError/.match?(error_class)
21
- error!(message, status_code || 401) # unauthorized
22
- elsif /OAuthForbiddenError/.match?(error_class) || /CanCan::AccessDenied/.match?(error_class)
23
- error!(message, status_code || 403) # forbidden
24
- elsif /RecordNotFound/.match?(error_class) || /unable to find/i.match?(message)
25
- error!(message, status_code || 404) # not found
26
- elsif /Grape::Exceptions::ValidationErrors/.match?(error_class)
27
- error!(message, status_code || 406) # not acceptable
28
- else
29
- Rails.logger.error message unless Rails.env.test?
30
-
31
- options = {error: message}
32
- options[:trace] = error.backtrace[0, 10] unless Rails.env.production?
33
-
34
- status_code = status_code || error.try(:status) || 500 # internal server error
35
-
36
- Rack::Response.new(options.to_json, status_code, {
37
- "Content-Type" => "application/json",
38
- "Access-Control-Allow-Origin" => "*",
39
- "Access-Control-Request-Method" => "*"
40
- }).finish
41
- end
42
- end
43
-
44
- def convert_status_symbol_to_integer(status)
45
- # defaults to an internal server error if the status code can't be found
46
- Rack::Utils::SYMBOL_TO_STATUS_CODE[status] || 500
47
- end
48
- end
49
- end
50
- end
@@ -1,42 +0,0 @@
1
- module Api::V1::LoadsAndAuthorizesApiResource
2
- extend ActiveSupport::Concern
3
-
4
- included do
5
- helpers do
6
- def load_and_authorize_api_resource(api_resource_class)
7
- raise unless api_resource_class.present?
8
-
9
- instance_variable_name = "@#{api_resource_class.name.demodulize.underscore}"
10
- instance_variable_collection_name = instance_variable_name.pluralize
11
-
12
- options = route.settings[:api_resource_options] || {permission: :read, skip_authorize: false}
13
-
14
- permission = options[:permission]
15
- skip_authorize = options[:skip_authorize]
16
-
17
- api_resource_params = declared(params, include_missing: false)
18
- api_resource_param_id = api_resource_params[:id]
19
- api_resource_params_other_ids = api_resource_params.select { |param, value|
20
- /_id$/i.match?(param)
21
- }
22
-
23
- all_accessible_api_resources = api_resource_class.accessible_by(current_ability, permission)
24
-
25
- if api_resource_param_id.present? # :read, :update, :delete
26
- instance_variable_set(instance_variable_name, all_accessible_api_resources.find(api_resource_param_id))
27
- elsif permission.eql? :create
28
- instance_variable_set(instance_variable_name, api_resource_class.new(api_resource_params))
29
- elsif permission.eql? :read
30
- all_accessible_api_resources = all_accessible_api_resources.where(api_resource_params_other_ids) if api_resource_params_other_ids.present?
31
- instance_variable_set(instance_variable_collection_name, all_accessible_api_resources)
32
- skip_authorize = true # can't use CanCan to authorize collections
33
- end
34
-
35
- eval "authorize! :#{permission}, #{instance_variable_name}" unless skip_authorize
36
- rescue ActiveRecord::RecordNotFound
37
- # the default RecordNotFound message includes the raw SQL... which feels bad
38
- handle_api_error(ActiveRecord::RecordNotFound.new("The id #{api_resource_param_id} could not be found."))
39
- end
40
- end
41
- end
42
- end
@@ -1,9 +0,0 @@
1
- class Api::V1::MeEndpoint < Api::V1::Root
2
- resource :me, desc: Api.title(:actions) do
3
- desc Api.title(:show), &Api.show_desc
4
- oauth2
5
- get do
6
- render current_user, include: [:teams, :memberships], serializer: "Api::V1::UserSerializer", adapter: :attributes
7
- end
8
- end
9
- end
@@ -1,9 +0,0 @@
1
- class Api::V1::TeamsEndpoint < Api::V1::Root
2
- PARAMS = proc do
3
- # 🚅 super scaffolding will insert new fields above this line.
4
- # 🚅 super scaffolding will insert new arrays above this line.
5
- # 🚅 super scaffolding will insert processing for new fields above this line.
6
- end
7
-
8
- include Api::V1::Teams::EndpointBase
9
- end
@@ -1,4 +0,0 @@
1
- require "api"
2
-
3
- module Api::V1
4
- end
@@ -1,96 +0,0 @@
1
- # We have to do this because there are a couple of different Grape contexts where you're trying to define something
2
- # for a model's API representation, but the execution context doesn't have access to the endpoint object you're
3
- # defining things for. I understand the design thinking that caused that situation, but unfortunately for us we just
4
- # have to brute force our way around it for now. In doing so, we're implementing something not unlike the magic of the
5
- # `t` helper in Rails views.
6
-
7
- module Api
8
- def self.topic
9
- path = caller.find { |path| (path.include?("controllers/api") || path.include?("app/controllers/concerns/api")) && !path.include?("app/controllers/api.rb") && !path.include?("app/controllers/api/v1/root.rb") && !path.include?("app/controllers/api/base.rb") }
10
- if path.include?("controllers/api")
11
- path.split(/\/app\/controllers\/api\/v\d+\//).last.split("_endpoint.").first
12
- elsif path.include?("app/controllers/concerns/api")
13
- path.split(/\/app\/controllers\/concerns\/api\/v\d+\//).last.split("/endpoint_base.").first
14
- end
15
- end
16
-
17
- def self.serializer
18
- # TODO This could be smart enough to figure out if `V1` is accurate, as well.
19
- "Api::V1::#{Api.topic.classify}Serializer"
20
- end
21
-
22
- def self.status(code)
23
- I18n.t("api.statuses.#{code}")
24
- end
25
-
26
- def self.title(path)
27
- I18n.t("#{topic}.api.#{path}")
28
- end
29
-
30
- def self.heading(path)
31
- I18n.t("#{topic}.api.fields.#{path}.heading")
32
- end
33
-
34
- def self.show_desc
35
- proc do
36
- success code: 200, model: Api.serializer, message: Api.status(200)
37
- failure [
38
- {code: 401, message: Api.status(401)}, # unauthorized
39
- {code: 403, message: Api.status(403)}, # forbidden
40
- {code: 404, message: Api.status(404)}, # not found
41
- {code: 429, message: Api.status(429)} # too many requests
42
- ]
43
- produces ["application/vnd.api+json"]
44
- end
45
- end
46
-
47
- def self.index_desc
48
- proc do
49
- success code: 200, model: Api.serializer, message: Api.status(200)
50
- failure [
51
- {code: 401, message: Api.status(401)}, # unauthorized
52
- {code: 429, message: Api.status(429)} # too many requests
53
- ]
54
- produces ["application/vnd.api+json"]
55
- is_array true
56
- end
57
- end
58
-
59
- def self.create_desc
60
- proc do
61
- success code: 201, model: Api.serializer, message: Api.status(201)
62
- failure [
63
- {code: 400, message: Api.status(400)}, # bad request
64
- {code: 401, message: Api.status(401)}, # unauthorized
65
- {code: 403, message: Api.status(403)}, # forbidden
66
- {code: 406, message: Api.status(406)}, # not acceptable
67
- {code: 422, message: Api.status(422)}, # unprocessable entity
68
- {code: 429, message: Api.status(429)} # too many requests
69
- ]
70
- consumes ["application/json", "multipart/form-data"]
71
- produces ["application/vnd.api+json"]
72
- end
73
- end
74
-
75
- def self.update_desc
76
- proc do
77
- success code: 200, model: Api.serializer, message: Api.status(200)
78
- failure [
79
- {code: 400, message: Api.status(400)}, # bad request
80
- {code: 401, message: Api.status(401)}, # unauthorized
81
- {code: 403, message: Api.status(403)}, # forbidden
82
- {code: 404, message: Api.status(404)}, # not found
83
- {code: 406, message: Api.status(406)}, # not acceptable
84
- {code: 422, message: Api.status(422)}, # unprocessable entity
85
- {code: 429, message: Api.status(429)} # too many requests
86
- ]
87
- consumes ["application/json", "multipart/form-data"]
88
- produces ["application/vnd.api+json"]
89
- end
90
- end
91
-
92
- def self.destroy_desc
93
- # TODO We don't have anything for this, but we want to make sure it's easy to roll out updates going forward.
94
- proc {}
95
- end
96
- end
@@ -1,37 +0,0 @@
1
- module Api::V1::Base
2
- extend ActiveSupport::Concern
3
-
4
- included do
5
- include Api::V1::Defaults
6
- include Api::V1::LoadsAndAuthorizesApiResource
7
- include Api::V1::ExceptionsHandler
8
-
9
- version "v1"
10
-
11
- use ::WineBouncer::OAuth2, message: "Doorkeeper OAuth2 Authentication"
12
-
13
- rescue_from :all do |error|
14
- handle_api_error(error)
15
- end
16
-
17
- BulletTrain::Api.endpoints.each do |endpoint_class|
18
- mount endpoint_class.constantize
19
- end
20
-
21
- after_validation do
22
- # Ensure responses never get cached.
23
- header "Cache-Control", "no-store"
24
- end
25
- end
26
-
27
- class_methods do
28
- # TODO I actually don't know of any way to make this work. This was supposed to be run after all other endpoints
29
- # are registered, but I don't know of a way to know when we're done running `initializer` blocks from the engines
30
- # a user may have included.
31
- def handle_not_found
32
- route :any, "*path" do
33
- raise StandardError, "Unable to find API endpoint"
34
- end
35
- end
36
- end
37
- end
@@ -1,87 +0,0 @@
1
- module Api::V1::Teams::EndpointBase
2
- extend ActiveSupport::Concern
3
-
4
- included do
5
- helpers do
6
- params :id do
7
- requires :id, type: Integer, allow_blank: false, desc: Api.heading(:id)
8
- end
9
-
10
- params :team do
11
- optional :name, type: String, allow_blank: false, desc: Api.heading(:name)
12
- optional :locale, type: String, desc: Api.heading(:locale)
13
-
14
- # TODO I don't like this, but I can't figure out a better way to accomplish the same thing. I'm open to any
15
- # suggestions on this. I don't know why `@api.class` returns `Class` but `@api.to_s` returns e.g.
16
- # `Api::V1::TeamsEndpoint`, but since we can get the latter, we'll use that to fetch whatever proc is defined
17
- # in ADDITIONAL_PARAMS.
18
- if defined?(@api.to_s.constantize::PARAMS)
19
- instance_eval(&@api.to_s.constantize::PARAMS)
20
- end
21
- end
22
- end
23
-
24
- resource :teams, desc: Api.title(:actions) do
25
- after_validation do
26
- load_and_authorize_api_resource Team
27
- end
28
-
29
- desc Api.title(:index), &Api.index_desc
30
- oauth2
31
- paginate per_page: 100
32
- get "/" do
33
- @paginated_teams = paginate @teams
34
- render @paginated_teams, serializer: Api.serializer, adapter: :attributes
35
- end
36
-
37
- desc Api.title(:show), &Api.show_desc
38
- params do
39
- use :id
40
- end
41
- oauth2
42
- route_param :id do
43
- get do
44
- render @team, serializer: Api.serializer
45
- end
46
- end
47
-
48
- desc Api.title(:create), &Api.create_desc
49
- params do
50
- use :team
51
- end
52
- route_setting :api_resource_options, permission: :create
53
- oauth2 "write"
54
- post "/" do
55
- if @team.save
56
- # sets the team creator as the default admin
57
- @team.memberships.create(user: current_user, roles: [Role.admin])
58
-
59
- current_user.current_team = @team
60
- current_user.former_user = false
61
- current_user.save
62
-
63
- render @team, serializer: Api.serializer
64
- else
65
- record_not_saved @team
66
- end
67
- end
68
-
69
- desc Api.title(:update), &Api.update_desc
70
- params do
71
- use :id
72
- use :team
73
- end
74
- route_setting :api_resource_options, permission: :update
75
- oauth2 "write"
76
- route_param :id do
77
- put do
78
- if @team.update(declared(params, include_missing: false))
79
- render @team, serializer: Api.serializer
80
- else
81
- record_not_saved @team
82
- end
83
- end
84
- end
85
- end
86
- end
87
- end
@@ -1,8 +0,0 @@
1
- class Api::V1::InvitationSerializer < Api::V1::ApplicationSerializer
2
- include Api::V1::Invitations::SerializerBase
3
-
4
- # The `:id` entries are redundant, but for the moment they help us generate valid code.
5
- attributes :id,
6
- # 🚅 super scaffolding will insert new fields above this line.
7
- :id
8
- end
@@ -1,8 +0,0 @@
1
- class Api::V1::MembershipSerializer < Api::V1::ApplicationSerializer
2
- include Api::V1::Memberships::SerializerBase
3
-
4
- # The `:id` entries are redundant, but for the moment they help us generate valid code.
5
- attributes :id,
6
- # 🚅 super scaffolding will insert new fields above this line.
7
- :id
8
- end
@@ -1,8 +0,0 @@
1
- class Api::V1::TeamSerializer < Api::V1::ApplicationSerializer
2
- include Api::V1::Teams::SerializerBase
3
-
4
- # The `:id` entries are redundant, but for the moment they help us generate valid code.
5
- attributes :id,
6
- # 🚅 super scaffolding will insert new fields above this line.
7
- :id
8
- end
@@ -1,8 +0,0 @@
1
- class Api::V1::UserSerializer < Api::V1::ApplicationSerializer
2
- include Api::V1::Users::SerializerBase
3
-
4
- # The `:id` entries are redundant, but for the moment they help us generate valid code.
5
- attributes :id,
6
- # 🚅 super scaffolding will insert new fields above this line.
7
- :id
8
- end
@@ -1,17 +0,0 @@
1
- module Api::V1::Invitations::SerializerBase
2
- extend ActiveSupport::Concern
3
-
4
- included do
5
- set_type "invitation"
6
-
7
- attributes :id,
8
- :team_id,
9
- :email,
10
- :from_membership_id,
11
- :created_at,
12
- :updated_at
13
-
14
- belongs_to :from_membership, serializer: Api::V1::MembershipSerializer
15
- has_one :membership, serializer: Api::V1::MembershipSerializer
16
- end
17
- end
@@ -1,24 +0,0 @@
1
- module Api::V1::Memberships::SerializerBase
2
- extend ActiveSupport::Concern
3
-
4
- included do
5
- set_type "membership"
6
-
7
- attributes :id,
8
- :team_id,
9
- :user_id,
10
- :invitation_id,
11
- :user_first_name,
12
- :user_last_name,
13
- :user_profile_photo_id,
14
- :user_email,
15
- :added_by_id,
16
- :created_at,
17
- :updated_at
18
-
19
- belongs_to :user, serializer: Api::V1::UserSerializer
20
- belongs_to :team, serializer: Api::V1::TeamSerializer
21
- belongs_to :invitation, serializer: Api::V1::InvitationSerializer
22
- belongs_to :added_by, serializer: Api::V1::MembershipSerializer
23
- end
24
- end
@@ -1,16 +0,0 @@
1
- module Api::V1::Teams::SerializerBase
2
- extend ActiveSupport::Concern
3
-
4
- included do
5
- set_type "team"
6
-
7
- attributes :id,
8
- :name,
9
- :time_zone,
10
- :locale,
11
- :created_at,
12
- :updated_at
13
-
14
- has_many :scaffolding_absolutely_abstract_creative_concepts, serializer: Api::V1::Scaffolding::AbsolutelyAbstract::CreativeConceptSerializer
15
- end
16
- end
@@ -1,22 +0,0 @@
1
- module Api::V1::Users::SerializerBase
2
- extend ActiveSupport::Concern
3
-
4
- included do
5
- set_type "user"
6
-
7
- attributes :id,
8
- :email,
9
- :first_name,
10
- :last_name,
11
- :time_zone,
12
- :profile_photo_id,
13
- :former_user,
14
- :locale,
15
- :platform_agent_of_id,
16
- :created_at,
17
- :updated_at
18
-
19
- has_many :teams, serializer: Api::V1::TeamSerializer
20
- has_many :memberships, serializer: Api::V1::MembershipSerializer
21
- end
22
- end