Module: Maestrano::Connector::Rails::Concerns::Entity

Extended by:
ActiveSupport::Concern
Included in:
Entity
Defined in:
app/models/maestrano/connector/rails/concerns/entity.rb

Defined Under Namespace

Modules: ClassMethods

Instance Method Summary collapse

Instance Method Details

#after_sync(connec_client, external_client, last_synchronization, organization, opts) ⇒ Object



419
420
421
# File 'app/models/maestrano/connector/rails/concerns/entity.rb', line 419

def after_sync(connec_client, external_client, last_synchronization, organization, opts)
  # Does nothing by default
end

#batch_op(method, mapped_external_entity, id, connec_entity_name, organization) ⇒ Object



274
275
276
277
278
279
280
281
282
283
# File 'app/models/maestrano/connector/rails/concerns/entity.rb', line 274

def batch_op(method, mapped_external_entity, id, connec_entity_name, organization)
  Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Sending #{method.upcase} #{connec_entity_name}: #{mapped_external_entity} to Connec! (Preparing batch request)")
  {
    method: method,
    url: "/api/v2/#{organization.uid}/#{connec_entity_name}" + (id.nil? ? '' : "/#{id}"),
    params: {
      "#{connec_entity_name}".to_sym => mapped_external_entity
    }
  }
end

#before_sync(connec_client, external_client, last_synchronization, organization, opts) ⇒ Object


After and before sync



415
416
417
# File 'app/models/maestrano/connector/rails/concerns/entity.rb', line 415

def before_sync(connec_client, external_client, last_synchronization, organization, opts)
  # Does nothing by default
end

#consolidate_and_map_data(connec_entities, external_entities, organization, opts = {}) ⇒ Object


General methods

  • Discards entities that do not need to be pushed because they have not been updated since their last push

  • Discards entities from one of the two source in case of conflict

  • Maps not discarded entities and associates them with their idmap, or create one if there isn’t any

  • Return a hash [], external_entities: []



358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
# File 'app/models/maestrano/connector/rails/concerns/entity.rb', line 358

def consolidate_and_map_data(connec_entities, external_entities, organization, opts={})
  return consolidate_and_map_singleton(connec_entities, external_entities, organization, opts) if self.class.singleton?

  mapped_external_entities = external_entities.map{|entity|
    idmap = self.class.find_idmap({external_id: self.class.id_from_external_entity_hash(entity), organization_id: organization.id})
    # No idmap: creating one, nothing else to do
    if idmap
      idmap.update(name: self.class.object_name_from_external_entity_hash(entity))
    else
      next {entity: map_to_connec(entity, organization), idmap: self.class.create_idmap_from_external_entity(entity, organization)}
    end

    # Not pushing entity to Connec!
    next nil unless idmap.to_connec

    # Entity has not been modified since its last push to connec!
    next nil if self.class.not_modified_since_last_push_to_connec?(idmap, entity, self, organization)

    # Check for conflict with entities from connec!
    self.class.solve_conflict(entity, self, connec_entities, self.class.connec_entity_name, idmap, organization, opts)
  }
  mapped_external_entities.compact!

  mapped_connec_entities = connec_entities.map{|entity|
    map_to_external_with_idmap(entity, organization)
  }
  mapped_connec_entities.compact!

  return {connec_entities: mapped_connec_entities, external_entities: mapped_external_entities}
end

#consolidate_and_map_singleton(connec_entities, external_entities, organization, opts = {}) ⇒ Object



389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
# File 'app/models/maestrano/connector/rails/concerns/entity.rb', line 389

def consolidate_and_map_singleton(connec_entities, external_entities, organization, opts={})
  return {connec_entities: [], external_entities: []} if external_entities.empty? && connec_entities.empty?

  idmap = self.class.find_or_create_idmap({organization_id: organization.id})

  if external_entities.empty?
    keep_external = false
  elsif connec_entities.empty?
    keep_external = true
  elsif !opts[:connec_preemption].nil?
    keep_external = !opts[:connec_preemption]
  else
    keep_external = self.class.is_external_more_recent?(connec_entities.first, external_entities.first, self)
  end
  if keep_external
    idmap.update(external_id: self.class.id_from_external_entity_hash(external_entities.first), name: self.class.object_name_from_external_entity_hash(external_entities.first))
    return {connec_entities: [], external_entities: [{entity: map_to_connec(external_entities.first, organization), idmap: idmap}]}
  else
    idmap.update(connec_id: connec_entities.first['id'], name: self.class.object_name_from_connec_entity_hash(connec_entities.first))
    return {connec_entities: [{entity: map_to_external(connec_entities.first, organization), idmap: idmap}], external_entities: []}
  end
