maestrano-connector-rails 0.2.16 → 0.2.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +27 -0
  4. data/VERSION +1 -1
  5. data/app/helpers/maestrano/connector/rails/session_helper.rb +5 -1
  6. data/app/models/maestrano/connector/rails/complex_entity.rb +14 -33
  7. data/app/models/maestrano/connector/rails/concerns/entity.rb +161 -73
  8. data/app/models/maestrano/connector/rails/sub_entity_base.rb +44 -4
  9. data/lib/generators/connector/install_generator.rb +20 -24
  10. data/lib/generators/connector/templates/home_controller.rb +33 -10
  11. data/lib/generators/connector/templates/home_controller_spec.rb +141 -0
  12. data/lib/generators/connector/templates/home_index.haml +103 -0
  13. data/lib/generators/connector/templates/layouts.haml +45 -0
  14. data/lib/generators/connector/templates/oauth_controller.rb +3 -6
  15. data/lib/generators/connector/templates/shared_entities_controller.rb +7 -0
  16. data/lib/generators/connector/templates/shared_entities_controller_spec.rb +23 -0
  17. data/lib/generators/connector/templates/shared_entities_index.haml +41 -0
  18. data/lib/generators/connector/templates/stylesheets/application.sass +24 -0
  19. data/lib/generators/connector/templates/stylesheets/banners.sass +59 -0
  20. data/lib/generators/connector/templates/stylesheets/home.sass +25 -0
  21. data/lib/generators/connector/templates/stylesheets/layout.sass +125 -0
  22. data/lib/generators/connector/templates/stylesheets/spacers.sass +46 -0
  23. data/lib/generators/connector/templates/stylesheets/variables.sass +57 -0
  24. data/lib/generators/connector/templates/sychronizations_controller_spec.rb +22 -0
  25. data/lib/generators/connector/templates/synchronizations_controller.rb +7 -0
  26. data/lib/generators/connector/templates/synchronizations_index.haml +42 -0
  27. data/maestrano-connector-rails.gemspec +29 -6
  28. data/pkg/maestrano-connector-rails-0.2.16.gem +0 -0
  29. data/spec/models/complex_entity_spec.rb +46 -12
  30. data/spec/models/entity_spec.rb +212 -113
  31. data/spec/models/sub_entity_base_spec.rb +59 -0
  32. data/template/maestrano-connector-template.rb +4 -3
  33. data/template/routes.rb +14 -0
  34. metadata +61 -5
  35. data/lib/generators/connector/templates/admin_controller.rb +0 -58
  36. data/lib/generators/connector/templates/admin_index.html.erb +0 -55
  37. data/lib/generators/connector/templates/home_index.html.erb +0 -48
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1ac017da954066c7397861d757d620250c1f9d17
4
- data.tar.gz: 95034e887a9a9e1ea69c66e397bcc1c9c698e824
3
+ metadata.gz: fb5c07cb2cd6e41eed0cf6d969bccfaca04bbfd7
4
+ data.tar.gz: 597eff9e10f8091d32df3efa8fe3d401686471b1
5
5
  SHA512:
6
- metadata.gz: 9f47439af3beb4b4795d47ba4b5a9685ec35557d9aff988f90486d5ed313499752b46b55c2a7e469e93c34ecb6dd200efc2e570ed6d1aca30d395118a6e97aea
7
- data.tar.gz: 9c91c0d18bb5edde590ce3a63349ac3fcd00fde0f359c30a8bd93d07372316c8998de736fa63a05b78250b1812553d554a12c8ea0c4e81277ef56bd49289402e
6
+ metadata.gz: 8255bd1676f26950267b98125e88679f3d91d5d127e650fbcfe4bc7d72c55b4d3d5ef3742022b6e2cdd267d5cab9d0fa449ed4197817f94c8ee43400320c4a55
7
+ data.tar.gz: 897a96ac9045cb71828ade21e216586f34db7af9bbec6ea7d6c1dca7cdbcbf9666e44a4a5dcb8f1fdb74fcd2fa194fa6f8386125f49b8cc21c73fc4dc372a65c
data/Gemfile CHANGED
@@ -4,6 +4,9 @@ source "http://rubygems.org"
4
4
  gem 'maestrano-rails'
5
5
  gem 'hash_mapper'
6
6
  gem 'sidekiq'
7
+ gem 'haml-rails'
8
+ gem 'bootstrap-sass'
9
+ gem 'autoprefixer-rails'
7
10
 
8
11
  # Add dependencies to develop your gem here.
9
12
  group :development do
data/Gemfile.lock CHANGED
@@ -38,6 +38,11 @@ GEM
38
38
  tzinfo (~> 1.1)
