Class: ApiMaker::BaseCommand

Inherits:
Object
  • Object
show all
Defined in:
app/services/api_maker/base_command.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ability:, api_maker_args:, collection:, collection_instance:, command:, commands:, command_response:, controller:) ⇒ BaseCommand

Returns a new instance of BaseCommand.



15
16
17
18
19
20
21
22
23
24
# File 'app/services/api_maker/base_command.rb', line 15

def initialize(ability:, api_maker_args:, collection:, collection_instance:, command:, commands:, command_response:, controller:)
  @api_maker_args = api_maker_args
  @current_ability = ability
  @collection = collection
  @collection_instance = collection_instance
  @command = command
  @commands = commands
  @command_response = command_response
  @controller = controller
end

Instance Attribute Details

#api_maker_argsObject (readonly)

Returns the value of attribute api_maker_args.



4
5
6
# File 'app/services/api_maker/base_command.rb', line 4

def api_maker_args
  @api_maker_args
end

#collectionObject (readonly)

Returns the value of attribute collection.



4
5
6
# File 'app/services/api_maker/base_command.rb', line 4

def collection
  @collection
end

#collection_instanceObject (readonly)

Returns the value of attribute collection_instance.



4
5
6
# File 'app/services/api_maker/base_command.rb', line 4

def collection_instance
  @collection_instance
end

#commandObject (readonly)

Returns the value of attribute command.



4
5
6
# File 'app/services/api_maker/base_command.rb', line 4

def command
  @command
end

#command_responseObject (readonly)

Returns the value of attribute command_response.



4
5
6
# File 'app/services/api_maker/base_command.rb', line 4

def command_response
  @command_response
end

#commandsObject (readonly)

Returns the value of attribute commands.



4
5
6
# File 'app/services/api_maker/base_command.rb', line 4

def commands
  @commands
end

#controllerObject (readonly)

Returns the value of attribute controller.



4
5
6
# File 'app/services/api_maker/base_command.rb', line 4

def controller
  @controller
end

#current_abilityObject (readonly)

Returns the value of attribute current_ability.



4
5
6
# File 'app/services/api_maker/base_command.rb', line 4

def current_ability
  @current_ability
end

Class Method Details

.command_error_message(error) ⇒ Object



32
33
34
35
36
37
38
# File 'app/services/api_maker/base_command.rb', line 32

def self.command_error_message(error)
  if Rails.application.config.consider_all_requests_local
    "#{error.class.name}: #{error.message}"
  else
    "Internal server error"
  end
end

.each_command(collection:, command_response:, commands:, controller:, threadded:, &blk) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'app/services/api_maker/base_command.rb', line 82

def self.each_command(collection:, command_response:, commands:, controller:, threadded:, &blk)
  commands.each do |command_id, command_data|
    if threadded
      command_response.with_thread do
        run_command(
          command_id: command_id,
          command_data: command_data,
          command_response: command_response,
          collection: collection,
          controller: controller,
          &blk
        )
      end
    else
      run_command(
        command_id: command_id,
        command_data: command_data,
        command_response: command_response,
        collection: collection,
        controller: controller,
        &blk
      )
    end
  end
end

.execute_in_thread!(ability:, api_maker_args:, collection:, commands:, command_response:, controller:) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'app/services/api_maker/base_command.rb', line 40

def self.execute_in_thread!(ability:, api_maker_args:, collection:, commands:, command_response:, controller:)
  command_response.with_thread do
    if const_defined?(:CollectionInstance)
      collection_instance = const_get(:CollectionInstance).new(
        ability: ability,
        api_maker_args: api_maker_args,
        collection: collection,
        commands: commands,
        command_response: command_response,
        controller: controller
      )

      collection = collection_instance.custom_collection if collection_instance.respond_to?(:custom_collection)
      collection_instance.collection = collection

      threadded = collection_instance.try(:threadded?)
    end

    if threadded
      # Goldiloader doesn't work with threads (loads all relationships for each thread)
      collection = collection.auto_include(false) if ApiMaker::BaseCommand.goldiloader?

      # Load relationship before commands so each command doesn't query on its own
      collection.load
    end

    each_command(collection: collection, command_response: command_response, commands: commands, controller: controller, threadded: threadded) do |command|
      command_instance = new(
        ability: ability,
        api_maker_args: api_maker_args,
        collection: collection,
        collection_instance: collection_instance,
        command: command,
        commands: command,
        command_response: command_response,
        controller: controller
      )
      command_instance.execute_with_response
    end
  end
end

.goldiloader?Boolean

Returns true if the gem “goldiloader” is present in the app

Returns:

  • (Boolean)


10
11
12
13
# File 'app/services/api_maker/base_command.rb', line 10

def self.goldiloader?
  @goldiloader = Gem::Specification.find_all_by_name("goldiloader").any? if @goldiloader.nil?
  @goldiloader
