api_maker 0.0.1 → 0.0.2

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.
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
@@ -1,24 +1,38 @@
1
1
  class ApiMaker::CollectionCommandService < ApiMaker::CommandService
2
- def execute
3
- authorize!
2
+ def perform
3
+ ApiMaker::Configuration.profile(-> { "CollectionCommand: #{namespace}::#{command_name}" }) do
4
+ if authorized?
5
+ constant.execute_in_thread!(
6
+ ability: ability,
7
+ api_maker_args: api_maker_args,
8
+ collection: nil,
9
+ commands: commands,
10
+ command_response: command_response,
11
+ controller: controller
12
+ )
13
+ else
14
+ fail_with_no_access
15
+ end
4
16
 
5
- constant.execute_in_thread!(
6
- ability: ability,
7
- args: args,
8
- collection: nil,
9
- commands: commands,
10
- command_response: command_response,
11
- controller: controller
12
- )
13
-
14
- ServicePattern::Response.new(success: true)
17
+ succeed!
18
+ end
15
19
  end
16
20
 
17
- def authorize!
18
- raise CanCan::AccessDenied, "No access to '#{@command_name}' on '#{model_class.name}'" unless @ability.can?(@command_name.to_sym, model_class)
21
+ def authorized?
22
+ ability.can?(command_name.to_sym, model_class)
19
23
  end
20
24
 
21
25
  def constant
22
- @constant ||= "Commands::#{namespace}::#{@command_name.camelize}".constantize
26
+ @constant ||= "Commands::#{namespace}::#{command_name.camelize}".constantize
27
+ end
28
+
29
+ def fail_with_no_access
30
+ commands.each_key do |command_id|
31
+ command_response.error_for_command(
32
+ command_id,
33
+ success: false,
34
+ errors: ["No access to '#{command_name}' on '#{model_class.name}'"]
35
+ )
36
+ end
23
37
  end
24
38
  end