39
39
  addressable (2.4.0)
40
40
  arel (6.0.3)
41
+ autoprefixer-rails (6.3.3.1)
42
+ execjs
43
+ bootstrap-sass (3.3.6)
44
+ autoprefixer-rails (>= 5.2.1)
45
+ sass (>= 3.3.4)
41
46
  builder (3.2.2)
42
47
  concurrent-ruby (1.0.0)
43
48
  connection_pool (2.2.0)
@@ -48,6 +53,7 @@ GEM
48
53
  domain_name (0.5.20160128)
49
54
  unf (>= 0.0.5, < 1.0.0)
50
55
  erubis (2.7.0)
56
+ execjs (2.6.0)
51
57
  factory_girl (4.5.0)
52
58
  activesupport (>= 3.0.0)
53
59
  factory_girl_rails (4.6.0)
@@ -65,10 +71,23 @@ GEM
65
71
  oauth2
66
72
  globalid (0.3.6)
67
73
  activesupport (>= 4.1.0)
74
+ haml (4.0.7)
75
+ tilt
76
+ haml-rails (0.9.0)
77
+ actionpack (>= 4.0.1)
78
+ activesupport (>= 4.0.1)
79
+ haml (>= 4.0.6, < 5.0)
80
+ html2haml (>= 1.0.1)
81
+ railties (>= 4.0.1)
68
82
  hash_mapper (0.2.1)
69
83
  activesupport (~> 4)
70
84
  hashie (3.4.3)
71
85
  highline (1.7.8)
86
+ html2haml (2.0.0)
87
+ erubis (~> 2.7.0)
88
+ haml (~> 4.0.0)
89
+ nokogiri (~> 1.6.0)
90
+ ruby_parser (~> 3.5)
72
91
  http-cookie (1.0.2)
73
92
  domain_name (~> 0.5)
74
93
  httparty (0.13.7)
@@ -174,6 +193,10 @@ GEM
174
193
  rspec-mocks (~> 3.4.0)
175
194
  rspec-support (~> 3.4.0)
176
195
  rspec-support (3.4.1)
196
+ ruby_parser (3.8.1)
197
+ sexp_processor (~> 4.1)
198
+ sass (3.4.21)
199
+ sexp_processor (4.7.0)
177
200
  shoulda (3.5.0)
178
201
  shoulda-context (~> 1.0, >= 1.0.1)
179
202
  shoulda-matchers (>= 1.4.1, < 3.0)
@@ -200,6 +223,7 @@ GEM
200
223
  systemu (2.6.5)
201
224
  thor (0.19.1)
202
225
  thread_safe (0.3.5)
226
+ tilt (2.0.2)
203
227
  tzinfo (1.2.2)
204
228
  thread_safe (~> 0.1)
205
229
  unf (0.1.4)
@@ -212,8 +236,11 @@ PLATFORMS
212
236
  ruby
213
237
 
214
238
  DEPENDENCIES
239
+ autoprefixer-rails
240
+ bootstrap-sass
215
241
  bundler (~> 1.0)
216
242
  factory_girl_rails
243
+ haml-rails
217
244
  hash_mapper
218
245
  jeweler (~> 2.0.1)
219
246
  maestrano-rails
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.16
1
+ 0.2.17
@@ -6,12 +6,16 @@ module Maestrano::Connector::Rails
6
6
  end
7
7
 
8
8
  def current_organization
9
- Organization.find_by(uid: session[:org_uid], tenant: session[:tenant])
9
+ @current_organization ||= Organization.find_by(uid: session[:org_uid], tenant: session[:tenant])
10
10
  end
11
11
 
12
12
  def current_user
13
13
  @current_user ||= User.find_by(uid: session[:uid], tenant: session[:tenant])
14
14
  end
15
15
 
16
+ def is_admin
17
+ @is_admin ||= current_user && current_organization && is_admin?(current_user, current_organization)
18
+ end
19
+
16
20
  end
17
21
  end
@@ -52,14 +52,14 @@ module Maestrano::Connector::Rails
52
52
  # -------------------------------------------------------------
53
53
  # General methods
54
54
  # -------------------------------------------------------------
55
- def map_to_external_with_idmap(entity, organization, connec_entity_name, external_entity_name, sub_entity_instance)
56
- idmap = IdMap.find_by(connec_id: entity['id'], connec_entity: connec_entity_name.downcase, external_entity: external_entity_name.downcase, organization_id: organization.id)
55
+ def map_to_external_with_idmap(entity, organization, external_entity_name, sub_entity_instance)
56
+ idmap = sub_entity_instance.find_idmap({connec_id: entity['id'], external_entity: external_entity_name, organization_id: organization.id})
57
57
 
