maestrano-connector-rails 0.2.16 → 0.2.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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