@@ -0,0 +1,124 @@
1
+ class ApiMaker::CollectionLoader < ApiMaker::ApplicationService
2
+ attr_reader :ability, :api_maker_args, :collection, :locals, :params
3
+
4
+ def initialize(ability:, api_maker_args:, collection:, locals: nil, params: {})
5
+ @ability = ability
6
+ @api_maker_args = api_maker_args
7
+ @collection = collection
8
+ @locals = locals || api_maker_args&.dig(:locals) || {}
9
+ @params = params
10
+ end
11
+
12
+ def perform
13
+ set_query
14
+
15
+ if params[:count]
16
+ count = @query.count
17
+ count = count.length if count.is_a?(Hash)
18
+
19
+ succeed!(count: count)
20
+ else
21
+ collection = collection_from_query(@query)
22
+ response = collection.as_json
23
+ include_pagination_data(response, @query)
24
+
25
+ succeed!(response)
26
+ end
27
+ end
28
+
29
+ def filter_custom_accessible_by(collection)
30
+ return collection if params[:accessible_by].blank?
31
+
32
+ collection.accessible_by(ability, params[:accessible_by].to_sym)
33
+ end
34
+
35
+ def collection_from_query(collection)
36
+ ApiMaker::CollectionSerializer.new(
37
+ ability: ability,
38
+ api_maker_args: api_maker_args,
39
+ collection: collection,
40
+ locals: locals,
41
+ query_params: params
42
+ ).result
43
+ end
44
+
45
+ def distinct_query
46
+ @query = @query.distinct if params[:distinct]
47
+ end
48
+
49
+ def group_query
50
+ return if params[:group_by].blank?
51
+
52
+ params[:group_by].each do |group_by|
53
+ if group_by.is_a?(Array)
54
+ raise "Expected table and column but array length was wrong: #{group_by.length}" unless group_by.length == 2
55
+
56
+ resource_class = group_by[0]
57
+ column_name = group_by[1]
58
+ model_class = resource_class.model_class
59
+ raise "Not a valid column name: #{column_name}" unless model_class.column_names.include?(column_name)
60
+
61
+ arel_column = model_class.arel_table[column_name]
62
+ else
63
+ arel_column = collection.klass.arel_table[group_by]
64
+ raise "Not a valid column name: #{group_by}" unless collection.klass.column_names.include?(group_by)
65
+ end
66
+
67
+ @query = @query.group(arel_column)
68
+ end
69
+ end
70
+
71
+ def limit_query
72
+ @query = @query.limit(params[:limit]) if params[:limit].present?
73
+ end
74
+
75
+ def include_pagination_data(response, collection)
76
+ return if params[:page].blank?
77
+
78
+ countable_collection = collection.except(:distinct).except(:group).except(:select).except(:order)
79
+
80
+ Rails.logger.debug { "API maker: CollectionLoader total pages for #{model_class.name}" }
81
+ total_pages = countable_collection.total_pages
82
+
83
+ Rails.logger.debug { "API maker: CollectionLoader total count for #{model_class.name}" }
84
+ total_count = countable_collection.try(:total_count) || countable_collection.total_entries
85
+
86
+ response[:meta] = {
87
+ currentPage: collection.current_page,
88
+ perPage: collection.try(:per_page) || collection.limit_value,
89
+ totalCount: total_count,
90
+ totalPages: total_pages
91
+ }
92
+ end
93
+
94
+ def manage_through_relationship
95
+ return if params[:through].blank?
96
+
97
+ through_model_class = params[:through][:model].safe_constantize
98
+ through_model = through_model_class.accessible_by(ability).find_by(through_model_class.primary_key => params[:through][:id])
99
+
100
+ return if through_model.nil?
101
+
102
+ association = ActiveRecord::Associations::Association.new(through_model, through_model_class.reflections.fetch(params[:through][:reflection]))
103
+
104
+ query_through = association.scope
105
+ query_through = query_through.accessible_by(ability)
106
+
107
+ filter_custom_accessible_by(query_through)
108
+ end
109
+
110
+ def model_class
111
+ @model_class ||= collection.klass
112
+ end
113
+
114
+ def set_query
115
+ @query = manage_through_relationship || collection
116
+ group_query
117
+ distinct_query
118
+ @query = @query.ransack(params[:q]).result
119
+ limit_query
120
+ @query = @query.page(params[:page]) if params[:page].present?
121
+ @query = @query.per_page(params[:per]) if params[:per].present?
122
+ @query = filter_custom_accessible_by(@query)
123
+ end
124
+ end
@@ -0,0 +1,3 @@
1
+ class ApiMaker::CommandFailedError < RuntimeError
2
+ attr_accessor :api_maker_args, :api_maker_block
3
+ end
@@ -47,21 +47,32 @@ class ApiMaker::CommandResponse
47
47
 
48
48
  private
49
49
 
50
- def spawn_thread
50
+ def spawn_thread(&blk)
51
+ parent_thread = Thread.current
52
+
51
53
  @threads << Thread.new do
54
+ child_thread = Thread.current
55
+
52
56
  Rails.application.executor.wrap do
53
- I18n.with_locale(locale) do
54
- yield
57
+ ApiMaker::Configuration.current.on_thread_callbacks&.each do |on_thread_callback|
58
+ on_thread_callback.call(parent_thread: parent_thread, child_thread: child_thread)
55
59
  end
60
+
61
+ I18n.with_locale(locale, &blk)
56
62
  end
57
63
  rescue => e # rubocop:disable Style/RescueStandardError
58
64
  puts e.inspect
59
- puts e.backtrace
65
+ puts Rails.backtrace_cleaner.clean(e.backtrace)
60
66
 
61
67
  Rails.logger.error e.message
62
- Rails.logger.error e.backtrace.join("\n")
68
+ Rails.logger.error Rails.backtrace_cleaner.clean(e.backtrace).join("\n")
63
69
 
64
- ApiMaker::Configuration.current.report_error(controller: controller, error: e)
70
+ ApiMaker::Configuration.current.report_error(
71
+ command: nil,
72
+ controller: controller,
73
+ error: e,
74
+ response: nil
75
+ )
65
76
  end
66
77
  end
67
78
  end
@@ -1,9 +1,9 @@
1
1
  class ApiMaker::CommandService < ApiMaker::ApplicationService