end

.run_command(collection:, command_id:, command_data:, command_response:, controller:) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'app/services/api_maker/base_command.rb', line 108

def self.run_command(collection:, command_id:, command_data:, command_response:, controller:)
  command = ApiMaker::IndividualCommand.new(
    args: ApiMaker::Deserializer.execute!(arg: command_data[:args]),
    collection: collection,
    command: self,
    id: command_id,
    primary_key: command_data[:primary_key],
    response: command_response
  )

  begin
    yield command
  rescue => e # rubocop:disable Style/RescueStandardError
    error_response = {
      success: false,
      errors: [{message: command_error_message(e), type: :runtime_error}]
    }

    Rails.logger.error e.message
    Rails.logger.error Rails.backtrace_cleaner.clean(e.backtrace).join("\n")

    ApiMaker::Configuration.current.report_error(
      command: command,
      controller: controller,
      error: e,
      response: error_response
    )

    command.error(error_response)
  end
end

Instance Method Details

#execute_service_or_fail(service_class, *args, &blk) ⇒ Object



140
141
142
143
144
145
146
147
148
# File 'app/services/api_maker/base_command.rb', line 140

def execute_service_or_fail(service_class, *args, &blk)
  response = service_class.execute(*args, &blk)

  if response.success?
    succeed!(success: true)
  else
    fail_command_from_service_error_response(response)
  end
end

#execute_with_responseObject



26
27
28
29
30
# File 'app/services/api_maker/base_command.rb', line 26

def execute_with_response
  execute!
rescue ApiMaker::CommandFailedError => e
  command.fail(*e.api_maker_args, &e.api_maker_block)
end

#fail!(*args, &blk) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'app/services/api_maker/base_command.rb', line 204

def fail!(*args, &blk)
  if args.is_a?(Hash) && args.key?(:errors)
    error_messages = args.fetch(:errors).map do |error|
      if error.is_a?(Hash) && error.key?(:message)
        error.fetch(:message)
      else
        error
      end
    end
  else
    error_messages = ["Command failed"]
  end

  error = ApiMaker::CommandFailedError.new(error_messages)
  error.api_maker_args = args
  error.api_maker_block = blk

  raise error
end

#fail_command_from_service_error_response(response) ⇒ Object



150
151
152
# File 'app/services/api_maker/base_command.rb', line 150

def fail_command_from_service_error_response(response)
  fail!(errors: serialize_service_errors(response.errors))
end

#failure_response(errors:) ⇒ Object



154
155
156
157
158
159
160
# File 'app/services/api_maker/base_command.rb', line 154

def failure_response(errors:)
  fail!(
    model: serialized_model(model),
    success: false,
    errors: errors
  )
end

#failure_save_response(additional_attributes: [], model:, params:, simple_model_errors: false) ⇒ Object



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'app/services/api_maker/base_command.rb', line 162

def failure_save_response(additional_attributes: [], model:, params:, simple_model_errors: false)
  raise "Cannot receive additional attributes unless simple model errors" if !additional_attributes.empty? && !simple_model_errors

  error_messages = if simple_model_errors
    ApiMaker::SimpleModelErrors.execute!(additional_attributes: additional_attributes, model: model)
  else
    model.errors.full_messages
  end

  fail!(
    error_type: :validation_error,
    errors: error_messages.map { |error_message| {message: error_message, type: :validation_error} },
    model: serialized_model(model),
    success: false,
    validation_errors: ApiMaker::ValidationErrorsGeneratorService.execute!(model: model, params: params)
  )
end

#inspectObject



228
229
230
# File 'app/services/api_maker/base_command.rb', line 228

def inspect
  "#<#{self.class.name}:#{__id__}>"
end

#model_classObject



180
181
182
# File 'app/services/api_maker/base_command.rb', line 180

def model_class
  @model_class ||= collection.klass
end

#save_models_or_fail(*models, simple_model_errors: false) ⇒ Object



184
185
186
187
188
189
190
191
192
193
# File 'app/services/api_maker/base_command.rb', line 184

def save_models_or_fail(*models, simple_model_errors: false)
  response = ApiMaker::Models::Save.execute(models: models, simple_model_errors: simple_model_errors)

  if response.success?
    succeed!(success: true)
    true
  else
    fail!(errors: response.error_messages.map { |error| {message: error, type: :validation_error} })
  end
end

#serialize_service_errors(errors) ⇒ Object



195
196
197
198
199
200
201
202
# File 'app/services/api_maker/base_command.rb', line 195

def serialize_service_errors(errors)
  errors.map do |error|
    {
      message: error.message,
      type: error.type
    }
  end
end

#succeed!(*args, &blk) ⇒ Object



224
225
226
# File 'app/services/api_maker/base_command.rb', line 224

def succeed!(*args, &blk)
  command.result(*args, &blk)
end