end

#create_external_entity(client, mapped_connec_entity, external_entity_name, organization) ⇒ Object



342
343
344
345
# File 'app/models/maestrano/connector/rails/concerns/entity.rb', line 342

def create_external_entity(client, mapped_connec_entity, external_entity_name, organization)
  Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Sending create #{external_entity_name}: #{mapped_connec_entity} to #{Maestrano::Connector::Rails::External.external_name}")
  raise "Not implemented"
end

#get_connec_entities(client, last_synchronization, organization, opts = {}) ⇒ Object


Connec! methods

Supported options:

  • full_sync

  • $filter (see Connec! documentation)

  • $orderby (see Connec! documentation)



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'app/models/maestrano/connector/rails/concerns/entity.rb', line 174

def get_connec_entities(client, last_synchronization, organization, opts={})
  return [] unless self.class.can_read_connec?

  Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Fetching Connec! #{self.class.connec_entity_name}")

  entities = []
  query_params = {}
  query_params[:$orderby] = opts[:$orderby] if opts[:$orderby]

  # Fetch first page
  if last_synchronization.blank? || opts[:full_sync]
    Maestrano::Connector::Rails::ConnectorLogger.log('debug', organization, "entity=#{self.class.connec_entity_name}, fetching all data")
    query_params[:$filter] = opts[:$filter] if opts[:$filter]
  else
    Maestrano::Connector::Rails::ConnectorLogger.log('debug', organization, "entity=#{self.class.connec_entity_name}, fetching data since #{last_synchronization.updated_at.iso8601}")
    filter = "updated_at gt '#{last_synchronization.updated_at.iso8601}'"
    filter += " and #{opts[:$filter]}" if opts[:$filter]
    query_params[:$filter] = filter
  end
  response = client.get("/#{self.class.normalized_connec_entity_name}?#{query_params.to_query}")
  raise "No data received from Connec! when trying to fetch #{self.class.normalized_connec_entity_name}" unless response && !response.body.blank?

  response_hash = JSON.parse(response.body)
  Maestrano::Connector::Rails::ConnectorLogger.log('debug', organization, "received first page entity=#{self.class.connec_entity_name}, response=#{response.body}")
  if response_hash["#{self.class.normalized_connec_entity_name}"]
    entities << response_hash["#{self.class.normalized_connec_entity_name}"]
  else
    raise "Received unrecognized Connec! data when trying to fetch #{self.class.normalized_connec_entity_name}"
  end

  # Fetch subsequent pages
  while response_hash['pagination'] && response_hash['pagination']['next']
    # ugly way to convert https://api-connec/api/v2/group_id/organizations?next_page_params to /organizations?next_page_params
    next_page = response_hash['pagination']['next'].gsub(/^(.*)\/#{self.class.normalized_connec_entity_name}/, self.class.normalized_connec_entity_name)
    response = client.get(next_page)

    raise "No data received from Connec! when trying to fetch subsequent page of #{self.class.connec_entity_name.pluralize}" unless response && !response.body.blank?
    Maestrano::Connector::Rails::ConnectorLogger.log('debug', organization, "received next page entity=#{self.class.connec_entity_name}, response=#{response.body}")

    response_hash = JSON.parse(response.body)
    if response_hash["#{self.class.normalized_connec_entity_name}"]
      entities << response_hash["#{self.class.normalized_connec_entity_name}"]
    else
      raise "Received unrecognized Connec! data when trying to fetch subsequent page of #{self.class.connec_entity_name.pluralize}"
    end
  end

  entities = entities.flatten
  Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Received data: Source=Connec!, Entity=#{self.class.connec_entity_name}, Data=#{entities}")
  entities
end

#get_external_entities(client, last_synchronization, organization, opts = {}) ⇒ Object


External methods



304
305
306
307
308
# File 'app/models/maestrano/connector/rails/concerns/entity.rb', line 304

def get_external_entities(client, last_synchronization, organization, opts={})
  return [] unless self.class.can_read_external?
  Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Fetching #{Maestrano::Connector::Rails::External.external_name} #{self.class.external_entity_name.pluralize}")
  raise "Not implemented"
end

#map_external_entity_with_idmap(external_entity, connec_entity_name, idmap, organization) ⇒ Object



479
480
481
# File 'app/models/maestrano/connector/rails/concerns/entity.rb', line 479

def map_external_entity_with_idmap(external_entity, connec_entity_name, idmap, organization)
  {entity: map_to_connec(external_entity, organization), idmap: idmap}
end

#map_to_connec(entity, organization) ⇒ Object

Map an external entity to Connec! format



158
159
160
161
162
163
164
165
# File 'app/models/maestrano/connector/rails/concerns/entity.rb', line 158

def map_to_connec(entity, organization)
  ref_hash = {}
  self.class.references.each do |ref|
    ref_hash.merge! ref[:connec_field].split('/').reverse.inject(self.class.id_from_ref(entity, ref, true, organization)) { |a, n| { n.to_sym => a } }
  end

  self.class.mapper_class.denormalize(entity).merge(ref_hash)
end

#map_to_external(entity, organization) ⇒ Object


Mapper methods

Map a Connec! entity to the external format



148
149
150
151
152
153
154
155
# File 'app/models/maestrano/connector/rails/concerns/entity.rb', line 148

def map_to_external(entity, organization)
  ref_hash = {}
  self.class.references.each do |ref|
    ref_hash.merge! ref[:external_field].split('/').reverse.inject(self.class.id_from_ref(entity, ref, false, organization)) { |a, n| { n.to_sym => a } }
  end

  self.class.mapper_class.normalize(entity).merge(ref_hash)
end

#map_to_external_with_idmap(entity, organization) ⇒ Object



285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'app/models/maestrano/connector/rails/concerns/entity.rb', line 285

def map_to_external_with_idmap(entity, organization)
  idmap = self.class.find_idmap({connec_id: entity['id'], organization_id: organization.id})

  if idmap
    idmap.update(name: self.class.object_name_from_connec_entity_hash(entity))
    if (!idmap.to_external) || (idmap.last_push_to_external && idmap.last_push_to_external > entity['updated_at'])
      Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Discard Connec! #{self.class.connec_entity_name} : #{entity}")
      nil
    else
      {entity: map_to_external(entity, organization), idmap: idmap}
    end
  else
    {entity: map_to_external(entity, organization), idmap: self.class.create_idmap_from_connec_entity(entity, organization)}
  end
end

#push_entities_to_connec(connec_client, mapped_external_entities_with_idmaps, organization) ⇒ Object



226
227
228
# File 'app/models/maestrano/connector/rails/concerns/entity.rb', line 226

def push_entities_to_connec(connec_client, mapped_external_entities_with_idmaps, organization)
  push_entities_to_connec_to(connec_client, mapped_external_entities_with_idmaps, self.class.connec_entity_name, organization)
end

#push_entities_to_connec_to(connec_client, mapped_external_entities_with_idmaps, connec_entity_name, organization) ⇒ Object



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'app/models/maestrano/connector/rails/concerns/entity.rb', line 230

def push_entities_to_connec_to(connec_client, mapped_external_entities_with_idmaps, connec_entity_name, organization)
  return unless self.class.can_write_connec?

  Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Sending #{Maestrano::Connector::Rails::External.external_name} #{self.class.external_entity_name.pluralize} to Connec! #{connec_entity_name.pluralize}")
  
  request_per_call = 100
  start = 0
  while start < mapped_external_entities_with_idmaps.size
    # Prepare batch request
    batch_entities = mapped_external_entities_with_idmaps.slice(start, request_per_call)
    batch_request = {sequential: true, ops: []}
    batch_entities.each do |mapped_external_entity_with_idmap|
      external_entity = mapped_external_entity_with_idmap[:entity]
      idmap = mapped_external_entity_with_idmap[:idmap]
      if idmap.connec_id.blank?
        batch_request[:ops] << batch_op('post', external_entity, nil, self.class.normalize_connec_entity_name(connec_entity_name), organization)
      else
        next unless self.class.can_update_connec?
        batch_request[:ops] << batch_op('put', external_entity, idmap.connec_id, self.class.normalize_connec_entity_name(connec_entity_name), organization)
      end
    end

    # Batch call
    Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Sending batch request to Connec! for #{self.class.normalize_connec_entity_name(connec_entity_name)}. Batch_request_size: #{batch_request[:ops].size}. Call_number: #{(start/request_per_call) + 1}")
    response = connec_client.batch(batch_request)
    Maestrano::Connector::Rails::ConnectorLogger.log('debug', organization, "Received batch response from Connec! for #{self.class.normalize_connec_entity_name(connec_entity_name)}: #{response}")
    raise "No data received from Connec! when trying to send batch request for #{self.class.connec_entity_name.pluralize}" unless response && !response.body.blank?
    response = JSON.parse(response.body)

    # Parse barch response
    response['results'].each_with_index do |result, index|
      if result['status'] == 200
        batch_entities[index][:idmap].update_attributes(last_push_to_connec: Time.now, message: nil)
      elsif result['status'] == 201
        batch_entities[index][:idmap].update_attributes(connec_id: result['body'][self.class.normalize_connec_entity_name(connec_entity_name)]['id'], last_push_to_connec: Time.now, message: nil)
      else
        Maestrano::Connector::Rails::ConnectorLogger.log('error', organization, "Error while pushing to Connec!: #{result['body']}")
        batch_entities[index][:idmap].update_attributes(message: result['body'])
      end
    end
    start += request_per_call
  end
end

#push_entities_to_external(external_client, mapped_connec_entities_with_idmaps, organization) ⇒ Object



310
311
312
# File 'app/models/maestrano/connector/rails/concerns/entity.rb', line 310

def push_entities_to_external(external_client, mapped_connec_entities_with_idmaps, organization)
  push_entities_to_external_to(external_client, mapped_connec_entities_with_idmaps, self.class.external_entity_name, organization)
end

#push_entities_to_external_to(external_client, mapped_connec_entities_with_idmaps, external_entity_name, organization) ⇒ Object



314
315
316
317
318
319
320
# File 'app/models/maestrano/connector/rails/concerns/entity.rb', line 314

def push_entities_to_external_to(external_client, mapped_connec_entities_with_idmaps, external_entity_name, organization)
  return unless self.class.can_write_external?
  Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Sending Connec! #{self.class.connec_entity_name.pluralize} to #{Maestrano::Connector::Rails::External.external_name} #{external_entity_name.pluralize}")
  mapped_connec_entities_with_idmaps.each do |mapped_connec_entity_with_idmap|
    push_entity_to_external(external_client, mapped_connec_entity_with_idmap, external_entity_name, organization)
  end
end

#push_entity_to_external(external_client, mapped_connec_entity_with_idmap, external_entity_name, organization) ⇒ Object



322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'app/models/maestrano/connector/rails/concerns/entity.rb', line 322

def push_entity_to_external(external_client, mapped_connec_entity_with_idmap, external_entity_name, organization)
  idmap = mapped_connec_entity_with_idmap[:idmap]
  connec_entity = mapped_connec_entity_with_idmap[:entity]

  begin
    if idmap.external_id.blank?
      external_id = create_external_entity(external_client, connec_entity, external_entity_name, organization)
      idmap.update_attributes(external_id: external_id, last_push_to_external: Time.now, message: nil)
    else
      return unless self.class.can_update_external?
      update_external_entity(external_client, connec_entity, idmap.external_id, external_entity_name, organization)
      idmap.update_attributes(last_push_to_external: Time.now, message: nil)
    end
  rescue => e
    # Store External error
    Maestrano::Connector::Rails::ConnectorLogger.log('error', organization, "Error while pushing to #{Maestrano::Connector::Rails::External.external_name}: #{e}")
    idmap.update_attributes(message: e.message)
  end
end

#update_external_entity(client, mapped_connec_entity, external_id, external_entity_name, organization) ⇒ Object



347
348
349
350
# File 'app/models/maestrano/connector/rails/concerns/entity.rb', line 347

def update_external_entity(client, mapped_connec_entity, external_id, external_entity_name, organization)
  Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Sending update #{external_entity_name} (id=#{external_id}): #{mapped_connec_entity} to #{Maestrano::Connector::Rails::External.external_name}")
  raise "Not implemented"
end