2
- attr_reader :ability, :args, :commands, :command_name, :command_response, :controller, :resource_name
2
+ attr_reader :ability, :api_maker_args, :commands, :command_name, :command_response, :controller, :resource_name
3
3
 
4
- def initialize(ability:, args:, commands:, command_name:, command_response:, controller:, resource_name:)
4
+ def initialize(ability:, api_maker_args:, commands:, command_name:, command_response:, controller:, resource_name:)
5
5
  @ability = ability
6
- @args = args
6
+ @api_maker_args = api_maker_args
7
7
  @command_name = command_name
8
8
  @command_response = command_response
9
9
  @commands = commands
@@ -1,38 +1,27 @@
1
1
  class ApiMaker::CreateCommand < ApiMaker::BaseCommand
2
- attr_reader :command, :model, :params, :serializer
2
+ attr_reader :model, :serializer
3
3
 
4
4
  def execute!
5
- each_command do |command|
6
- @command = command
5
+ ApiMaker::Configuration.profile(-> { "CreateCommand: #{model_class.name}" }) do
7
6
  @model = collection.klass.new
8
- @params = command.args || {}
9
7
  @serializer = serialized_resource(model)
10
- @model.assign_attributes(sanitize_parameters)
8
+ sanitized_parameters = sanitize_parameters
9
+ @model.assign_attributes(sanitized_parameters)
11
10
 
12
- if !current_ability.can?(:create, @model)
13
- failure_response(["No access to create that resource"])
14
- elsif @model.save
11
+ if !current_ability.can?(:create, model)
12
+ failure_response(errors: ["No access to create that resource"])
13
+ elsif model.save
15
14
  success_response
16
15
  else
17
- failure_response(model.errors.full_messages)
16
+ failure_save_response(model: model, params: sanitized_parameters)
18
17
  end
19
18
  end
20
-
21
- ServicePattern::Response.new(success: true)
22
19
  end
23
20
 
24
21
  def api_maker_resource_class
25
22
  @api_maker_resource_class ||= "Resources::#{collection.klass.name}Resource".constantize
26
23
  end
27
24
 
28
- def failure_response(errors)
29
- command.fail(
30
- model: serializer.result,
31
- success: false,
32
- errors: errors
33
- )
34
- end
35
-
36
25
  def resource_instance_class_name
37
26
  @resource_instance_class_name ||= self.class.name.split("::").last.gsub(/Controller$/, "").singularize
38
27
  end
@@ -46,16 +35,12 @@ class ApiMaker::CreateCommand < ApiMaker::BaseCommand
46
35
  end
47
36
 
48
37
  def sanitize_parameters
49
- serializer.resource_instance.permitted_params(ApiMaker::PermittedParamsArgument.new(command: command, model: model))
50
- end
51
-
52
- def serialized_resource(model)
53
- ApiMaker::Serializer.new(ability: current_ability, args: api_maker_args, model: model)
38
+ serializer.resource_instance.permitted_params(ApiMaker::PermittedParamsArgument.new(command: self, model: model))
54
39
  end
55
40
 
56
41
  def success_response
