api_maker 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/app/api_maker/api_helpers/api_maker_helpers.rb +5 -0
  3. data/app/api_maker/services/can_can/load_abilities.rb +30 -0
  4. data/app/api_maker/services/devise/sign_in.rb +64 -0
  5. data/app/api_maker/services/devise/sign_out.rb +9 -0
  6. data/app/api_maker/services/models/find_or_create_by.rb +18 -0
  7. data/app/channels/api_maker/subscriptions_channel.rb +33 -2
  8. data/app/controllers/api_maker/base_controller.rb +7 -3
  9. data/app/controllers/api_maker/commands_controller.rb +26 -4
  10. data/app/controllers/api_maker/session_statuses_controller.rb +1 -1
  11. data/app/services/api_maker/abilities_loader.rb +104 -0
  12. data/app/services/api_maker/application_service.rb +2 -1
  13. data/app/services/api_maker/base_command.rb +248 -0
  14. data/app/services/api_maker/collection_command_service.rb +29 -15
  15. data/app/services/api_maker/collection_loader.rb +124 -0
  16. data/app/services/api_maker/command_failed_error.rb +3 -0
  17. data/app/services/api_maker/command_response.rb +17 -6
  18. data/app/services/api_maker/command_service.rb +3 -3
  19. data/app/services/api_maker/create_command.rb +11 -26
  20. data/app/services/api_maker/create_command_service.rb +3 -3
  21. data/app/services/api_maker/database_type.rb +9 -0
  22. data/app/services/api_maker/deep_merge_params.rb +26 -0
  23. data/app/services/api_maker/deserializer.rb +35 -0
  24. data/app/services/api_maker/destroy_command.rb +15 -21
  25. data/app/services/api_maker/destroy_command_service.rb +3 -3
  26. data/app/services/api_maker/generate_react_native_api_service.rb +3 -19
  27. data/app/services/api_maker/include_helpers.rb +17 -0
  28. data/app/services/api_maker/index_command.rb +8 -88
  29. data/app/services/api_maker/index_command_service.rb +5 -5
  30. data/app/services/api_maker/js_method_namer_service.rb +1 -1
  31. data/app/services/api_maker/locals_from_controller.rb +14 -0
  32. data/app/services/api_maker/member_command_service.rb +15 -13
  33. data/app/services/api_maker/model_classes_java_script_generator_service.rb +37 -0
  34. data/app/services/api_maker/model_content_generator_service.rb +17 -21
  35. data/app/services/api_maker/models/save.rb +29 -0
  36. data/app/services/api_maker/models_finder_service.rb +6 -2
  37. data/app/services/api_maker/models_generator_service.rb +6 -43
  38. data/app/services/api_maker/move_components_to_routes.rb +50 -0
  39. data/app/services/api_maker/primary_id_for_model.rb +6 -0
  40. data/app/services/api_maker/reset_indexed_db_service.rb +36 -0
  41. data/app/services/api_maker/routes_file_reloader.rb +20 -0
  42. data/app/services/api_maker/select_columns_on_collection.rb +78 -0
  43. data/app/services/api_maker/select_parser.rb +32 -0
  44. data/app/services/api_maker/service_command.rb +27 -0
  45. data/app/services/api_maker/service_command_service.rb +14 -0
  46. data/app/services/api_maker/simple_model_errors.rb +52 -0
  47. data/app/services/api_maker/update_command.rb +8 -24
  48. data/app/services/api_maker/update_command_service.rb +3 -3
  49. data/app/services/api_maker/valid_command.rb +4 -13
  50. data/app/services/api_maker/valid_command_service.rb +3 -3
  51. data/app/services/api_maker/validation_errors_generator_service.rb +146 -0
  52. data/app/views/api_maker/_data.html.erb +17 -11
  53. data/config/routes.rb +0 -2
  54. data/lib/api_maker/ability.rb +22 -7
  55. data/lib/api_maker/ability_loader.rb +9 -6
  56. data/lib/api_maker/base_collection_instance.rb +15 -0
  57. data/lib/api_maker/base_resource.rb +135 -9
  58. data/lib/api_maker/base_service.rb +14 -0
  59. data/lib/api_maker/collection_serializer.rb +95 -34
  60. data/lib/api_maker/command_spec_helper.rb +41 -11
  61. data/lib/api_maker/configuration.rb +31 -4
  62. data/lib/api_maker/expect_to_able_to_helper.rb +31 -0
  63. data/lib/api_maker/individual_command.rb +24 -9
  64. data/lib/api_maker/javascript/model-template.js.erb +39 -25
  65. data/lib/api_maker/javascript/models.js.erb +6 -0
  66. data/lib/api_maker/loader.rb +1 -1
  67. data/lib/api_maker/memory_storage.rb +1 -1
  68. data/lib/api_maker/model_extensions.rb +34 -18
  69. data/lib/api_maker/permitted_params_argument.rb +5 -1
  70. data/lib/api_maker/preloader.rb +71 -32
  71. data/lib/api_maker/preloader_base.rb +108 -0
  72. data/lib/api_maker/preloader_belongs_to.rb +34 -33
  73. data/lib/api_maker/preloader_has_many.rb +45 -39
  74. data/lib/api_maker/preloader_has_one.rb +30 -47
  75. data/lib/api_maker/railtie.rb +3 -11
  76. data/lib/api_maker/relationship_preloader.rb +42 -0
  77. data/lib/api_maker/resource_routing.rb +18 -4
  78. data/lib/api_maker/result_parser.rb +34 -20
  79. data/lib/api_maker/serializer.rb +53 -22
  80. data/lib/api_maker/spec_helper/browser_logs.rb +14 -0
  81. data/lib/api_maker/spec_helper/execute_collection_command.rb +46 -0
  82. data/lib/api_maker/spec_helper/execute_member_command.rb +52 -0
  83. data/lib/api_maker/spec_helper/expect_no_browser_errors.rb +18 -0
  84. data/lib/api_maker/spec_helper/wait_for_expect.rb +20 -0
  85. data/lib/api_maker/spec_helper/wait_for_flash_message.rb +21 -0
  86. data/lib/api_maker/spec_helper.rb +112 -48
  87. data/lib/api_maker/version.rb +1 -1
  88. data/lib/api_maker.rb +7 -3
  89. metadata +108 -89
  90. data/README.md +0 -476
  91. data/app/controllers/api_maker/devise_controller.rb +0 -60
  92. data/lib/api_maker/base_command.rb +0 -81
  93. data/lib/api_maker/javascript/api.js +0 -92
  94. data/lib/api_maker/javascript/base-model.js +0 -543
  95. data/lib/api_maker/javascript/bootstrap/attribute-row.jsx +0 -16
  96. data/lib/api_maker/javascript/bootstrap/attribute-rows.jsx +0 -47
  97. data/lib/api_maker/javascript/bootstrap/card.jsx +0 -79
  98. data/lib/api_maker/javascript/bootstrap/checkbox.jsx +0 -127
  99. data/lib/api_maker/javascript/bootstrap/checkboxes.jsx +0 -105
  100. data/lib/api_maker/javascript/bootstrap/live-table.jsx +0 -168
  101. data/lib/api_maker/javascript/bootstrap/money-input.jsx +0 -136
  102. data/lib/api_maker/javascript/bootstrap/radio-buttons.jsx +0 -80
  103. data/lib/api_maker/javascript/bootstrap/select.jsx +0 -168
  104. data/lib/api_maker/javascript/bootstrap/string-input.jsx +0 -203
  105. data/lib/api_maker/javascript/cable-connection-pool.js +0 -169
  106. data/lib/api_maker/javascript/cable-subscription-pool.js +0 -111
  107. data/lib/api_maker/javascript/cable-subscription.js +0 -33
  108. data/lib/api_maker/javascript/collection.js +0 -186
  109. data/lib/api_maker/javascript/commands-pool.js +0 -123
  110. data/lib/api_maker/javascript/custom-error.js +0 -14
  111. data/lib/api_maker/javascript/deserializer.js +0 -35
  112. data/lib/api_maker/javascript/devise.js.erb +0 -113
  113. data/lib/api_maker/javascript/error-logger.js +0 -119
  114. data/lib/api_maker/javascript/event-connection.jsx +0 -24
  115. data/lib/api_maker/javascript/event-created.jsx +0 -26
  116. data/lib/api_maker/javascript/event-destroyed.jsx +0 -26
  117. data/lib/api_maker/javascript/event-emitter-listener.jsx +0 -32
  118. data/lib/api_maker/javascript/event-listener.jsx +0 -41
  119. data/lib/api_maker/javascript/event-updated.jsx +0 -26
  120. data/lib/api_maker/javascript/form-data-to-object.js +0 -70
  121. data/lib/api_maker/javascript/included.js +0 -39
  122. data/lib/api_maker/javascript/key-value-store.js +0 -47
  123. data/lib/api_maker/javascript/logger.js +0 -23
  124. data/lib/api_maker/javascript/model-name.js +0 -21
  125. data/lib/api_maker/javascript/models-response-reader.js +0 -43
  126. data/lib/api_maker/javascript/paginate.jsx +0 -128
  127. data/lib/api_maker/javascript/params.js +0 -68
  128. data/lib/api_maker/javascript/resource-route.jsx +0 -75
  129. data/lib/api_maker/javascript/resource-routes.jsx +0 -36
  130. data/lib/api_maker/javascript/result.js +0 -25
  131. data/lib/api_maker/javascript/session-status-updater.js +0 -113
  132. data/lib/api_maker/javascript/sort-link.jsx +0 -88
  133. data/lib/api_maker/javascript/updated-attribute.jsx +0 -60
  134. data/lib/api_maker/preloader_through.rb +0 -101
  135. data/lib/api_maker/relationship_includer.rb +0 -42
