app/models/maestrano/connector/rails/concerns/entity.rb in maestrano-connector-rails-0.2.20 vs app/models/maestrano/connector/rails/concerns/entity.rb in maestrano-connector-rails-0.3.0
- old
+ new
@@ -6,147 +6,195 @@
# If you add new entities, you need to generate
# a migration to add them to existing organizations
def entities_list
raise "Not implemented"
end
- end
- @@external_name = Maestrano::Connector::Rails::External.external_name
+ # ----------------------------------------------
+ # IdMap methods
+ # ----------------------------------------------
+ def names_hash
+ {
+ connec_entity: connec_entity_name.downcase,
+ external_entity: external_entity_name.downcase
+ }
+ end
+ def find_or_create_idmap(organization_and_id)
+ Maestrano::Connector::Rails::IdMap.find_or_create_by(names_hash.merge(organization_and_id))
+ 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
+ def find_idmap(organization_and_id)
+ Maestrano::Connector::Rails::IdMap.find_by(names_hash.merge(organization_and_id))
+ end
+
+ def create_idmap_from_external_entity(entity, organization)
+ h = names_hash.merge({
+ external_id: id_from_external_entity_hash(entity),
+ name: object_name_from_external_entity_hash(entity),
+ organization_id: organization.id
+ })
+ Maestrano::Connector::Rails::IdMap.create(h)
+ end
+
+ def create_idmap_from_connec_entity(entity, organization)
+ h = names_hash.merge({
+ connec_id: entity['id'],
+ name: object_name_from_connec_entity_hash(entity),
+ organization_id: organization.id
+ })
+ Maestrano::Connector::Rails::IdMap.create(h)
+ end
+
+ # ----------------------------------------------
+ # Connec! methods
+ # ----------------------------------------------
+ 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
+ end
+
+ # ----------------------------------------------
+ # External methods
+ # ----------------------------------------------
+ def id_from_external_entity_hash(entity)
+ raise "Not implemented"
+ end
+
+ def last_update_date_from_external_entity_hash(entity)
+ raise "Not implemented"
+ end
+
+ # ----------------------------------------------
+ # Entity specific methods
+ # Those methods need to be define in each entity
+ # ----------------------------------------------
+ # Is this resource a singleton (in Connec!)?
+ def singleton?
+ false
+ end
+
+ # Entity name in Connec!
+ def connec_entity_name
+ raise "Not implemented"
+ end
+
+ # Entity name in external system
+ def external_entity_name
+ raise "Not implemented"
+ end
+
+ # Entity Mapper Class
+ def mapper_class
+ raise "Not implemented"
+ end
+
+ # Return a string representing the object from a connec! entity hash
+ def object_name_from_connec_entity_hash(entity)
+ raise "Not implemented"
+ end
+
+ # Return a string representing the object from an external entity hash
+ def object_name_from_external_entity_hash(entity)
+ raise "Not implemented"
+ end
+ end
+
# ----------------------------------------------
# Mapper methods
# ----------------------------------------------
# Map a Connec! entity to the external format
def map_to_external(entity, organization)
- mapper_class.normalize(entity)
+ self.class.mapper_class.normalize(entity)
end
# Map an external entity to Connec! format
def map_to_connec(entity, organization)
- mapper_class.denormalize(entity)
+ self.class.mapper_class.denormalize(entity)
end
# ----------------------------------------------
- # IdMap methods
- # ----------------------------------------------
- def names_hash
- {
- connec_entity: connec_entity_name.downcase,
- external_entity: external_entity_name.downcase
- }
- end
-
- def find_or_create_idmap(organization_and_id)
- Maestrano::Connector::Rails::IdMap.find_or_create_by(names_hash.merge(organization_and_id))
- 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
- def find_idmap(organization_and_id)
- Maestrano::Connector::Rails::IdMap.find_by(names_hash.merge(organization_and_id))
- end
-
- def create_idmap_from_external_entity(entity, organization)
- h = names_hash.merge({
- external_id: get_id_from_external_entity_hash(entity),
- name: object_name_from_external_entity_hash(entity),
- organization_id: organization.id
- })
- Maestrano::Connector::Rails::IdMap.create(h)
- end
-
- def create_idmap_from_connec_entity(entity, organization)
- h = names_hash.merge({
- connec_id: entity['id'],
- name: object_name_from_connec_entity_hash(entity),
- organization_id: organization.id
- })
- Maestrano::Connector::Rails::IdMap.create(h)
- end
- # ----------------------------------------------
# Connec! methods
# ----------------------------------------------
- def normalized_connec_entity_name
- normalize_connec_entity_name(connec_entity_name)
- end
-
- def normalize_connec_entity_name(connec_entity_name)
- if singleton?
- connec_entity_name.downcase
- else
- connec_entity_name.downcase.pluralize
- end
- end
-
def get_connec_entities(client, last_synchronization, organization, opts={})
- Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Fetching Connec! #{connec_entity_name}")
+ Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Fetching Connec! #{self.class.connec_entity_name}")
entities = []
# Fetch first page
if last_synchronization.blank? || opts[:full_sync]
- Maestrano::Connector::Rails::ConnectorLogger.log('debug', organization, "entity=#{connec_entity_name}, fetching all data")
- response = client.get("/#{normalized_connec_entity_name}")
+ Maestrano::Connector::Rails::ConnectorLogger.log('debug', organization, "entity=#{self.class.connec_entity_name}, fetching all data")
+ response = client.get("/#{self.class.normalized_connec_entity_name}")
else
- Maestrano::Connector::Rails::ConnectorLogger.log('debug', organization, "entity=#{connec_entity_name}, fetching data since #{last_synchronization.updated_at.iso8601}")
+ Maestrano::Connector::Rails::ConnectorLogger.log('debug', organization, "entity=#{self.class.connec_entity_name}, fetching data since #{last_synchronization.updated_at.iso8601}")
query_param = URI.encode("$filter=updated_at gt '#{last_synchronization.updated_at.iso8601}'")
- response = client.get("/#{normalized_connec_entity_name}?#{query_param}")
+ response = client.get("/#{self.class.normalized_connec_entity_name}?#{query_param}")
end
- raise "No data received from Connec! when trying to fetch #{connec_entity_name.pluralize}" unless response
+ raise "No data received from Connec! when trying to fetch #{self.class.connec_entity_name.pluralize}" unless response
response_hash = JSON.parse(response.body)
- Maestrano::Connector::Rails::ConnectorLogger.log('debug', organization, "received first page entity=#{connec_entity_name}, response=#{response.body}")
- if response_hash["#{normalized_connec_entity_name}"]
- entities << response_hash["#{normalized_connec_entity_name}"]
+ 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 #{connec_entity_name.pluralize}"
+ raise "Received unrecognized Connec! data when trying to fetch #{self.class.connec_entity_name.pluralize}"
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(/^(.*)\/#{normalized_connec_entity_name}/, normalized_connec_entity_name)
+ 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 #{connec_entity_name.pluralize}" unless response
- Maestrano::Connector::Rails::ConnectorLogger.log('debug', organization, "received next page entity=#{connec_entity_name}, response=#{response.body}")
+ raise "No data received from Connec! when trying to fetch subsequent page of #{self.class.connec_entity_name.pluralize}" unless response
+ 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["#{normalized_connec_entity_name}"]
- entities << response_hash["#{normalized_connec_entity_name}"]
+ 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 #{connec_entity_name.pluralize}"
+ 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=#{connec_entity_name}, Data=#{entities}")
+ Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Received data: Source=Connec!, Entity=#{self.class.connec_entity_name}, Data=#{entities}")
entities
end
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, connec_entity_name, organization)
+ push_entities_to_connec_to(connec_client, mapped_external_entities_with_idmaps, self.class.connec_entity_name, organization)
end
def push_entities_to_connec_to(connec_client, mapped_external_entities_with_idmaps, connec_entity_name, organization)
- Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Sending #{@@external_name} #{external_entity_name.pluralize} to Connec! #{connec_entity_name.pluralize}")
+ 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}")
mapped_external_entities_with_idmaps.each do |mapped_external_entity_with_idmap|
external_entity = mapped_external_entity_with_idmap[:entity]
idmap = mapped_external_entity_with_idmap[:idmap]
begin
if idmap.connec_id.blank?
- connec_entity = create_connec_entity(connec_client, external_entity, normalize_connec_entity_name(connec_entity_name), organization)
- idmap.update_attributes(connec_id: connec_entity['id'], connec_entity: connec_entity_name.downcase, last_push_to_connec: Time.now, message: nil)
+ connec_entity = create_connec_entity(connec_client, external_entity, self.class.normalize_connec_entity_name(connec_entity_name), organization)
+ idmap.update_attributes(connec_id: connec_entity['id'], last_push_to_connec: Time.now, message: nil)
else
- connec_entity = update_connec_entity(connec_client, external_entity, idmap.connec_id, normalize_connec_entity_name(connec_entity_name), organization)
+ connec_entity = update_connec_entity(connec_client, external_entity, idmap.connec_id, self.class.normalize_connec_entity_name(connec_entity_name), organization)
idmap.update_attributes(last_push_to_connec: Time.now, message: nil)
end
rescue => e
# Store Connec! error if any
+ Maestrano::Connector::Rails::ConnectorLogger.log('error', organization, "Error while pushing to Connec!: #{e}")
idmap.update_attributes(message: e.message)
end
end
end
@@ -165,39 +213,39 @@
raise "Connec!: #{response['errors']['title']}" if response['errors'] && response['errors']['title']
response["#{connec_entity_name}"]
end
def map_to_external_with_idmap(entity, organization)
- idmap = find_idmap({connec_id: entity['id'], organization_id: organization.id})
+ idmap = self.class.find_idmap({connec_id: entity['id'], organization_id: organization.id})
if idmap
- idmap.update(name: object_name_from_connec_entity_hash(entity))
+ 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! #{connec_entity_name} : #{entity}")
+ 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: create_idmap_from_connec_entity(entity, organization)}
+ {entity: map_to_external(entity, organization), idmap: self.class.create_idmap_from_connec_entity(entity, organization)}
end
end
# ----------------------------------------------
# External methods
# ----------------------------------------------
def get_external_entities(client, last_synchronization, organization, opts={})
- Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Fetching #{@@external_name} #{external_entity_name.pluralize}")
+ Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Fetching #{Maestrano::Connector::Rails::External.external_name} #{self.class.external_entity_name.pluralize}")
raise "Not implemented"
end
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, external_entity_name, organization)
+ push_entities_to_external_to(external_client, mapped_connec_entities_with_idmaps, self.class.external_entity_name, organization)
end
def push_entities_to_external_to(external_client, mapped_connec_entities_with_idmaps, external_entity_name, organization)
- Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Sending Connec! #{connec_entity_name.pluralize} to #{@@external_name} #{external_entity_name.pluralize}")
+ 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
@@ -206,66 +254,58 @@
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, external_entity: external_entity_name.downcase, last_push_to_external: Time.now, message: nil)
+ idmap.update_attributes(external_id: external_id, last_push_to_external: Time.now, message: nil)
else
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
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 #{@@external_name}")
+ 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
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 #{@@external_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
-
- def get_id_from_external_entity_hash(entity)
- raise "Not implemented"
- end
-
- def get_last_update_date_from_external_entity_hash(entity)
- raise "Not implemented"
- 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
# * Maps not discarded entities and associates them with their idmap, or create one if there isn't any
# * Return a hash {connec_entities: [], external_entities: []}
def consolidate_and_map_data(connec_entities, external_entities, organization, opts={})
- return consolidate_and_map_singleton(connec_entities, external_entities, organization, opts) if singleton?
+ return consolidate_and_map_singleton(connec_entities, external_entities, organization, opts) if self.class.singleton?
mapped_external_entities = external_entities.map{|entity|
- idmap = find_idmap({external_id: get_id_from_external_entity_hash(entity), organization_id: organization.id})
+ 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: object_name_from_external_entity_hash(entity))
+ idmap.update(name: self.class.object_name_from_external_entity_hash(entity))
else
- next {entity: map_to_connec(entity, organization), idmap: create_idmap_from_external_entity(entity, organization)}
+ 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)
+ 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, connec_entity_name, idmap, organization, opts)
+ 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)
@@ -276,11 +316,11 @@
end
def consolidate_and_map_singleton(connec_entities, external_entities, organization, opts={})
return {connec_entities: [], external_entities: []} if external_entities.empty? && connec_entities.empty?
- idmap = find_or_create_idmap({organization_id: organization.id})
+ 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
@@ -288,65 +328,41 @@
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: get_id_from_external_entity_hash(external_entities.first), name: object_name_from_external_entity_hash(external_entities.first))
+ 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: object_name_from_connec_entity_hash(connec_entities.first))
+ 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
# ----------------------------------------------
- # Entity specific methods
- # Those methods need to be define in each entity
+ # After and before sync
# ----------------------------------------------
- # Is this resource a singleton (in Connec!)?
- def singleton?
- false
+ def before_sync(connec_client, external_client, organization)
+ # Does nothing by default
end
- # Entity name in Connec!
- def connec_entity_name
- raise "Not implemented"
+ def after_sync(connec_client, external_client, organization)
+ # Does nothing by default
end
- # Entity name in external system
- def external_entity_name
- raise "Not implemented"
- end
-
- # Entity Mapper Class
- def mapper_class
- raise "Not implemented"
- end
-
- # Return a string representing the object from a connec! entity hash
- def object_name_from_connec_entity_hash(entity)
- raise "Not implemented"
- end
-
- # Return a string representing the object from an external entity hash
- def object_name_from_external_entity_hash(entity)
- raise "Not implemented"
- end
-
-
# ----------------------------------------------
# Internal helper methods
# ----------------------------------------------
module ClassMethods
- def not_modified_since_last_push_to_connec(idmap, entity, entity_instance, organization)
- result = idmap.last_push_to_connec && idmap.last_push_to_connec > entity_instance.get_last_update_date_from_external_entity_hash(entity)
- Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Discard #{entity_instance.external_entity_name} : #{entity}") if result
+ def not_modified_since_last_push_to_connec?(idmap, entity, entity_instance, organization)
+ result = idmap.last_push_to_connec && idmap.last_push_to_connec > entity_instance.class.last_update_date_from_external_entity_hash(entity)
+ Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Discard #{Maestrano::Connector::Rails::External::external_name} #{entity_instance.class.external_entity_name} : #{entity}") if result
result
end
def is_external_more_recent?(connec_entity, external_entity, entity_instance)
- connec_entity['updated_at'] < entity_instance.get_last_update_date_from_external_entity_hash(external_entity)
+ connec_entity['updated_at'] < entity_instance.class.last_update_date_from_external_entity_hash(external_entity)
end
def solve_conflict(external_entity, entity_instance, connec_entities, connec_entity_name, idmap, organization, opts)
if idmap.connec_id && connec_entity = connec_entities.detect{|connec_entity| connec_entity['id'] == idmap.connec_id}
# We keep the most recently updated entity
@@ -355,14 +371,14 @@
else
keep_external = is_external_more_recent?(connec_entity, external_entity, entity_instance)
end
if keep_external
- Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Conflict between #{entity_instance.external_entity_name} #{external_entity} and Connec! #{connec_entity_name} #{connec_entity}. Entity from external kept")
+ Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Conflict between #{Maestrano::Connector::Rails::External::external_name} #{entity_instance.class.external_entity_name} #{external_entity} and Connec! #{connec_entity_name} #{connec_entity}. Entity from external kept")
connec_entities.delete(connec_entity)
entity_instance.map_external_entity_with_idmap(external_entity, connec_entity_name, idmap, organization)
else
- Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Conflict between #{entity_instance.external_entity_name} #{external_entity} and Connec! #{connec_entity_name} #{connec_entity}. Entity from Connec! kept")
+ Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Conflict between #{Maestrano::Connector::Rails::External::external_name} #{entity_instance.class.external_entity_name} #{external_entity} and Connec! #{connec_entity_name} #{connec_entity}. Entity from Connec! kept")
nil
end
else
entity_instance.map_external_entity_with_idmap(external_entity, connec_entity_name, idmap, organization)
\ No newline at end of file