58
- if idmap && idmap.last_push_to_external && idmap.last_push_to_external > entity['updated_at']
59
- ConnectorLogger.log('info', organization, "Discard Connec! #{connec_entity_name} : #{entity}")
58
+ if idmap && ((!idmap.to_external) || idmap.last_push_to_external && idmap.last_push_to_external > entity['updated_at'])
59
+ ConnectorLogger.log('info', organization, "Discard Connec! #{sub_entity_instance.entity_name} : #{entity}")
60
60
  nil
61
61
  else
62
- {entity: sub_entity_instance.map_to(external_entity_name, entity, organization), idmap: idmap || IdMap.create(connec_id: entity['id'], connec_entity: connec_entity_name.downcase, external_entity: external_entity_name.downcase, organization_id: organization.id, name: sub_entity_instance.object_name_from_connec_entity_hash(entity))}
62
+ {entity: sub_entity_instance.map_to(external_entity_name, entity, organization), idmap: idmap || sub_entity_instance.create_idmap_from_connec_entity(entity, external_entity_name, organization)}
63
63
  end
64
64
  end
65
65
 
@@ -95,41 +95,22 @@ module Maestrano::Connector::Rails
95
95
  sub_entity_instance = "Entities::SubEntities::#{external_entity_name.titleize.split.join}".constantize.new
96
96
 
97
97
  entities.map!{|entity|
98
- idmap = IdMap.find_by(external_id: sub_entity_instance.get_id_from_external_entity_hash(entity), external_entity: external_entity_name.downcase, connec_entity: connec_entity_name.downcase, organization_id: organization.id)
98
+ idmap = sub_entity_instance.find_idmap(external_id: sub_entity_instance.get_id_from_external_entity_hash(entity), connec_entity: connec_entity_name, organization_id: organization.id)
99
99
 
100
100
  # No idmap: creating one, nothing else to do
101
101
  unless idmap
102
- next {entity: sub_entity_instance.map_to(connec_entity_name, entity, organization), idmap: IdMap.create(external_id: sub_entity_instance.get_id_from_external_entity_hash(entity), external_entity: external_entity_name.downcase, connec_entity: connec_entity_name.downcase, organization_id: organization.id, name: sub_entity_instance.object_name_from_external_entity_hash(entity))}
102
+ next {entity: sub_entity_instance.map_to(connec_entity_name, entity, organization), idmap: sub_entity_instance.create_idmap_from_external_entity(entity, connec_entity_name, organization)}
103
103
  end
104
104
 
105
+ # Not pushing entity to Connec!
106
+ next nil unless idmap.to_connec
107
+
105
108
  # Entity has not been modified since its last push to connec!
106
- if idmap.last_push_to_connec && idmap.last_push_to_connec > sub_entity_instance.get_last_update_date_from_external_entity_hash(entity)
107
- ConnectorLogger.log('info', organization, "Discard #{@@external_name} #{external_entity_name} : #{entity}")
108
- next nil
109
- end
109
+ next nil if Maestrano::Connector::Rails::Entity.not_modified_since_last_push_to_connec(idmap, entity, sub_entity_instance, organization)
110
110
 
111
- equivalent_connec_entities = connec_entities[connec_entity_name][external_entity_name] || []
112
111
  # Check for conflict with entities from connec!
113
- if idmap.connec_id && connec_entity = equivalent_connec_entities.detect{|connec_entity| connec_entity['id'] == idmap.connec_id}
114
- # We keep the most recently updated entity
115
- if !opts[:connec_preemption].nil?
116
- keep_external = !opts[:connec_preemption]
117
- else
118
- keep_external = connec_entity['updated_at'] < sub_entity_instance.get_last_update_date_from_external_entity_hash(entity)
119
- end
120
-
121
- if keep_external
122
- ConnectorLogger.log('info', organization, "Conflict between #{@@external_name} #{external_entity_name} #{entity} and Connec! #{connec_entity_name} #{connec_entity}. Entity from #{@@external_name} kept")
123
- equivalent_connec_entities.delete(connec_entity)
124
- {entity: sub_entity_instance.map_to(connec_entity_name, entity, organization), idmap: idmap}
125
- else
126
- ConnectorLogger.log('info', organization, "Conflict between #{@@external_name} #{external_entity_name} #{entity} and Connec! #{connec_entity_name} #{connec_entity}. Entity from Connec! kept")
127
- nil
128
- end
129
-
130
- else
131
- {entity: sub_entity_instance.map_to(connec_entity_name, entity, organization), idmap: idmap}
132
- end
112
+ equivalent_connec_entities = modeled_connec_entities[connec_entity_name][external_entity_name] || []
113
+ Maestrano::Connector::Rails::Entity.solve_conflict(entity, sub_entity_instance, equivalent_connec_entities, connec_entity_name, idmap, organization, opts)
133
114
  }.compact!