57
- command.result(
58
- model: serializer.result,
42
+ succeed!(
43
+ model: serialized_model(model),
59
44
  success: true
60
45
  )
61
46
  end
@@ -1,15 +1,15 @@
1
1
  class ApiMaker::CreateCommandService < ApiMaker::CommandService
2
- def execute
2
+ def perform
3
3
  ApiMaker::CreateCommand.execute_in_thread!(
4
4
  ability: ability,
5
- args: args,
5
+ api_maker_args: api_maker_args,
6
6
  collection: collection,
7
7
  commands: commands,
8
8
  command_response: command_response,
9
9
  controller: controller
10
10
  )
11
11
 
12
- ServicePattern::Response.new(success: true)
12
+ succeed!
13
13
  end
14
14
 
15
15
  def collection
@@ -0,0 +1,9 @@
1
+ class ApiMaker::DatabaseType
2
+ def self.postgres?
3
+ adapter_name == :postgresql
4
+ end
5
+
6
+ def self.adapter_name
7
+ @adapter_name ||= ApplicationRecord.connection.adapter_name.downcase.to_sym
8
+ end
9
+ end
@@ -0,0 +1,26 @@
1
+ class ApiMaker::DeepMergeParams < ApiMaker::ApplicationService
2
+ attr_reader :hash1, :hash2
3
+
4
+ def initialize(hash1, hash2)
5
+ @hash1 = hash1
6
+ @hash2 = hash2
7
+ end
8
+
9
+ def perform
10
+ merged_hash = hash1.deep_merge(hash2) do |_key, this_val, other_val|
11
+ if this_val.is_a?(Array) && other_val.is_a?(Array) && this_val.length == other_val.length
12
+ this_val.zip(other_val).collect do |a, b|
13
+ if a.is_a?(Hash) && b.is_a?(Hash)
14
+ a.deep_merge(b)
15
+ else
16
+ other_val
17
+ end
18
+ end
19
+ else
20
+ other_val
21
+ end
22
+ end
23
+
24
+ succeed! merged_hash
25
+ end
26
+ end
@@ -0,0 +1,35 @@
1
+ class ApiMaker::Deserializer < ApiMaker::ApplicationService
2
+ attr_reader :arg
3
+
4
+ def initialize(arg:)
5
+ @arg = arg
6
+ end
7
+
8
+ def perform
9
+ succeed! deserialize
10
+ end
11
+
12
+ def deserialize
13
+ if arg.is_a?(Array)
14
+ arg.map { |value| ApiMaker::Deserializer.execute!(arg: value) }
15
+ elsif arg.is_a?(Hash) || arg.is_a?(ActionController::Parameters)
16
+ if arg["api_maker_type"] == "resource"
17
+ "Resources::#{arg.fetch("name")}Resource".safe_constantize
18
+ elsif arg["api_maker_type"] == "datetime"
19
+ Time.zone.parse(arg.fetch("value"))
20
+ else
21
+ new_hash = arg.class.new
22
+ arg.each do |key, value|
23
+ deserialized_key = ApiMaker::Deserializer.execute!(arg: key)
24
+ deserialized_value = ApiMaker::Deserializer.execute!(arg: value)
25
+
26
+ new_hash[deserialized_key] = deserialized_value
27
+ end
28
+
29
+ new_hash
30
+ end
31
+ else
32
+ arg
33
+ end
34
+ end
35
+ end
@@ -1,38 +1,32 @@
1
1
  class ApiMaker::DestroyCommand < ApiMaker::BaseCommand
2
- attr_reader :command, :model, :params, :serializer
2
+ attr_reader :serializer
3
3
 
4
4
  def execute!
5
- each_command do |command|
6
- @command = command
7
- @model = command.model
8
- @params = command.args || {}
9
- @serializer = serialized_resource(model)
5
+ @serializer = serialized_resource(model)
10
6
 
11
- if command.model.destroy
12
- success_response
13
- else
14
- failure_response
15
- end
7
+ if command.model.destroy
8
+ success_response
9
+ else
10
+ failure_response
16
11
  end
12
+ end
17
13
 
18
- ServicePattern::Response.new(success: true)
14
+ def errors_for_model
15
+ ActiveRecordBetterDependentErrorMessages::DestroyValidator.(model: model) if model.errors.full_messages.empty?
16
+ model.errors.full_messages
19
17
  end
20
18
 
21
19
  def failure_response
22
- command.fail(
23
- model: serializer.result,
20
+ fail!(
21
+ model: serialized_model(model),
24
22
  success: false,
25
- errors: model.errors.full_messages
23
+ errors: errors_for_model
26
24
  )
27
25
  end
28
26
 
29
- def serialized_resource(model)
30
- ApiMaker::Serializer.new(ability: current_ability, args: api_maker_args, model: model)
31
- end
32
-
33
27
  def success_response
34
- command.result(
35
- model: serializer.result,
28
+ succeed!(
29
+ model: serialized_model(model),
36
30
  success: true
37
31
  )
38
32
  end
@@ -1,15 +1,15 @@
1
1
  class ApiMaker::DestroyCommandService < ApiMaker::CommandService
2
- def execute
2
+ def perform
3
3
  ApiMaker::DestroyCommand.execute_in_thread!(
4
4
  ability: ability,
5
- args: args,
5
+ api_maker_args: api_maker_args,
6
6
  collection: collection,
7
7
  commands: commands,
8
8
  command_response: command_response,
9
9
  controller: controller
10
10
  )
11
11
 
12
- ServicePattern::Response.new(success: true)
12
+ succeed!
13
13
  end
14
14
 
15
15
  def collection
@@ -1,10 +1,10 @@
1
1
  class ApiMaker::GenerateReactNativeApiService < ApiMaker::ApplicationService
2
- def execute
2
+ def perform
3
3
  check_if_root_folder_defined
4
4
  create_root_folder
5
5
  create_model_files
6
6
 
7
- ServicePattern::Response.new(success: true)
7
+ succeed!
8
8
  end
9
9
 
10
10
  def root_folder
@@ -21,20 +21,6 @@ class ApiMaker::GenerateReactNativeApiService < ApiMaker::ApplicationService
21
21
  Dir.mkdir(root_folder) unless Dir.exist?(root_folder)
22
22
  end
23
23
 
24
- def create_model_files
25
- model_generator_service.models.each do |model|
26
- next if model_generator_service.ignore_model?(model)
27
-
28
- model_content_response = ApiMaker::ModelContentGeneratorService.execute!(model: model)
29
-
30
- if model_content_response.success?
31
- File.open(model_file(model), "w") { |fp| fp.write(model_content_response.result) }
32
- else
33
- puts model_content_response.errors.join(". ")
34
- end
35
- end
36
- end
37
-
38
24
  def model_generator_service
39
25
  @model_generator_service ||= ApiMaker::ModelsGeneratorService.new
40
26
  end
@@ -50,9 +36,7 @@ class ApiMaker::GenerateReactNativeApiService < ApiMaker::ApplicationService
50
36
 
51
37
  def copy_base_model
52
38
  files = %w[
53
- api.js base-model.js cable-connection-pool.js cable-subscription.js cable-subscription-pool.js
54
- collection.js commands-pool.js devise.js event-listener.jsx error-logger.js form-data-to-object.js logger.js
55
- model-name.js models-response-reader.js result.js event-connection.jsx
39
+ devise.js event-connection.jsx
56
40
  ]
57
41
  path = File.join(__dir__, "..", "..", "..", "lib", "api_maker", "javascript")
58
42
 
@@ -0,0 +1,17 @@
1
+ require_relative "../../api_maker/api_helpers/api_maker_helpers" # Sometimes this file isn't auto-loaded so it needs to be manually required
2
+
3
+ class ApiMaker::IncludeHelpers < ApiMaker::ApplicationService
4
+ attr_reader :klass
5
+
6
+ def initialize(klass:)
7
+ @klass = klass
8
+ end
9
+
10
+ def perform
11
+ ::ApiHelpers.constants(false).each do |constant|
12
+ klass.include ApiHelpers.const_get(constant)
13
+ end
14
+
15
+ succeed!
16
+ end
17
+ end
@@ -1,96 +1,16 @@
1
1
  class ApiMaker::IndexCommand < ApiMaker::BaseCommand
2
- attr_reader :params
3
-
4
2
  def execute!
5
- ApiMaker::Configuration.profile("IndexCommand execute") do
6
- each_command do |command|
7
- @params = command.args || {}
8
-
9
- set_collection
10
-
11
- if params[:count]
12
- count = @query.count
13
- count = count.length if count.is_a?(Hash)
14
-
15
- command.result(count: count)
16
- else
17
- collection = collection_from_query(@query.fix)
18
- response = collection.as_json
19
- include_pagination_data(response, @query)
20
-
21
- command.result(response)
22
- end
23
- end
24
- end
25
-
26
- ServicePattern::Response.new(success: true)
27
- end
28
-
29
- def filter_custom_accessible_by(collection)
30
- return collection if params[:accessible_by].blank?
31
-
32
- collection.accessible_by(current_ability, params[:accessible_by].to_sym)
33
- end
3
+ ApiMaker::Configuration.profile(-> { "IndexCommand: #{model_class.name}" }) do
4
+ Rails.logger.debug { "API maker: IndexCommand execute: #{model_class.name} with Ransack: #{command.args&.dig(:q)&.permit!&.to_h}" }
34
5
 
35
- def collection_from_query(collection)
36
- ApiMaker::Configuration.profile("IndexCommand collection_from_query") do
37
- select = parse_select(params[:select]&.permit!&.to_hash) if params[:select]
38
-
39
- ApiMaker::CollectionSerializer.new(
6
+ result = ApiMaker::CollectionLoader.execute!(
40
7
  ability: current_ability,
41
- args: api_maker_args,
8
+ api_maker_args: api_maker_args,
42
9
  collection: collection,
43
- include_param: params[:include],
44
- select: select
45
- ).result
10
+ locals: locals,
11
+ params: command.args || {}
12
+ )
13
+ succeed!(result)
46
14
  end
47
15
  end
48
-
49
- def include_pagination_data(response, collection)
50
- return if params[:page].blank?
51
-
52
- response[:meta] = {
53
- currentPage: collection.current_page,
54
- totalCount: collection.try(:total_count) || collection.try(:total_entries),
55
- totalPages: collection.total_pages
56
- }
57
- end
58
-
59
- def manage_through_relationship
60
- return if params[:through].blank?
61
-
62
- model_class = params[:through][:model].safe_constantize
63
- through_model = model_class.accessible_by(current_ability).find(params[:through][:id])
64
- association = ActiveRecord::Associations::Association.new(through_model, model_class.reflections.fetch(params[:through][:reflection]))
65
-
66
- query_through = association.scope
67
- query_through = query_through.accessible_by(current_ability)
68
- query_through = filter_custom_accessible_by(query_through)
69
- query_through
70
- end
71
-
72
- # This converts the list of attributes to a hash that contains the data needed for the serializer (so the serializer doesn't have to do it for each model)
73
- def parse_select(select)
74
- new_select = {}
75
-
76
- select.each do |model_collection_name, attributes|
77
- model_class = model_collection_name.underscore.singularize.camelize
78
- resource = "Resources::#{model_class}Resource".safe_constantize
79
- raise "Resource not found for: #{model_collection_name}" unless resource
80
-
81
- new_attributes = resource._attributes.select { |key| attributes.include?(key.to_s) }
82
- new_select[resource.model_class] = new_attributes
83
- end
84
-
85
- new_select
86
- end
87
-
88
- def set_collection
89
- @query = manage_through_relationship || collection
90
- @query = @query.distinct if params[:distinct]
91
- @query = @query.ransack(params[:q]).result
92
- @query = @query.limit(params[:limit]) if params[:limit].present?
93
- @query = @query.page(params[:page]) if params[:page].present?
94
- @query = filter_custom_accessible_by(@query)
95
- end
96
16
  end
@@ -1,22 +1,22 @@
1
1
  class ApiMaker::IndexCommandService < ApiMaker::CommandService
2
- def execute
2
+ def perform
3
3
  ApiMaker::IndexCommand.execute_in_thread!(
4
4
  ability: ability,
5
- args: args,
5
+ api_maker_args: api_maker_args,
6
6
  collection: collection,
7
7
  commands: commands,
8
8
  command_response: command_response,
9
9
  controller: controller
10
10
  )
11
11
 
12
- ServicePattern::Response.new(success: true)
12
+ succeed!
13
13
  end
14
14
 
15
15
  def collection
16
- @collection ||= model_class.accessible_by(@ability)
16
+ @collection ||= model_class.accessible_by(ability)
17
17
  end
18
18
 
19
19
  def ids
20
- @ids ||= @commands.values.map { |command| command.fetch("primary_key") }
20
+ @ids ||= commands.values.map { |command| command.fetch("primary_key") }
21
21
  end
22
22
  end
@@ -3,7 +3,7 @@ class ApiMaker::JsMethodNamerService < ApiMaker::ApplicationService
3
3
  @name = name
4
4
  end
5
5
 
6
- def execute
6
+ def perform
7
7
  camelized = @name.to_s.camelize
8
8
  new_name = "#{camelized[0..0].downcase}#{camelized[1..camelized.length]}"
9
9
  ServicePattern::Response.new(result: new_name)