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