134
115
  end
135
116
  end
@@ -138,7 +119,7 @@ module Maestrano::Connector::Rails
138
119
  entities_in_external_model.each do |external_entity_name, entities|
139
120
  sub_entity_instance = "Entities::SubEntities::#{connec_entity_name.titleize.split.join}".constantize.new
140
121
  entities.map!{|entity|
141
- self.map_to_external_with_idmap(entity, organization, connec_entity_name, external_entity_name, sub_entity_instance)
122
+ self.map_to_external_with_idmap(entity, organization, external_entity_name, sub_entity_instance)
142
123
  }.compact!
143
124
  end
144
125
  end
@@ -17,116 +17,159 @@ module Maestrano::Connector::Rails::Concerns::Entity
17
17
  # ----------------------------------------------
18
18
  # Map a Connec! entity to the external format
19
19
  def map_to_external(entity, organization)
20
- self.mapper_class.normalize(entity)
20
+ mapper_class.normalize(entity)
21
21
  end
22
22
 
23
23
  # Map an external entity to Connec! format
24
24
  def map_to_connec(entity, organization)
25
- self.mapper_class.denormalize(entity)
25
+ mapper_class.denormalize(entity)
26
26
  end
27
27
 
28
+ # ----------------------------------------------
29
+ # IdMap methods
30
+ # ----------------------------------------------
31
+ def names_hash
32
+ {
33
+ connec_entity: connec_entity_name.downcase,
34
+ external_entity: external_entity_name.downcase
35
+ }
36
+ end
37
+
38
+ def find_or_create_idmap(organization_and_id)
39
+ Maestrano::Connector::Rails::IdMap.find_or_create_by(names_hash.merge(organization_and_id))
40
+ end
41
+
42
+ # organization_and_id can be either:
43
+ # * {connec_id: 'id', organization_id: 'id'}
44
+ # * {external_id: 'id', organization_id: 'id'}
45
+ # Needs to include either connec_entity or external_entity for complex entities
46
+ def find_idmap(organization_and_id)
47
+ Maestrano::Connector::Rails::IdMap.find_by(names_hash.merge(organization_and_id))
48
+ end
49
+
50
+ def create_idmap_from_external_entity(entity, organization)
51
+ h = names_hash.merge({
52
+ external_id: get_id_from_external_entity_hash(entity),
53
+ name: object_name_from_external_entity_hash(entity),
54
+ organization_id: organization.id
55
+ })
56
+ Maestrano::Connector::Rails::IdMap.create(h)
57
+ end
58
+
59
+ def create_idmap_from_connec_entity(entity, organization)
60
+ h = names_hash.merge({
61
+ connec_id: entity['id'],
62
+ name: object_name_from_connec_entity_hash(entity),
63
+ organization_id: organization.id
64
+ })
65
+ Maestrano::Connector::Rails::IdMap.create(h)
66
+ end
28
67
  # ----------------------------------------------
29
68
  # Connec! methods
30
69
  # ----------------------------------------------
31
70
  def normalized_connec_entity_name
32
- if self.singleton?
33
- self.connec_entity_name.downcase
71
+ if singleton?
72
+ connec_entity_name.downcase
34
73
  else
35
- self.connec_entity_name.downcase.pluralize
74
+ connec_entity_name.downcase.pluralize
36
75
  end
37
76
  end
38
77
 
39
78
  def get_connec_entities(client, last_synchronization, organization, opts={})
40
- Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Fetching Connec! #{self.connec_entity_name}")
79
+ Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Fetching Connec! #{connec_entity_name}")
41
80
 
42
81
  entities = []
43
82
 
44
83
  # Fetch first page
45
84
  if last_synchronization.blank? || opts[:full_sync]
46
- Maestrano::Connector::Rails::ConnectorLogger.log('debug', organization, "entity=#{self.connec_entity_name}, fetching all data")
47
- response = client.get("/#{self.normalized_connec_entity_name}")
85
+ Maestrano::Connector::Rails::ConnectorLogger.log('debug', organization, "entity=#{connec_entity_name}, fetching all data")
86
+ response = client.get("/#{normalized_connec_entity_name}")
48
87
  else
