app/models/maestrano/connector/rails/concerns/entity.rb in maestrano-connector-rails-1.2.3 vs app/models/maestrano/connector/rails/concerns/entity.rb in maestrano-connector-rails-1.3.0
- old
+ new
@@ -13,11 +13,11 @@
end
# organization_and_id can be either:
# * {connec_id: 'id', organization_id: 'id'}
# * {external_id: 'id', organization_id: 'id'}
- # Needs to include either connec_entity or external_entity for complex entities
+ # Needs to include either connec_entity or external_entity for sub entities
def find_or_create_idmap(organization_and_id)
Maestrano::Connector::Rails::IdMap.find_or_create_by(names_hash.merge(organization_and_id))
end
def find_idmap(organization_and_id)
@@ -34,15 +34,11 @@
def normalized_connec_entity_name
normalize_connec_entity_name(connec_entity_name)
end
def normalize_connec_entity_name(name)
- if singleton?
- name.parameterize('_')
- else
- name.parameterize('_').pluralize
- end
+ singleton? ? name.parameterize('_') : name.parameterize('_').pluralize
end
# ----------------------------------------------
# External methods
# ----------------------------------------------
@@ -114,10 +110,14 @@
def can_read_external?
can_write_connec?
end
+ def can_update_connec?
+ true
+ end
+
def can_write_connec?
true
end
def can_write_external?
@@ -145,13 +145,11 @@
# ----------------------------------------------
# Mapper methods
# ----------------------------------------------
# Map a Connec! entity to the external model
def map_to_external(entity)
- connec_id = entity[:__connec_id]
- mapped_entity = self.class.mapper_class.normalize(entity)
- (connec_id ? mapped_entity.merge(__connec_id: connec_id) : mapped_entity).with_indifferent_access
+ self.class.mapper_class.normalize(entity).with_indifferent_access
end
# Map an external entity to Connec! model
def map_to_connec(entity)
mapped_entity = self.class.mapper_class.denormalize(entity).merge(id: self.class.id_from_external_entity_hash(entity))
@@ -221,10 +219,14 @@
def push_entities_to_connec_to(mapped_external_entities_with_idmaps, connec_entity_name)
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}")
+ unless self.class.can_update_connec?
+ mapped_external_entities_with_idmaps.select! { |mapped_external_entity_with_idmap| !mapped_external_entity_with_idmap[:idmap].connec_id }
+ end
+
proc = ->(mapped_external_entity_with_idmap) { batch_op('post', mapped_external_entity_with_idmap[:entity], nil, self.class.normalize_connec_entity_name(connec_entity_name)) }
batch_calls(mapped_external_entities_with_idmaps, proc, connec_entity_name)
end
def batch_op(method, mapped_external_entity, id, connec_entity_name)
@@ -239,17 +241,17 @@
end
# ----------------------------------------------
# External methods
# ----------------------------------------------
- def get_external_entities_wrapper(last_synchronization_date = nil)
+ def get_external_entities_wrapper(last_synchronization_date = nil, entity_name = self.class.external_entity_name)
return [] if @opts[:__skip_external] || !self.class.can_read_external?
- get_external_entities(last_synchronization_date)
+ get_external_entities(entity_name, last_synchronization_date)
end
- def get_external_entities(last_synchronization_date = nil)
- Maestrano::Connector::Rails::ConnectorLogger.log('info', @organization, "Fetching #{Maestrano::Connector::Rails::External.external_name} #{self.class.external_entity_name.pluralize}")
+ def get_external_entities(external_entity_name, last_synchronization_date = nil)
+ Maestrano::Connector::Rails::ConnectorLogger.log('info', @organization, "Fetching #{Maestrano::Connector::Rails::External.external_name} #{external_entity_name.pluralize}")
raise 'Not implemented'
end
def push_entities_to_external(mapped_connec_entities_with_idmaps)
push_entities_to_external_to(mapped_connec_entities_with_idmaps, self.class.external_entity_name)
@@ -257,46 +259,52 @@
def push_entities_to_external_to(mapped_connec_entities_with_idmaps, external_entity_name)
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}")
- ids_to_send_to_connec = mapped_connec_entities_with_idmaps.map{ |mapped_connec_entity_with_idmap|
- idmap = push_entity_to_external(mapped_connec_entity_with_idmap, external_entity_name)
- idmap ? {idmap: idmap} : nil
+ entities_to_send_to_connec = mapped_connec_entities_with_idmaps.map{ |mapped_connec_entity_with_idmap|
+ push_entity_to_external(mapped_connec_entity_with_idmap, external_entity_name)
}.compact
- return if ids_to_send_to_connec.empty?
+ return if entities_to_send_to_connec.empty?
# Send the external ids to connec if it was a creation
- proc = ->(id) { batch_op('put', {id: [Maestrano::Connector::Rails::ConnecHelper.id_hash(id[:idmap].external_id, @organization)]}, id[:idmap].connec_id, self.class.normalize_connec_entity_name(self.class.connec_entity_name)) }
- batch_calls(ids_to_send_to_connec, proc, self.class.connec_entity_name, true)
+ # or if there are some sub entities ids to send (completed_hash)
+ proc = lambda do |entity|
+ id = {id: [Maestrano::Connector::Rails::ConnecHelper.id_hash(entity[:idmap].external_id, @organization)]}
+ body = entity[:completed_hash] ? entity[:completed_hash].merge(id) : id
+ batch_op('put', body, entity[:idmap].connec_id, self.class.normalized_connec_entity_name)
+ end
+ batch_calls(entities_to_send_to_connec, proc, self.class.connec_entity_name, true)
end
def push_entity_to_external(mapped_connec_entity_with_idmap, external_entity_name)
idmap = mapped_connec_entity_with_idmap[:idmap]
mapped_connec_entity = mapped_connec_entity_with_idmap[:entity]
+ id_refs_only_connec_entity = mapped_connec_entity_with_idmap[:id_refs_only_connec_entity]
begin
# Create and return id to send to connec!
if idmap.external_id.blank?
- external_id = create_external_entity(mapped_connec_entity, external_entity_name)
- idmap.update(external_id: external_id, last_push_to_external: Time.now, message: nil)
- return idmap
+ external_hash = create_external_entity(mapped_connec_entity, external_entity_name)
+ idmap.update(external_id: self.class.id_from_external_entity_hash(external_hash), last_push_to_external: Time.now, message: nil)
+ return {idmap: idmap, completed_hash: map_and_complete_hash_with_connec_ids(external_hash, external_entity_name, id_refs_only_connec_entity)}
# Update
else
- return unless self.class.can_update_external?
- update_external_entity(mapped_connec_entity, idmap.external_id, external_entity_name)
+ return nil unless self.class.can_update_external?
+ external_hash = update_external_entity(mapped_connec_entity, idmap.external_id, external_entity_name)
- # Return the id to send it to connec! if the first push of a singleton
- if self.class.singleton? && idmap.last_push_to_external.nil?
+ completed_hash = map_and_complete_hash_with_connec_ids(external_hash, external_entity_name, id_refs_only_connec_entity)
+
+ # Return the idmap to send it to connec! only if it's the first push of a singleton
+ # or if there is a completed hash to send
+ if (self.class.singleton? && idmap.last_push_to_external.nil?) || completed_hash
idmap.update(last_push_to_external: Time.now, message: nil)
- return idmap
- else
- idmap.update(last_push_to_external: Time.now, message: nil)
+ return {idmap: idmap, completed_hash: completed_hash}
end
-
+ idmap.update(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(message: e.message.truncate(255))
@@ -312,10 +320,20 @@
def update_external_entity(mapped_connec_entity, external_id, external_entity_name)
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
+ # Maps the entity received from external after a creation or an update and complete the received ids with the connec ones
+ def map_and_complete_hash_with_connec_ids(external_hash, external_entity_name, connec_hash)
+ return nil if connec_hash.empty?
+
+ mapped_external_hash = map_to_connec(external_hash)
+ id_references = Maestrano::Connector::Rails::ConnecHelper.format_references(self.class.references)
+
+ Maestrano::Connector::Rails::ConnecHelper.merge_id_hashes(connec_hash, mapped_external_hash, id_references[:id_references])
+ end
+
# ----------------------------------------------
# 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
@@ -334,25 +352,27 @@
def consolidate_and_map_connec_entities(connec_entities, external_entities, references, external_entity_name)
connec_entities.map{|entity|
# Entity has been created before date filtering limit
next nil if before_date_filtering_limit?(entity, false) && !@opts[:full_sync]
- entity = Maestrano::Connector::Rails::ConnecHelper.unfold_references(entity, references, @organization)
+ unfold_hash = Maestrano::Connector::Rails::ConnecHelper.unfold_references(entity, references, @organization)
+ entity = unfold_hash[:entity]
next nil unless entity
- connec_id = entity.delete(:__connec_id)
+ connec_id = unfold_hash[:connec_id]
+ id_refs_only_connec_entity = unfold_hash[:id_refs_only_connec_entity]
if entity['id'].blank?
idmap = self.class.find_or_create_idmap(organization_id: @organization.id, name: self.class.object_name_from_connec_entity_hash(entity), external_entity: external_entity_name.downcase, connec_id: connec_id)
- next map_connec_entity_with_idmap(entity, external_entity_name, idmap)
+ next map_connec_entity_with_idmap(entity, external_entity_name, idmap, id_refs_only_connec_entity)
end
idmap = self.class.find_or_create_idmap(external_id: entity['id'], organization_id: @organization.id, external_entity: external_entity_name.downcase, connec_id: connec_id)
idmap.update(name: self.class.object_name_from_connec_entity_hash(entity))
next nil if idmap.external_inactive || !idmap.to_external || (!@opts[:full_sync] && not_modified_since_last_push_to_external?(idmap, entity))
# Check for conflict with entities from external
- solve_conflict(entity, external_entities, external_entity_name, idmap)
+ solve_conflict(entity, external_entities, external_entity_name, idmap, id_refs_only_connec_entity)
}.compact
end
def consolidate_and_map_external_entities(external_entities, connec_entity_name)
external_entities.map{|entity|
@@ -395,14 +415,15 @@
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), idmap: idmap}]}
else
- entity = Maestrano::Connector::Rails::ConnecHelper.unfold_references(connec_entities.first, self.class.references, @organization)
- idmap.update(name: self.class.object_name_from_connec_entity_hash(entity), connec_id: entity.delete(:__connec_id))
+ unfold_hash = Maestrano::Connector::Rails::ConnecHelper.unfold_references(connec_entities.first, self.class.references, @organization)
+ entity = unfold_hash[:entity]
+ idmap.update(name: self.class.object_name_from_connec_entity_hash(entity), connec_id: unfold_hash[:connec_id])
idmap.update(external_id: self.class.id_from_external_entity_hash(external_entities.first)) unless external_entities.empty?
- return {connec_entities: [{entity: map_to_external(entity), idmap: idmap}], external_entities: []}
+ return {connec_entities: [{entity: map_to_external(entity), idmap: idmap, id_refs_only_connec_entity: {}}], external_entities: []}
end
end
# ----------------------------------------------
# Internal helper methods
@@ -464,30 +485,30 @@
def is_connec_more_recent?(connec_entity, external_entity)
connec_entity['updated_at'] > self.class.last_update_date_from_external_entity_hash(external_entity)
end
- def solve_conflict(connec_entity, external_entities, external_entity_name, idmap)
+ def solve_conflict(connec_entity, external_entities, external_entity_name, idmap, id_refs_only_connec_entity)
external_entity = external_entities.find { |entity| connec_entity['id'] == self.class.id_from_external_entity_hash(entity) }
# No conflict
- return map_connec_entity_with_idmap(connec_entity, external_entity_name, idmap) unless external_entity
+ return map_connec_entity_with_idmap(connec_entity, external_entity_name, idmap, id_refs_only_connec_entity) unless external_entity
# Conflict
# We keep the most recently updated entity
keep_connec = @opts.key?(:connec_preemption) ? @opts[:connec_preemption] : is_connec_more_recent?(connec_entity, external_entity)
if keep_connec
Maestrano::Connector::Rails::ConnectorLogger.log('info', @organization, "Conflict between #{Maestrano::Connector::Rails::External.external_name} #{external_entity_name} #{external_entity} and Connec! #{self.class.connec_entity_name} #{connec_entity}. Entity from Connec! kept")
external_entities.delete(external_entity)
- map_connec_entity_with_idmap(connec_entity, external_entity_name, idmap)
+ map_connec_entity_with_idmap(connec_entity, external_entity_name, idmap, id_refs_only_connec_entity)
else
Maestrano::Connector::Rails::ConnectorLogger.log('info', @organization, "Conflict between #{Maestrano::Connector::Rails::External.external_name} #{external_entity_name} #{external_entity} and Connec! #{self.class.connec_entity_name} #{connec_entity}. Entity from external kept")
nil
end
end
- def map_connec_entity_with_idmap(connec_entity, external_entity_name, idmap)
- {entity: map_to_external(connec_entity), idmap: idmap}
+ def map_connec_entity_with_idmap(connec_entity, external_entity_name, idmap, id_refs_only_connec_entity)
+ {entity: map_to_external(connec_entity), idmap: idmap, id_refs_only_connec_entity: id_refs_only_connec_entity}
end
def map_external_entity_with_idmap(external_entity, connec_entity_name, idmap)
{entity: map_to_connec(external_entity), idmap: idmap}
end