data/README.md DELETED
@@ -1,476 +0,0 @@
1
- # ApiMaker
2
-
3
- Generates Rails API endpoints and JavaScript API files for Webpack and more by inspecting your models and serializers.
4
-
5
- ## Installation
6
- Add this line to your application's Gemfile:
7
-
8
- ```ruby
9
- gem "api_maker"
10
- ```
11
-
12
- ApiMaker requires [Webpacker](https://github.com/rails/webpacker), so make sure you have that set up as well. It also uses an extension called [qs](https://www.npmjs.com/package/qs), that you should add to your packages, but that is probally already there by default.
13
-
14
- ApiMaker makes use of [CanCanCan](https://github.com/CanCanCommunity/cancancan) to keep track of what models a given user should have access to. Each resource defines its own abilities under `app/api_maker/resources/user_resource` like this:
15
- ```ruby
16
- class Resources::UserResource < Resources::ApplicationResource
17
- def abilities
18
- can :update, User if current_user&.admin?
19
- can :update, User, id: current_user&.id if current_user.present?
20
- can :read, User
21
- end
22
- end
23
- ```
24
-
25
- Add an `api_maker_args` method to your application controller. This controls what arguments will be passed to the CanCan ability and the serializers:
26
- ```ruby
27
- class ApplicationController
28
- private
29
-
30
- def api_maker_args
31
- @api_maker_args ||= {current_user: current_user}
32
- end
33
- end
34
- ```
35
-
36
- Insert this mount into `config/routes.rb`:
37
- ```ruby
38
- Rails.application.routes.draw do
39
- mount ApiMaker::Engine => "/api_maker"
40
- end
41
- ```
42
-
43
- ApiMaker will only create models, endpoints and serializers for ActiveRecord models that are defined as resources. So be sure to add resources under `app/api_maker/resources` for your models first. You can add some helper methods if you want to use in your resources like `current_user` and `signed_in_as_admin?`.
44
- ```ruby
45
- class Resources::ApplicationResource < ApiMaker::BaseResource
46
- def current_user
47
- args&.dig(:current_user)
48
- end
49
-
50
- def signed_in_as_admin?
51
- current_user&.role == "admin"
52
- end
53
- end
54
- ```
55
-
56
- ```ruby
57
- class Resources::UserResources < Resources::ApplicationResource
58
- attributes :id, :email, :custom_attribute
59
- attributes :calculated_attribute, selected_by_default: false
60
- attributes :secret_attribute, if: :signed_in_as_admin?
61
- collection_commands :count_users
62
- member_commands :calculate_age
63
- relationships :account, :tasks
64
-
65
- def custom_attribute
66
- "Hello world! Current user is: #{args.fetch(:current_user).email}"
67
- end
68
- end
69
- ```
70
-
71
- You should also create an application command here: `app/api_maker/commands/application_command` with content like this:
72
- ```ruby
73
- class Commands::ApplicationCommand < ApiMaker::BaseCommand
74
- end
75
- ```
76
-
77
- Add this to your application model:
78
- ```ruby
79
- class ApplicationRecord < ActiveRecord::Base
80
- include ApiMaker::ModelExtensions
81
- end
82
- ```
83
-
84
- ApiMaker uses that to keep track of what attributes, relationships and commands you want exposed through the API.
85
-
86
- Its now time to generate everything like this:
87
- ```bash
88
- rake api_maker:generate_models
89
- ```
90
-
91
- If you want to be able to create and update models, then you should go into each resource and create a params method to define, which attributes can be written on each model like this:
92
- ```ruby
93
- class Resources::TaskResource < ApiMaker::ModelController
94
- def permitted_params(arg)
95
- arg.params.require(:project).permit(:name)
96
- end
97
- end
98
- ```
99
-
100
- ### I18n
101
-
102
- In order to use the built in text support, you need to add `i18n-js` to your project.
103
-
104
- Start by adding to your Gemfile:
105
- ```ruby
106
- gem "i18n-js"
107
- ```
108
-
109
- Then add `config/i18n-js.yml`:
110
- ```yml
111
- translations:
112
- - file: "app/assets/javascripts/i18n/translations.js"
113
- only: ["*.activerecord.attributes.*", "*.activerecord.models.*", "*.date.*", "*.js.*", "*.number.currency.*", "*.time.*"]
114
- ```
115
-
116
- Then add this to `app/assets/javascript/application.js.erb`:
117
- ```js
118
- //= require i18n
119
- //= require i18n/translations
120
-
121
- var locale = document.querySelector("html").getAttribute("lang")
122
- I18n.locale = locale
123
-
124
- <% if Rails.env.development? || Rails.env.test? %>
125
- I18n.missingTranslation = function(key) {
126
- console.error(`No translation for: ${key}`)
127
- return `translation missing: ${key}`
128
- }
129
- <% end %>
130
- ```
131
-
132
- Add this to the `<html>`-tag:
133
- ```html
134
- <html lang="<%= I18n.locale %>">
135
- ```
136
-
137
- Add this to `config/application.rb` to ease development:
138
- ```ruby
139
- config.middleware.use I18n::JS::Middleware
140
- ```
141
-
142
- ### ActionCable
143
-
144
- Your `connection.rb` should look something like this:
145
- ```rb
146
- class ApplicationCable::Connection < ActionCable::Connection::Base
147
- identified_by :current_user
148
-
149
- def connect
150
- self.current_user = find_verified_user
151
- end
152
-
153
- private
154
-
155
- def find_verified_user
156
- verified_user = User.find_by(id: cookies.signed["user.id"])
157
-
158
- if verified_user && cookies.signed["user.expires_at"] > Time.zone.now
159
- verified_user
160
- else
161
- reject_unauthorized_connection
162
- end
163
- end
164
- end
165
- ```
166
-
167
- Your `channel.rb` should look something like this:
168
- ```rb
169
- class ApplicationCable::Channel < ActionCable::Channel::Base
170
- private # rubocop:disable Layout/IndentationWidth
171
-
172
- def current_ability
173
- @current_ability ||= ApiMakerAbility.for_user(current_user)
174
- end
175
-
176
- def current_user
177
- @current_user ||= env["warden"].user
178
- end
179
- end
180
- ```
181
-
182
- ## Usage
183
-
184
- ### Creating a new model from JavaScript
185
-
186
- ```js
187
- import Task from "api-maker/models/task"
188
-
189
- var task = new Task()
190
- task.assignAttributes({name: "New task"})
191
- task.create().then(status => {
192
- if (status.success) {
193
- console.log(`Task was created with ID: ${task.id()}`)
194
- } else {
195
- console.log("Task wasnt created")
196
- }
197
- })
198
- ```
199
-
200
- ### Finding an existing model
201
-
202
- ```js
203
- Task.find(5).then(task => {
204
- console.log(`Task found: ${task.name()}`)
205
- })
206
- ```
207
-
208
- ### Updating a model
209
-
210
- ```js
211
- task.assignAttributes({name: "New name"})
212
- task.save().then(status => {
213
- if (status.success) {
214
- console.log(`Task was updated and name is now: ${task.name()}`)
215
- } else {
216
- console.log("Task wasnt updated")
217
- }
218
- })
219
- ```
220
-
221
- ```js
222
- task.update({name: "New name"}).then(status => {
223
- if (status.success) {
224
- console.log(`Task was updated and name is now: ${task.name()}`)
225
- } else {
226
- console.log("Task wasnt updated")
227
- }
228
- })
229
- ```
230
-
231
- ### Deleting a model
232
-
233
- ```js
234
- task.destroy().then(status => {
235
- if (status.success) {
236
- console.log("Task was destroyed")
237
- } else {
238
- console.log("Task wasnt destroyed")
239
- }
240
- })
241
- ```
242
-
243
- ### Preloading models
244
-
245
- ```js
246
- Task.ransack().preload("project.customer").toArray().then(tasks => {
247
- for(var task of tasks) {
248
- console.log(`Project of task ${task.id()}: ${task.project().name()}`)
249
- console.log(`Customer of task ${task.id()}: ${task.project().customer().name()}`)
250
- }
251
- })
252
- ```
253
-
254
- ### Query models
255
-
256
- ApiModels uses [Ransack](https://github.com/activerecord-hackery/ransack) to expose a huge amount of options to query data.
257
-
258
- ```js
259
- Task.ransack({name_cont: "something"}).toArray().then(tasks => {
260
- console.log(`Found: ${tasks.length} tasks`)
261
- })
262
- ```
263
-
264
- Distinct:
265
- ```js
266
- var tasks = await Task.ransack({relationships_something_eq: "something"}).distinct().toArray()
267
- ```
268
-
269
- ### Selecting only specific attributes
270
-
271
- ```js
272
- Task.ransack().select({Task: ["id", "name"]}).toArray().then(tasks => this.setState({tasks}))
273
- ```
274
-
275
- ### Sorting models
276
-
277
- ```js
278
- Task.ransack({s: "id desc"})
279
- ```
280
-
281
- ### Attributes
282
-
283
- Each attribute is defined as a method on each model. So if you have an attribute called `name` on the `Task`-model, then it be read by doing this: `task.name()`.
284
-
285
- ### Relationships
286
-
287
- #### Has many
288
-
289
- A `has many` relationship will return a collection the queries the sub models.
290
-
291
- ```js
292
- project.tasks().toArray().then(tasks => {
293
- console.log(`Project ${project.id()} has ${tasks.length} tasks`)
294
-
295
- for(var key in tasks) {
296
- var task = tasks[key]
297
- console.log(`Task ${task.id()} is named: ${task.name()}`)
298
- }
299
- })
300
- ```
301
-
302
- #### Belongs to
303
-
304
- A `belongs to` relationship will return a promise that will get that model:
305
-
306
- ```js
307
- task.project().then(project => {
308
- console.log(`Task ${task.id()} belongs to a project called: ${project.name()}`)
309
- })
310
- ```
311
-
312
- #### Has one
313
-
314
- A `has one` relationship will also return a promise that will get that model like a `belongs to` relationship.
315
-
316
- #### Getting the current user
317
-
318
- First include this in your layout, so JS can know which user is signed in:
319
- ```erb
320
- <body>
321
- <%= render "/api_maker/data" %>
322
- ```
323
-
324
- Then you can do like this in JS:
325
- ```js
326
- import Devise from "api-maker/devise"
327
-
328
- Devise.currentUser().then(user => {
329
- console.log(`The current user has this email: ${user.email()}`)
330
- })
331
- ```
332
-
333
- ## Events from the backend
334
-
335
- ### Custom events
336
-
337
- Add the relevant access to your abilities:
338
-
339
- ```ruby
340
- class ApiMakerAbility < ApplicationAbility
341
- def initialize(args:)
342
- can :event_new_message, User, id: 5
343
- end
344
- end
345
- ```
346
-
347
- ```ruby
348
- user = User.find(5)
349
- user.api_maker_event("new_message", message: "Hello world")
350
- ```
351
-
352
- ```js
353
- User.find(5).then(user => {
354
- user.connect("new_message", args => {
355
- console.log(`New message: ${args.message}`)
356
- })
357
- })
358
- ```
359
-
360
- ### Update models
361
-
362
- Add this to your abilities:
363
- ```ruby
364
- class ApiMakerAbility < ApplicationAbility
365
- def initialize(args:)
366
- can [:create_events, :destroy_events, :update_events], User, id: 5
367
- end
368
- end
369
- ```
370
-
371
- Add this to the model you want to broadcast updates:
372
- ```ruby
373
- class User < ApplicationRecord
374
- api_maker_broadcast_creates
375
- api_maker_broadcast_destroys
376
- api_maker_broadcast_updates
377
- end
378
- ```
379
-
380
- ```js
381
- User.find(5).then(user => {
382
- let subscription = user.connectUpdated(args => {
383
- console.log(`Model was updated: ${args.model.id()}`)
384
- })
385
- })
386
- ```
387
-
388
- Remember to unsubscrube again:
389
- ```js
390
- subscription.unsubscribe()
391
- ```
392
-
393
- You can also use a React component if you use React and dont want to keep track of when to unsubscribe:
394
- ```jsx
395
- import EventUpdated from "api-maker/event-created"
396
- import EventUpdated from "api-maker/event-destroyed"
397
- import EventUpdated from "api-maker/event-updated"
398
- ```
399
-
400
- ```jsx
401
- <EventCreated modelClass={User} onCreated={(args) => this.onUserCreated(args)} />
402
- <EventDestroyed model={user} onDestroyed={(args) => this.onUserDestroyed(args)} />
403
- <EventUpdated model={user} onUpdated={(args) => this.onUserUpdated(args)} />
404
- ```
405
-
406
- ```jsx
407
- onUserCreated(args) {
408
- this.setState({user: args.model})
409
- }
410
-
411
- onUserDestroyed(args) {
412
- this.setState({user: args.model})
413
- }
414
-
415
- onUserUpdated(args) {
416
- this.setState({user: args.model})
417
- }
418
- ```
419
-
420
- You can also use this React component to show a models attribute with automatic updates:
421
-
422
- ```jsx
423
- import UpdatedAttribute from "api-maker/updated-attribute"
424
- ```
425
-
426
- ```jsx
427
- <UpdatedAttribute model={user} attribute="email" />
428
- ```
429
-
430
- You can also use the `EventConnection` React component so you don't need to keep track of your subscription and unsubscribe:
431
- ```jsx
432
- import EventConnection from "api-maker/event-connection"
433
- ```
434
-
435
- ```jsx
436
- <EventConnection model={this.state.user} event="eventName" onCall={(data) => this.onEvent(data)} />
437
- ```
438
-
439
- ## Serializing
440
-
441
- ### Conditional attributes
442
-
443
- This will only include the email for users, if the current user signed in is an admin.
444
-
445
- ```ruby
446
- class Resources::UserResource < Resources::ApplicationResource
447
- attributes :id
448
- attributes :email, if: :signed_in_as_admin?
449
-
450
- private
451
-
452
- def signed_in_as_admin?
453
- args[:current_user]&.admin?
454
- end
455
- end
456
- ```
457
-
458
-
459
- ## Reporting errors
460
-
461
- Add an intializer with something like this:
462
-
463
- ```ruby
464
- ApiMaker::Configuration.configure do |config|
465
- config.on_error do |controller:, error:|
466
- ExceptionNotifier.notify_exception(error, env: controller&.request&.env)
467
- end
468
- end
469
- ```
470
-
471
-
472
- ## Contributing
473
- Contribution directions go here.
474
-
475
- ## License
476
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -1,60 +0,0 @@
1
- class ApiMaker::DeviseController < ApiMaker::BaseController
2
- include Devise::Controllers::Rememberable
3
-
4
- before_action :check_model_exists, only: :do_sign_in
5
- before_action :check_serializer_exists, only: :do_sign_in
6
-
7
- def do_sign_in
8
- if !model.active_for_authentication?
9
- render json: {success: false, errors: [model.inactive_message]}
10
- elsif model.valid_password?(params[:password])
11
- sign_in(model, scope: scope)
12
- remember_me(model) if params.dig(:args, :rememberMe)
13
- render json: {success: true, model_data: serializer.result}
14
- else
15
- render json: {success: false, errors: [invalid_error_message]}
16
- end
17
- end
18
-
19
- def do_sign_out
20
- scope = params.dig(:args, :scope).presence || "user"
21
- current_model = __send__("current_#{scope}")
22
- sign_out current_model
23
- render json: {success: true}
24
- end
25
-
26
- private
27
-
28
- def check_model_exists
29
- error_msg = t("devise.failure.not_found_in_database", authentication_keys: model_class.authentication_keys.join(", "))
30
- render json: {success: false, errors: [error_msg]} unless model
31
- end
32
-
33
- def check_serializer_exists
34
- render json: {success: false, errors: ["Serializer doesn't exist for #{scope}"]} unless resource
35
- end
36
-
37
- def invalid_error_message
38
- t("devise.failure.invalid", authentication_keys: model_class.authentication_keys.join(", "))
39
- end
40
-
41
- def model
42
- @model ||= model_class.find_for_authentication(email: params[:username])
43
- end
44
-
45
- def model_class
46
- @model_class ||= scope.camelize.safe_constantize
47
- end
48
-
49
- def scope
50
- @scope ||= params.dig(:args, :scope).presence || "user"
51
- end
52
-
53
- def resource
54
- @resource ||= ApiMaker::Serializer.resource_for(model.class)
55
- end
56
-
57
- def serializer
58
- @serializer ||= ApiMaker::Serializer.new(ability: current_ability, args: api_maker_args, model: model)
59
- end
60
- end
@@ -1,81 +0,0 @@
1
- class ApiMaker::BaseCommand
2
- attr_reader :api_maker_args, :commands, :command_response, :collection, :controller, :current_ability
3
-
4
- # Returns true if the gem "goldiloader" is present in the app
5
- def self.goldiloader?
6
- @goldiloader = Gem::Specification.find_all_by_name("goldiloader").any? if @goldiloader.nil?
7
- @goldiloader
8
- end
9
-
10
- def initialize(ability:, args:, collection:, commands:, command_response:, controller:)
11
- @api_maker_args = args
12
- @current_ability = ability
13
- @collection = collection
14
- @commands = commands
15
- @command_response = command_response
16
- @controller = controller
17
-
18
- # Make it possible to do custom preloads (useful in threadded mode that doesnt support Goldiloader)
19
- @collection = custom_collection(@collection) if respond_to?(:custom_collection)
20
- end
21
-
22
- def self.execute_in_thread!(**args)
23
- args.fetch(:command_response).with_thread do
24
- new(**args).execute!
25
- end
26
- end
27
-
28
- def each_command(args = {}, &blk)
29
- if args[:threadded]
30
- # Goldiloader doesn't work with threads (loads all relationships for each thread)
31
- @collection = @collection.auto_include(false) if ApiMaker::BaseCommand.goldiloader?
32
-
33
- # Load relationship before commands so each command doesn't query on its own
34
- @collection.load
35
- end
36
-
37
- @commands.each do |command_id, command_data|
38
- if args[:threadded]
39
- command_response.with_thread do
40
- run_command(command_id, command_data, &blk)
41
- end
42
- else
43
- run_command(command_id, command_data, &blk)
44
- end
45
- end
46
- end
47
-
48
- delegate :result_for_command, to: :command_response
49
-
50
- private
51
-
52
- def run_command(command_id, command_data)
53
- command = ApiMaker::IndividualCommand.new(
54
- args: command_data[:args],
55
- collection: @collection,
56
- command: self,
57
- id: command_id,
58
- primary_key: command_data[:primary_key],
59
- response: command_response
60
- )
61
-
62
- begin
63
- yield command
64
- rescue => e # rubocop:disable Style/RescueStandardError
65
- command.error(success: false, errors: [command_error_message(e)])
66
-
67
- Rails.logger.error e.message
68
- Rails.logger.error e.backtrace.join("\n")
69
-
70
- ApiMaker::Configuration.current.report_error(controller: controller, error: e)
71
- end
72
- end
73
-
74
- def command_error_message(error)
75
- if Rails.application.config.consider_all_requests_local
76
- "#{error.class.name}: #{error.message}"
77
- else
78
- "Internal server error"
79
- end
80
- end
81
- end
@@ -1,92 +0,0 @@
1
- import CustomError from "./custom-error"
2
- import qs from "qs"
3
-
4
- export default class {
5
- static get(path, data = null) {
6
- return this.requestLocal({"path": path, "pathParams": data, "method": "GET"})
7
- }
8
-
9
- static delete(path, data = null) {
10
- return this.requestLocal({"path": path, "pathParams": data, "method": "DELETE"})
11
- }
12
-
13
- static patch(path, data = {}) {
14
- return this.requestLocal({"path": path, "data": data, "method": "PATCH"})
15
- }
16
-
17
- static post(path, data = {}) {
18
- return this.requestLocal({"path": path, "data": data, "method": "POST"})
19
- }
20
-
21
- static request(args) {
22
- var path = args.path
23
-
24
- if (args.pathParams) {
25
- var pathParamsString = qs.stringify(args.pathParams, {"arrayFormat": "brackets"})
26
- path += `?${pathParamsString}`
27
- }
28
-
29
- return new Promise((resolve, reject) => {
30
- var xhr = new XMLHttpRequest()
31
- xhr.open(args.method, path, true)
32
-
33
- if (args.headers) {
34
- for(var headerName in args.headers) {
35
- xhr.setRequestHeader(headerName, args.headers[headerName])
36
- }
37
- }
38
-
39
- xhr.onload = () => {
40
- var response = this._parseResponse(xhr)
41
-
42
- if (xhr.status == 200) {
43
- resolve(response)
44
- } else {
45
- reject(new CustomError(`Request failed with code: ${xhr.status}`, {response: response}))
46
- }
47
- }
48
-
49
- xhr.send(args.data)
50
- })
51
- }
52
-
53
- static requestLocal(args) {
54
- if (!args.headers)
55
- args["headers"] = {}
56
-
57
- var token = this._token()
58
- if (token)
59
- args["headers"]["X-CSRF-Token"] = token
60
-
61
- if (args.data) {
62
- args["headers"]["Content-Type"] = "application/json"
63
- args["data"] = JSON.stringify(args.data)
64
- }
65
-
66
- if (args.rawData)
67
- args["data"] = args.rawData
68
-
69
- return this.request(args)
70
- }
71
-
72
- static put(path, data = {}) {
73
- return this.requestLocal({"path": path, "data": data, "method": "PUT"})
74
- }
75
-
76
- static _token() {
77
- var tokenElement = document.querySelector("meta[name='csrf-token']")
78
-
79
- if (tokenElement)
80
- return tokenElement.getAttribute("content")
81
- }
82
-
83
- static _parseResponse(xhr) {
84
- var responseType = xhr.getResponseHeader("content-type")
85
-
86
- if (responseType && responseType.startsWith("application/json")) {
87
- return JSON.parse(xhr.responseText)
88
- } else {
89
- return xhr.responseText
90
- }
91
- }
92
- }