49
- Maestrano::Connector::Rails::ConnectorLogger.log('debug', organization, "entity=#{self.connec_entity_name}, fetching data since #{last_synchronization.updated_at.iso8601}")
88
+ Maestrano::Connector::Rails::ConnectorLogger.log('debug', organization, "entity=#{connec_entity_name}, fetching data since #{last_synchronization.updated_at.iso8601}")
50
89
  query_param = URI.encode("$filter=updated_at gt '#{last_synchronization.updated_at.iso8601}'")
51
- response = client.get("/#{self.normalized_connec_entity_name}?#{query_param}")
90
+ response = client.get("/#{normalized_connec_entity_name}?#{query_param}")
52
91
  end
53
- raise "No data received from Connec! when trying to fetch #{self.connec_entity_name.pluralize}" unless response
92
+ raise "No data received from Connec! when trying to fetch #{connec_entity_name.pluralize}" unless response
54
93
 
55
94
  response_hash = JSON.parse(response.body)
56
- Maestrano::Connector::Rails::ConnectorLogger.log('debug', organization, "received first page entity=#{self.connec_entity_name}, response=#{response.body}")
57
- if response_hash["#{self.normalized_connec_entity_name}"]
58
- entities << response_hash["#{self.normalized_connec_entity_name}"]
95
+ Maestrano::Connector::Rails::ConnectorLogger.log('debug', organization, "received first page entity=#{connec_entity_name}, response=#{response.body}")
96
+ if response_hash["#{normalized_connec_entity_name}"]
97
+ entities << response_hash["#{normalized_connec_entity_name}"]
59
98
  else
60
- raise "Received unrecognized Connec! data when trying to fetch #{self.connec_entity_name.pluralize}"
99
+ raise "Received unrecognized Connec! data when trying to fetch #{connec_entity_name.pluralize}"
61
100
  end
62
101
 
63
102
  # Fetch subsequent pages
64
103
  while response_hash['pagination'] && response_hash['pagination']['next']
65
104
  # ugly way to convert https://api-connec/api/v2/group_id/organizations?next_page_params to /organizations?next_page_params
66
- next_page = response_hash['pagination']['next'].gsub(/^(.*)\/#{self.normalized_connec_entity_name}/, self.normalized_connec_entity_name)
105
+ next_page = response_hash['pagination']['next'].gsub(/^(.*)\/#{normalized_connec_entity_name}/, normalized_connec_entity_name)
67
106
  response = client.get(next_page)
68
107
 
69
- raise "No data received from Connec! when trying to fetch subsequent page of #{self.connec_entity_name.pluralize}" unless response
70
- Maestrano::Connector::Rails::ConnectorLogger.log('debug', organization, "received next page entity=#{self.connec_entity_name}, response=#{response.body}")
108
+ raise "No data received from Connec! when trying to fetch subsequent page of #{connec_entity_name.pluralize}" unless response
109
+ Maestrano::Connector::Rails::ConnectorLogger.log('debug', organization, "received next page entity=#{connec_entity_name}, response=#{response.body}")
71
110
 
72
111
  response_hash = JSON.parse(response.body)
73
- if response_hash["#{self.normalized_connec_entity_name}"]
74
- entities << response_hash["#{self.normalized_connec_entity_name}"]
112
+ if response_hash["#{normalized_connec_entity_name}"]
113
+ entities << response_hash["#{normalized_connec_entity_name}"]
75
114
  else
76
- raise "Received unrecognized Connec! data when trying to fetch subsequent page of #{self.connec_entity_name.pluralize}"
115
+ raise "Received unrecognized Connec! data when trying to fetch subsequent page of #{connec_entity_name.pluralize}"
77
116
  end
78
117
  end
79
118
 
80
119
  entities = entities.flatten
81
- Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Received data: Source=Connec!, Entity=#{self.connec_entity_name}, Data=#{entities}")
120
+ Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Received data: Source=Connec!, Entity=#{connec_entity_name}, Data=#{entities}")
82
121
  entities
83
122
  end
84
123
 
85
124
  def push_entities_to_connec(connec_client, mapped_external_entities_with_idmaps, organization)
86
- self.push_entities_to_connec_to(connec_client, mapped_external_entities_with_idmaps, self.connec_entity_name, organization)
125
+ push_entities_to_connec_to(connec_client, mapped_external_entities_with_idmaps, connec_entity_name, organization)
87
126
  end
88
127
 
89
128
  def push_entities_to_connec_to(connec_client, mapped_external_entities_with_idmaps, connec_entity_name, organization)
90
- Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Sending #{@@external_name} #{self.external_entity_name.pluralize} to Connec! #{connec_entity_name.pluralize}")
129
+ Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Sending #{@@external_name} #{external_entity_name.pluralize} to Connec! #{connec_entity_name.pluralize}")
91
130
  mapped_external_entities_with_idmaps.each do |mapped_external_entity_with_idmap|
92
131
  external_entity = mapped_external_entity_with_idmap[:entity]
93
132
  idmap = mapped_external_entity_with_idmap[:idmap]
94
133
 
95
- if idmap.connec_id.blank?
96
- connec_entity = self.create_connec_entity(connec_client, external_entity, connec_entity_name, organization)
97
- idmap.update_attributes(connec_id: connec_entity['id'], connec_entity: connec_entity_name.downcase, last_push_to_connec: Time.now, message: nil)
98
- else
99
- connec_entity = self.update_connec_entity(connec_client, external_entity, idmap.connec_id, connec_entity_name, organization)
100
- idmap.update_attributes(last_push_to_connec: Time.now, message: nil)
134
+ begin
135
+ if idmap.connec_id.blank?
136
+ connec_entity = create_connec_entity(connec_client, external_entity, connec_entity_name, organization)
137
+ idmap.update_attributes(connec_id: connec_entity['id'], connec_entity: connec_entity_name.downcase, last_push_to_connec: Time.now, message: nil)
138
+ else
139
+ connec_entity = update_connec_entity(connec_client, external_entity, idmap.connec_id, connec_entity_name, organization)
140
+ idmap.update_attributes(last_push_to_connec: Time.now, message: nil)
141
+ end
142
+ rescue => e
143
+ # Store Connec! error if any
144
+ idmap.update_attributes(message: e.message)
101
145
  end
102
-
103
- # Store Connec! error if any
104
- idmap.update_attributes(message: connec_entity['errors'].first['title']) unless connec_entity.blank? || connec_entity['errors'].blank?
105
146
  end
106
147
  end
107
148
 
108
149
  def create_connec_entity(connec_client, mapped_external_entity, connec_entity_name, organization)
109
150
  Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Sending create #{connec_entity_name}: #{mapped_external_entity} to Connec!")
110
151
  response = connec_client.post("/#{normalized_connec_entity_name}", { "#{normalized_connec_entity_name}".to_sym => mapped_external_entity })
111
- raise "No response received from Connec! when trying to create a #{self.connec_entity_name}" unless response
112
- JSON.parse(response.body)["#{normalized_connec_entity_name}"]
152
+ response = JSON.parse(response.body)
153
+ raise "Connec!: #{response['errors']['title']}" if response['errors'] && response['errors']['title']
154
+ response["#{normalized_connec_entity_name}"]
113
155
  end
114
156
 
115
157
  def update_connec_entity(connec_client, mapped_external_entity, connec_id, connec_entity_name, organization)
116
158
  Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Sending update #{connec_entity_name}: #{mapped_external_entity} to Connec!")
117
159
  response = connec_client.put("/#{normalized_connec_entity_name}/#{connec_id}", { "#{normalized_connec_entity_name}".to_sym => mapped_external_entity })
118
- raise "No response received from Connec! when trying to update a #{self.connec_entity_name}" unless response
119
- JSON.parse(response.body)["#{normalized_connec_entity_name}"]
160
+ response = JSON.parse(response.body)
161
+ raise "Connec!: #{response['errors']['title']}" if response['errors'] && response['errors']['title']
162
+ response["#{normalized_connec_entity_name}"]
120
163
  end
121
164
 
122
165
  def map_to_external_with_idmap(entity, organization)
123
- idmap = Maestrano::Connector::Rails::IdMap.find_by(connec_id: entity['id'], connec_entity: self.connec_entity_name.downcase, organization_id: organization.id)
166
+ idmap = find_idmap({connec_id: entity['id'], organization_id: organization.id})
124
167
 
125
168
  if idmap && ((!idmap.to_external) || (idmap.last_push_to_external && idmap.last_push_to_external > entity['updated_at']))
126
- Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Discard Connec! #{self.connec_entity_name} : #{entity}")
169
+ Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Discard Connec! #{connec_entity_name} : #{entity}")
127
170
  nil
128
171
  else
129
- {entity: self.map_to_external(entity, organization), idmap: idmap || Maestrano::Connector::Rails::IdMap.create(connec_id: entity['id'], connec_entity: self.connec_entity_name.downcase, organization_id: organization.id, name: object_name_from_connec_entity_hash(entity))}
172
+ {entity: map_to_external(entity, organization), idmap: idmap || create_idmap_from_connec_entity(entity, organization)}
130
173
  end
131
174
  end
132
175
 
@@ -134,18 +177,18 @@ module Maestrano::Connector::Rails::Concerns::Entity
134
177
  # External methods
135
178
  # ----------------------------------------------
136
179
  def get_external_entities(client, last_synchronization, organization, opts={})
137
- Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Fetching #{@@external_name} #{self.external_entity_name.pluralize}")
180
+ Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Fetching #{@@external_name} #{external_entity_name.pluralize}")
138
181
  raise "Not implemented"
139
182
  end
140
183
 
141
184
  def push_entities_to_external(external_client, mapped_connec_entities_with_idmaps, organization)
142
- push_entities_to_external_to(external_client, mapped_connec_entities_with_idmaps, self.external_entity_name, organization)
185
+ push_entities_to_external_to(external_client, mapped_connec_entities_with_idmaps, external_entity_name, organization)
143
186
  end
144
187
 
145
188
  def push_entities_to_external_to(external_client, mapped_connec_entities_with_idmaps, external_entity_name, organization)
146
- Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Sending Connec! #{self.connec_entity_name.pluralize} to #{@@external_name} #{external_entity_name.pluralize}")
189
+ Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Sending Connec! #{connec_entity_name.pluralize} to #{@@external_name} #{external_entity_name.pluralize}")
147
190
  mapped_connec_entities_with_idmaps.each do |mapped_connec_entity_with_idmap|
148
- self.push_entity_to_external(external_client, mapped_connec_entity_with_idmap, external_entity_name, organization)
191
+ push_entity_to_external(external_client, mapped_connec_entity_with_idmap, external_entity_name, organization)
149
192
  end
150
193
  end
151
194
 
@@ -155,10 +198,10 @@ module Maestrano::Connector::Rails::Concerns::Entity
155
198
 
156
199
  begin
157
200
  if idmap.external_id.blank?
158
- external_id = self.create_external_entity(external_client, connec_entity, external_entity_name, organization)
201
+ external_id = create_external_entity(external_client, connec_entity, external_entity_name, organization)
159
202
  idmap.update_attributes(external_id: external_id, external_entity: external_entity_name.downcase, last_push_to_external: Time.now, message: nil)
160
203
  else
161
- self.update_external_entity(external_client, connec_entity, idmap.external_id, external_entity_name, organization)
204
+ update_external_entity(external_client, connec_entity, idmap.external_id, external_entity_name, organization)
162
205
  idmap.update_attributes(last_push_to_external: Time.now, message: nil)
163
206
  end
164
207
  rescue => e
@@ -193,54 +236,56 @@ module Maestrano::Connector::Rails::Concerns::Entity
193
236
  # * Maps not discarded entities and associates them with their idmap, or create one if there isn't any
194
237
  # * Return a hash {connec_entities: [], external_entities: []}
195
238
  def consolidate_and_map_data(connec_entities, external_entities, organization, opts={})
239
+ return consolidate_and_map_singleton(connec_entities, external_entities, organization, opts) if singleton?
240
+
196
241
  mapped_external_entities = external_entities.map{|entity|
197
- idmap = Maestrano::Connector::Rails::IdMap.find_by(external_id: self.get_id_from_external_entity_hash(entity), external_entity: self.external_entity_name.downcase, organization_id: organization.id)
242
+ idmap = find_idmap({external_id: get_id_from_external_entity_hash(entity), organization_id: organization.id})
198
243
  # No idmap: creating one, nothing else to do
199
244
  unless idmap
200
- next {entity: self.map_to_connec(entity, organization), idmap: Maestrano::Connector::Rails::IdMap.create(external_id: self.get_id_from_external_entity_hash(entity), external_entity: self.external_entity_name.downcase, organization_id: organization.id, name: self.object_name_from_external_entity_hash(entity))}
245
+ next {entity: map_to_connec(entity, organization), idmap: create_idmap_from_external_entity(entity, organization)}
201
246
  end
202
247
 
203
248
  # Not pushing entity to Connec!
204
249
  next nil unless idmap.to_connec
205
250
 
206
251
  # Entity has not been modified since its last push to connec!
207
- if idmap.last_push_to_connec && idmap.last_push_to_connec > self.get_last_update_date_from_external_entity_hash(entity)
208
- Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Discard #{@@external_name} #{self.external_entity_name} : #{entity}")
209
- next nil
210
- end
252
+ next nil if self.class.not_modified_since_last_push_to_connec(idmap, entity, self, organization)
211
253
 
212
254
  # Check for conflict with entities from connec!
213
- if idmap.connec_id && connec_entity = connec_entities.detect{|connec_entity| connec_entity['id'] == idmap.connec_id}
214
- # We keep the most recently updated entity
215
- if !opts[:connec_preemption].nil?
216
- keep_external = !opts[:connec_preemption]
217
- else
218
- keep_external = connec_entity['updated_at'] < self.get_last_update_date_from_external_entity_hash(entity)
219
- end
220
-
221
- if keep_external
222
- Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Conflict between #{@@external_name} #{self.external_entity_name} #{entity} and Connec! #{self.connec_entity_name} #{connec_entity}. Entity from #{@@external_name} kept")
223
- connec_entities.delete(connec_entity)
224
- {entity: self.map_to_connec(entity, organization), idmap: idmap}
225
- else
226
- Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Conflict between #{@@external_name} #{self.external_entity_name} #{entity} and Connec! #{self.connec_entity_name} #{connec_entity}. Entity from Connec! kept")
227
- nil
228
- end
229
-
230
- else
231
- {entity: self.map_to_connec(entity, organization), idmap: idmap}
232
- end
255
+ self.class.solve_conflict(entity, self, connec_entities, connec_entity_name, idmap, organization, opts)
233
256
  }
234
257
  mapped_external_entities.compact!
235
258
 
236
259
  mapped_connec_entities = connec_entities.map{|entity|
237
- self.map_to_external_with_idmap(entity, organization)
260
+ map_to_external_with_idmap(entity, organization)
238
261
  }
239
262
  mapped_connec_entities.compact!
240
263
 
241
264
  return {connec_entities: mapped_connec_entities, external_entities: mapped_external_entities}
242
265
  end
243
266
 
267
+ def consolidate_and_map_singleton(connec_entities, external_entities, organization, opts={})
268
+ return {connec_entities: [], external_entities: []} if external_entities.empty? && connec_entities.empty?
269
+
270
+ idmap = find_or_create_idmap({organization_id: organization.id})
271
+
272
+ if external_entities.empty?
273
+ keep_external = false
274
+ elsif connec_entities.empty?
275
+ keep_external = true
276
+ elsif !opts[:connec_preemption].nil?
277
+ keep_external = !opts[:connec_preemption]
278
+ else
279
+ keep_external = self.class.is_external_more_recent?(connec_entities.first, external_entities.first, self)
280
+ end
281
+ if keep_external
282
+ idmap.update(external_id: get_id_from_external_entity_hash(external_entities.first))
283
+ return {connec_entities: [], external_entities: [{entity: map_to_connec(external_entities.first, organization), idmap: idmap}]}
284
+ else
285
+ idmap.update(connec_id: connec_entities.first['id'])
286
+ return {connec_entities: [{entity: map_to_external(connec_entities.first, organization), idmap: idmap}], external_entities: []}
287
+ end
288
+ end
244
289
 
245
290
  # ----------------------------------------------
246
291
  # Entity specific methods
@@ -275,4 +320,47 @@ module Maestrano::Connector::Rails::Concerns::Entity
275
320
  def object_name_from_external_entity_hash(entity)
276
321
  raise "Not implemented"
277
322
  end
323
+
324
+
325
+ # ----------------------------------------------
326
+ # Internal helper methods
327
+ # ----------------------------------------------
328
+ module ClassMethods
329
+ def not_modified_since_last_push_to_connec(idmap, entity, entity_instance, organization)
330
+ result = idmap.last_push_to_connec && idmap.last_push_to_connec > entity_instance.get_last_update_date_from_external_entity_hash(entity)
331
+ Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Discard #{entity_instance.external_entity_name} : #{entity}") unless result
332
+ result
333
+ end
334
+
335
+ def is_external_more_recent?(connec_entity, external_entity, entity_instance)
336
+ connec_entity['updated_at'] < entity_instance.get_last_update_date_from_external_entity_hash(external_entity)
337
+ end
338
+
339
+ def solve_conflict(external_entity, entity_instance, connec_entities, connec_entity_name, idmap, organization, opts)
340
+ if idmap.connec_id && connec_entity = connec_entities.detect{|connec_entity| connec_entity['id'] == idmap.connec_id}
341
+ # We keep the most recently updated entity
342
+ if !opts[:connec_preemption].nil?
343
+ keep_external = !opts[:connec_preemption]
344
+ else
345
+ keep_external = is_external_more_recent?(connec_entity, external_entity, entity_instance)
346
+ end
347
+
348
+ if keep_external
349
+ 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")
350
+ connec_entities.delete(connec_entity)
351
+ entity_instance.map_external_entity_with_idmap(external_entity, connec_entity_name, idmap, organization)
352
+ else
353
+ 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")
354
+ nil
355
+ end
356
+
357
+ else
358
+ entity_instance.map_external_entity_with_idmap(external_entity, connec_entity_name, idmap, organization)
359
+ end
360
+ end
361
+ end
362
+
363
+ def map_external_entity_with_idmap(external_entity, connec_entity_name, idmap, organization)
364
+ {entity: map_to_connec(external_entity, organization), idmap: idmap}
365
+ end
278
366
  end