maestrano-connector-rails 1.0.4 → 1.1.0

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -2
  3. data/LICENSE +1 -1
  4. data/VERSION +1 -1
  5. data/app/controllers/maestrano/connec_controller.rb +3 -3
  6. data/app/jobs/maestrano/connector/rails/all_synchronizations_job.rb +1 -1
  7. data/app/jobs/maestrano/connector/rails/push_to_connec_job.rb +3 -3
  8. data/app/jobs/maestrano/connector/rails/synchronization_job.rb +9 -8
  9. data/app/models/maestrano/connector/rails/concerns/complex_entity.rb +6 -6
  10. data/app/models/maestrano/connector/rails/concerns/entity.rb +24 -11
  11. data/app/models/maestrano/connector/rails/organization.rb +7 -2
  12. data/app/models/maestrano/connector/rails/synchronization.rb +1 -1
  13. data/db/migrate/20160614114401_add_date_filtering_limit_to_organization.rb +11 -0
  14. data/db/migrate/20160614160654_add_encryption_on_oauth_keys.rb +19 -0
  15. data/lib/generators/connector/install_generator.rb +1 -0
  16. data/lib/generators/connector/templates/complex_entity_example/person.rb +1 -1
  17. data/lib/generators/connector/templates/entity.rb +9 -3
  18. data/lib/generators/connector/templates/home.js +11 -0
  19. data/lib/generators/connector/templates/home_controller.rb +12 -1
  20. data/lib/generators/connector/templates/home_index.haml +11 -0
  21. data/maestrano-connector-rails.gemspec +21 -4
  22. data/release_notes.md +6 -0
  23. data/spec/dummy/config/settings.yml +37 -0
  24. data/spec/dummy/config/settings/development.yml +12 -0
  25. data/spec/dummy/config/settings/production.yml +1 -0
  26. data/spec/dummy/config/settings/test.yml +12 -0
  27. data/spec/dummy/config/settings/uat.yml +1 -0
  28. data/spec/dummy/db/schema.rb +11 -6
  29. data/spec/integration/connec_to_external_spec.rb +24 -2
  30. data/spec/models/entity_spec.rb +49 -8
  31. data/spec/models/organization_spec.rb +34 -1
  32. data/spec/models/synchronization_spec.rb +16 -7
  33. data/spec/spec_helper.rb +1 -0
  34. data/template/application.yml.sample +5 -0
  35. data/template/maestrano-connector-template.rb +3 -1
  36. data/template/settings/development.yml +2 -1
  37. data/template/settings/settings.yml +2 -1
  38. data/template/settings/test.yml +2 -1
  39. metadata +53 -3
  40. data/db/20160524112054_add_encryption_on_oauth_keys.rb +0 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4b5dba94b691f6b3af1ac41b6a7d0398ee5396af
4
- data.tar.gz: dc4611f5da086cff9fc297b0d0d0eba7aa4e5c8d
3
+ metadata.gz: d686a6327d19c9928de390fdff97b8a49314ee12
4
+ data.tar.gz: 24766745312ecf5dbdc9cabd262602ecaee2883f
5
5
  SHA512:
6
- metadata.gz: 4b2683f1f4283965f1f4a4c81be839aaa72d734432aa6246d21d2dbf15e90be0161d11a8e863584863531c15af823a2375f38acd667a212733793d47fc32ad57
7
- data.tar.gz: d7931fb298a3b542c4a9878ccb6e5b2969efa4b67eb11c47aef601b50ff681d3539d013ed25e3934c129f55a9bb3b992a269fa81e7031fe75c8dbbda5e5f7088
6
+ metadata.gz: 44eeb799a05babf52afa93d910dcabb1addc680609c9ad18b5347f2dbe5055e5e9d7edd317013c032691f84570865d2d9d83b16a732273777b48e919218fd9ee
7
+ data.tar.gz: 5f2e00125ac1c7e482015f705fff22f689ee1b5eade8eeb3c27031dbad863ab989972851e399c97384c3b7fc5d533564405b543748c424602b3f8ce7ac77a67a
data/Gemfile CHANGED
@@ -7,8 +7,8 @@ gem 'sidekiq'
7
7
  gem 'haml-rails'
8
8
  gem 'bootstrap-sass'
9
9
  gem 'autoprefixer-rails'
10
- # gem 'attr_encrypted', '~> 3.0.0'
11
- # gem 'config'
10
+ gem 'attr_encrypted', '~> 1.4.0'
11
+ gem 'config'
12
12
 
13
13
  # Add dependencies to develop your gem here.
14
14
  group :development do
@@ -21,4 +21,5 @@ group :development do
21
21
  gem 'factory_girl_rails'
22
22
  gem 'sqlite3'
23
23
  gem 'shoulda-matchers'
24
+ gem 'timecop'
24
25
  end
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2015 Maestrano
3
+ Copyright (c) 2016 Maestrano
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.4
1
+ 1.1.0
@@ -18,10 +18,10 @@ class Maestrano::ConnecController < Maestrano::Rails::WebHookController
18
18
  Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Received entity from Connec! webhook: Entity=#{entity_name}, Data=#{entity}")
19
19
  connec_client = Maestrano::Connector::Rails::ConnecHelper.get_client(organization)
20
20
  external_client = Maestrano::Connector::Rails::External.get_client(organization)
21
- last_synchronization = organization.last_successful_synchronization
21
+ last_synchronization_date = organization.last_synchronization_date
22
22
 
23
23
  entity_instance = entity_class_hash[:class].new(organization, connec_client, external_client, {})
24
- entity_instance.before_sync(last_synchronization)
24
+ entity_instance.before_sync(last_synchronization_date)
25
25
 
26
26
  # Build expected input for consolidate_and_map_data
27
27
  if entity_class_hash[:is_complex]
@@ -33,7 +33,7 @@ class Maestrano::ConnecController < Maestrano::Rails::WebHookController
33
33
  end
34
34
  entity_instance.push_entities_to_external(mapped_entity[:connec_entities])
35
35
 
36
- entity_instance.after_sync(last_synchronization)
36
+ entity_instance.after_sync(last_synchronization_date)
37
37
  end
38
38
  end
39
39
  rescue => e
@@ -4,7 +4,7 @@ module Maestrano::Connector::Rails
4
4
 
5
5
  # Trigger synchronization of all active organizations
6
6
  def perform(name=nil, count=nil)
7
- Maestrano::Connector::Rails::Organization.where('oauth_provider IS NOT NULL AND oauth_token IS NOT NULL').each do |o|
7
+ Maestrano::Connector::Rails::Organization.where.not(oauth_provider: nil, encrypted_oauth_token: nil).each do |o|
8
8
  next unless [true, 1].include?(o.sync_enabled)
9
9
  Maestrano::Connector::Rails::SynchronizationJob.perform_later(o, {})
10
10
  end
@@ -8,7 +8,7 @@ module Maestrano::Connector::Rails
8
8
 
9
9
  connec_client = Maestrano::Connector::Rails::ConnecHelper.get_client(organization)
10
10
  external_client = Maestrano::Connector::Rails::External.get_client(organization)
11
- last_synchronization = organization.last_successful_synchronization
11
+ last_synchronization_date = organization.last_synchronization_date
12
12
 
13
13
  entities_hash.each do |external_entity_name, entities|
14
14
  if entity_instance_hash = find_entity_instance(external_entity_name, organization, connec_client, external_client, opts)
@@ -16,7 +16,7 @@ module Maestrano::Connector::Rails
16
16
 
17
17
  entity_instance = entity_instance_hash[:instance]
18
18
 
19
- entity_instance.before_sync(last_synchronization)
19
+ entity_instance.before_sync(last_synchronization_date)
20
20
  # Build expected input for consolidate_and_map_data
21
21
  if entity_instance_hash[:is_complex]
22
22
  mapped_entities = entity_instance.consolidate_and_map_data(ComplexEntity.build_empty_hash(entity_instance.class.connec_entities_names), ComplexEntity.build_hash_with_entities(entity_instance.class.external_entities_names, external_entity_name, lambda{|name| name}, entities))
@@ -25,7 +25,7 @@ module Maestrano::Connector::Rails
25
25
  end
26
26
  entity_instance.push_entities_to_connec(mapped_entities[:external_entities])
27
27
 
28
- entity_instance.after_sync(last_synchronization)
28
+ entity_instance.after_sync(last_synchronization_date)
29
29
  else
30
30
  Rails.logger.warn "Called push to connec job with unknow entity: #{external_entity_name}"
31
31
  end
@@ -29,6 +29,7 @@ module Maestrano::Connector::Rails
29
29
 
30
30
  begin
31
31
  last_synchronization = organization.last_successful_synchronization
32
+ last_synchronization_date = organization.last_synchronization_date
32
33
  connec_client = ConnecHelper.get_client(organization)
33
34
  external_client = External.get_client(organization)
34
35
 
@@ -38,7 +39,7 @@ module Maestrano::Connector::Rails
38
39
  ConnectorLogger.log('info', organization, "First synchronization ever. Doing two half syncs to allow smart merging to work its magic.")
39
40
  [{skip_connec: true}, {skip_external: true}].each do |opt|
40
41
  organization.synchronized_entities.select{|k, v| v}.keys.each do |entity|
41
- sync_entity(entity.to_s, organization, connec_client, external_client, last_synchronization, opts.merge(opt))
42
+ sync_entity(entity.to_s, organization, connec_client, external_client, last_synchronization_date, opts.merge(opt))
42
43
  end
43
44
  end
44
45
  elsif opts[:only_entities]
@@ -46,11 +47,11 @@ module Maestrano::Connector::Rails
46
47
  # The synchronization is marked as partial and will not be considered as the last-synchronization for the next sync
47
48
  current_synchronization.set_partial
48
49
  opts[:only_entities].each do |entity|
49
- sync_entity(entity, organization, connec_client, external_client, last_synchronization, opts)
50
+ sync_entity(entity, organization, connec_client, external_client, last_synchronization_date, opts)
50
51
  end
51
52
  else
52
53
  organization.synchronized_entities.select{|k, v| v}.keys.each do |entity|
53
- sync_entity(entity.to_s, organization, connec_client, external_client, last_synchronization, opts)
54
+ sync_entity(entity.to_s, organization, connec_client, external_client, last_synchronization_date, opts)
54
55
  end
55
56
  end
56
57
 
@@ -62,16 +63,16 @@ module Maestrano::Connector::Rails
62
63
  end
63
64
  end
64
65
 
65
- def sync_entity(entity_name, organization, connec_client, external_client, last_synchronization, opts)
66
+ def sync_entity(entity_name, organization, connec_client, external_client, last_synchronization_date, opts)
66
67
  entity_instance = "Entities::#{entity_name.titleize.split.join}".constantize.new(organization, connec_client, external_client, opts.dup)
67
68
 
68
- entity_instance.before_sync(last_synchronization)
69
- external_entities = entity_instance.get_external_entities_wrapper(last_synchronization)
70
- connec_entities = entity_instance.get_connec_entities(last_synchronization)
69
+ entity_instance.before_sync(last_synchronization_date)
70
+ external_entities = entity_instance.get_external_entities_wrapper(last_synchronization_date)
71
+ connec_entities = entity_instance.get_connec_entities(last_synchronization_date)
71
72
  mapped_entities = entity_instance.consolidate_and_map_data(connec_entities, external_entities)
72
73
  entity_instance.push_entities_to_external(mapped_entities[:connec_entities])
73
74
  entity_instance.push_entities_to_connec(mapped_entities[:external_entities])
74
- entity_instance.after_sync(last_synchronization)
75
+ entity_instance.after_sync(last_synchronization_date)
75
76
  end
76
77
  end
77
78
  end
@@ -59,22 +59,22 @@ module Maestrano::Connector::Rails::Concerns::ComplexEntity
59
59
  # -------------------------------------------------------------
60
60
  # Entity equivalent methods
61
61
  # -------------------------------------------------------------
62
- def get_connec_entities(last_synchronization)
62
+ def get_connec_entities(last_synchronization_date=nil)
63
63
  entities = ActiveSupport::HashWithIndifferentAccess.new
64
64
 
65
65
  self.class.connec_entities_names.each do |connec_entity_name|
66
66
  sub_entity_instance = instantiate_sub_entity_instance(connec_entity_name)
67
- entities[connec_entity_name] = sub_entity_instance.get_connec_entities(last_synchronization)
67
+ entities[connec_entity_name] = sub_entity_instance.get_connec_entities(last_synchronization_date)
68
68
  end
69
69
  entities
70
70
  end
71
71
 
72
- def get_external_entities_wrapper(last_synchronization)
72
+ def get_external_entities_wrapper(last_synchronization_date=nil)
73
73
  entities = ActiveSupport::HashWithIndifferentAccess.new
74
74
 
75
75
  self.class.external_entities_names.each do |external_entity_name|
76
76
  sub_entity_instance = instantiate_sub_entity_instance(external_entity_name)
77
- entities[external_entity_name] = sub_entity_instance.get_external_entities_wrapper(last_synchronization)
77
+ entities[external_entity_name] = sub_entity_instance.get_external_entities_wrapper(last_synchronization_date)
78
78
  end
79
79
  entities
80
80
  end
@@ -141,11 +141,11 @@ module Maestrano::Connector::Rails::Concerns::ComplexEntity
141
141
  end
142
142
  end
143
143
 
144
- def before_sync(last_synchronization)
144
+ def before_sync(last_synchronization_date)
145
145
  # Does nothing by default
146
146
  end
147
147
 
148
- def after_sync(last_synchronization)
148
+ def after_sync(last_synchronization_date)
149
149
  # Does nothing by default
150
150
  end
151
151
 
@@ -59,6 +59,10 @@ module Maestrano::Connector::Rails::Concerns::Entity
59
59
  raise "Not implemented"
60
60
  end
61
61
 
62
+ def creation_date_from_external_entity_hash(entity)
63
+ raise "Not implemented"
64
+ end
65
+
62
66
  # Return a string representing the object from a connec! entity hash
63
67
  def object_name_from_connec_entity_hash(entity)
64
68
  raise "Not implemented"
@@ -154,7 +158,7 @@ module Maestrano::Connector::Rails::Concerns::Entity
154
158
  # * full_sync
155
159
  # * $filter (see Connec! documentation)
156
160
  # * $orderby (see Connec! documentation)
157
- def get_connec_entities(last_synchronization)
161
+ def get_connec_entities(last_synchronization_date=nil)
158
162
  return [] if @opts[:skip_connec] || !self.class.can_read_connec?
159
163
 
160
164
  Maestrano::Connector::Rails::ConnectorLogger.log('info', @organization, "Fetching Connec! #{self.class.connec_entity_name}")
@@ -165,10 +169,10 @@ module Maestrano::Connector::Rails::Concerns::Entity
165
169
 
166
170
  # Fetch first page
167
171
  page_number = 0
168
- if last_synchronization.blank? || @opts[:full_sync]
172
+ if last_synchronization_date.blank? || @opts[:full_sync]
169
173
  query_params[:$filter] = @opts[:$filter] if @opts[:$filter]
170
174
  else
171
- query_params[:$filter] = "updated_at gt '#{last_synchronization.updated_at.iso8601}'" + (@opts[:$filter] ? " and #{@opts[:$filter]}" : '')
175
+ query_params[:$filter] = "updated_at gt '#{last_synchronization_date.iso8601}'" + (@opts[:$filter] ? " and #{@opts[:$filter]}" : '')
172
176
  end
173
177
 
174
178
  Maestrano::Connector::Rails::ConnectorLogger.log('debug', @organization, "entity=#{self.class.connec_entity_name}, fetching data with #{query_params.to_query}")
@@ -219,12 +223,12 @@ module Maestrano::Connector::Rails::Concerns::Entity
219
223
  # ----------------------------------------------
220
224
  # External methods
221
225
  # ----------------------------------------------
222
- def get_external_entities_wrapper(last_synchronization)
226
+ def get_external_entities_wrapper(last_synchronization_date=nil)
223
227
  return [] if @opts[:skip_external] || !self.class.can_read_external?
224
- get_external_entities(last_synchronization)
228
+ get_external_entities(last_synchronization_date)
225
229
  end
226
230
 
227
- def get_external_entities(last_synchronization)
231
+ def get_external_entities(last_synchronization_date=nil)
228
232
  Maestrano::Connector::Rails::ConnectorLogger.log('info', @organization, "Fetching #{Maestrano::Connector::Rails::External.external_name} #{self.class.external_entity_name.pluralize}")
229
233
  raise "Not implemented"
230
234
  end
@@ -319,6 +323,9 @@ module Maestrano::Connector::Rails::Concerns::Entity
319
323
 
320
324
  def consolidate_and_map_connec_entities(connec_entities, external_entities, references, external_entity_name)
321
325
  connec_entities.map{|entity|
326
+ # Entity has been created before date filtering limit
327
+ next nil if before_date_filtering_limit?(entity, false) && !@opts[:full_sync]
328
+
322
329
  entity = Maestrano::Connector::Rails::ConnecHelper.unfold_references(entity, references, @organization)
323
330
  next nil unless entity
324
331
  connec_id = entity.delete(:__connec_id)
@@ -332,7 +339,6 @@ module Maestrano::Connector::Rails::Concerns::Entity
332
339
  idmap.update(name: self.class.object_name_from_connec_entity_hash(entity))
333
340
 
334
341
  next nil if idmap.external_inactive || !idmap.to_external || (!@opts[:full_sync] && not_modified_since_last_push_to_external?(idmap, entity))
335
-
336
342
  # Check for conflict with entities from external
337
343
  solve_conflict(entity, external_entities, external_entity_name, idmap)
338
344
  }.compact
@@ -340,6 +346,9 @@ module Maestrano::Connector::Rails::Concerns::Entity
340
346
 
341
347
  def consolidate_and_map_external_entities(external_entities, connec_entity_name)
342
348
  external_entities.map{|entity|
349
+ # Entity has been created before date filtering limit
350
+ next nil if before_date_filtering_limit?(entity) && !@opts[:full_sync]
351
+
343
352
  entity_id = self.class.id_from_external_entity_hash(entity)
344
353
  idmap = self.class.find_or_create_idmap(external_id: entity_id, organization_id: @organization.id, connec_entity: connec_entity_name.downcase)
345
354
 
@@ -389,11 +398,11 @@ module Maestrano::Connector::Rails::Concerns::Entity
389
398
  # ----------------------------------------------
390
399
  # After and before sync
391
400
  # ----------------------------------------------
392
- def before_sync(last_synchronization)
401
+ def before_sync(last_synchronization_date)
393
402
  # Does nothing by default
394
403
  end
395
404
 
396
- def after_sync(last_synchronization)
405
+ def after_sync(last_synchronization_date)
397
406
  # Does nothing by default
398
407
  end
399
408
 
@@ -451,6 +460,10 @@ module Maestrano::Connector::Rails::Concerns::Entity
451
460
  not_modified
452
461
  end
453
462
 
463
+ def before_date_filtering_limit?(entity, external=true)
464
+ @organization.date_filtering_limit && @organization.date_filtering_limit > (external ? self.class.creation_date_from_external_entity_hash(entity) : entity['created_at'])
465
+ end
466
+
454
467
  def is_connec_more_recent?(connec_entity, external_entity)
455
468
  connec_entity['updated_at'] > self.class.last_update_date_from_external_entity_hash(external_entity)
456
469
  end
@@ -465,11 +478,11 @@ module Maestrano::Connector::Rails::Concerns::Entity
465
478
  end
466
479
 
467
480
  if keep_connec
468
- Maestrano::Connector::Rails::ConnectorLogger.log('info', @organization, "Conflict between #{Maestrano::Connector::Rails::External::external_name} #{external_entity_name} #{external_entity} and Connec! #{self.class.connec_entity_name} #{connec_entity}. Entity from external kept")
481
+ Maestrano::Connector::Rails::ConnectorLogger.log('info', @organization, "Conflict between #{Maestrano::Connector::Rails::External::external_name} #{external_entity_name} #{external_entity} and Connec! #{self.class.connec_entity_name} #{connec_entity}. Entity from Connec! kept")
469
482
  external_entities.delete(external_entity)
470
483
  map_connec_entity_with_idmap(connec_entity, external_entity_name, idmap)
471
484
  else
472
- Maestrano::Connector::Rails::ConnectorLogger.log('info', @organization, "Conflict between #{Maestrano::Connector::Rails::External::external_name} #{external_entity_name} #{external_entity} and Connec! #{self.class.connec_entity_name} #{connec_entity}. Entity from Connec! kept")
485
+ Maestrano::Connector::Rails::ConnectorLogger.log('info', @organization, "Conflict between #{Maestrano::Connector::Rails::External::external_name} #{external_entity_name} #{external_entity} and Connec! #{self.class.connec_entity_name} #{connec_entity}. Entity from external kept")
473
486
  nil
474
487
  end
475
488
 
@@ -20,8 +20,9 @@ module Maestrano::Connector::Rails
20
20
  #===================================
21
21
  # Encryptions
22
22
  #===================================
23
- # attr_encrypted :oauth_token, key: ::Settings.encryption_key
24
- # attr_encrypted :refresh_token, key: ::Settings.encryption_key
23
+ attr_encrypted_options.merge!(:mode => :per_attribute_iv_and_salt)
24
+ attr_encrypted :oauth_token, key: ::Settings.encryption_key1
25
+ attr_encrypted :refresh_token, key: ::Settings.encryption_key2
25
26
 
26
27
  #===================================
27
28
  # Associations
@@ -74,5 +75,9 @@ module Maestrano::Connector::Rails
74
75
  def last_successful_synchronization
75
76
  self.synchronizations.where(status: 'SUCCESS', partial: false).order(updated_at: :desc).first
76
77
  end
78
+
79
+ def last_synchronization_date
80
+ (last_successful_synchronization && last_successful_synchronization.updated_at) || date_filtering_limit
81
+ end
77
82
  end
78
83
  end
@@ -41,7 +41,7 @@ module Maestrano::Connector::Rails
41
41
  def clean_synchronizations
42
42
  count = self.organization.synchronizations.count
43
43
  if count > 100
44
- self.organization.synchronizations.limit(count - 100).destroy_all
44
+ self.organization.synchronizations.order('id ASC').limit(count - 100).destroy_all
45
45
  end
46
46
  end
47
47
  end
@@ -0,0 +1,11 @@
1
+ class AddDateFilteringLimitToOrganization < ActiveRecord::Migration
2
+ def change
3
+ add_column :organizations, :date_filtering_limit, :datetime
4
+ add_column :organizations, :historical_data, :boolean, default: false
5
+
6
+ # Set historical data to true for organization existing before the feature
7
+ Maestrano::Connector::Rails::Organization.all.each do |o|
8
+ o.update(historical_data: true)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ class AddEncryptionOnOauthKeys < ActiveRecord::Migration
2
+ def change
3
+ tokens = Maestrano::Connector::Rails::Organization.map{|o|
4
+ {id: o.id, oauth_token: o.oauth_token, refresh_token: o.refresh_token}
5
+ }
6
+
7
+ rename_column :organizations, :oauth_token, :encrypted_oauth_token
8
+ add_column :organizations, :encrypted_oauth_token_iv, :string
9
+ add_column :organizations, :encrypted_oauth_token_salt, :string
10
+ rename_column :organizations, :refresh_token, :encrypted_refresh_token
11
+ add_column :organizations, :encrypted_refresh_token_iv, :string
12
+ add_column :organizations, :encrypted_refresh_token_salt, :string
13
+
14
+ tokens.each do |token|
15
+ o = Maestrano::Connector::Rails::Organization.find(token[:id])
16
+ o.update(oauth_token: token[:oauth_token], refresh_token: token[:refresh_token])
17
+ end
18
+ end
19
+ end
@@ -38,6 +38,7 @@ module Connector
38
38
  copy_file 'home_controller.rb', 'app/controllers/home_controller.rb'
39
39
  copy_file 'home_controller_spec.rb', 'spec/controllers/home_controller_spec.rb'
40
40
  copy_file 'home_index.haml', 'app/views/home/index.html.haml'
41
+ copy_file 'home.js', 'app/assets/javascripts/home.js'
41
42
 
42
43
  copy_file 'synchronizations_controller.rb', 'app/controllers/synchronizations_controller.rb'
43
44
  copy_file 'synchronizations_controller_spec.rb', 'spec/controllers/synchronizations_controller_spec.rb'
@@ -1,7 +1,7 @@
1
1
  # TODO
2
2
  # This file is provided as an example and should be removed
3
3
  # See README for explanation
4
- # class Enities::SubEntities::Person < Maestrano::Connector::Rails::SubEntityBase
4
+ # class Entities::SubEntities::Person < Maestrano::Connector::Rails::SubEntityBase
5
5
  # def self.external?
6
6
  # false
7
7
  # end
@@ -8,11 +8,11 @@ class Maestrano::Connector::Rails::Entity
8
8
  # * @opts
9
9
 
10
10
  # Return an array of entities from the external app
11
- def get_external_entities(last_synchronization)
11
+ def get_external_entities(last_synchronization_date=nil)
12
12
  Maestrano::Connector::Rails::ConnectorLogger.log('info', @organization, "Fetching #{Maestrano::Connector::Rails::External.external_name} #{self.class.external_entity_name.pluralize}")
13
13
  # TODO
14
- # This method should return only entities that have been updated since the last_synchronization
15
- # It should also implements an option to do a full synchronization when @opts[:full_sync] == true or when there is no last_synchronization
14
+ # This method should return only entities that have been updated since the last_synchronization_date
15
+ # It should also implements an option to do a full synchronization when @opts[:full_sync] == true or when there is no last_synchronization_date
16
16
  # Maestrano::Connector::Rails::ConnectorLogger.log('info', @organization, "Received data: Source=#{Maestrano::Connector::Rails::External.external_name}, Entity=#{self.class.external_entity_name}, Response=#{entities}")
17
17
  end
18
18
 
@@ -40,6 +40,12 @@ class Maestrano::Connector::Rails::Entity
40
40
  # e.g entity['last_update']
41
41
  end
42
42
 
43
+ def self.creation_date_from_external_entity_hash(entity)
44
+ # TODO
45
+ # This method return the creation date from an external_entity_hash
46
+ # e.g entity['created_at']
47
+ end
48
+
43
49
  def self.inactive_from_external_entity_hash?(entity)
44
50
  # TODO
45
51
  # This method return true is entity is inactive in the external application
@@ -0,0 +1,11 @@
1
+ function historicalDataDisplay()
2
+ {
3
+ if (document.getElementById('historical-data').checked)
4
+ {
5
+ document.getElementById('historical-data-display-checked').style.display = 'block';
6
+ document.getElementById('historical-data-display-unchecked').style.display = 'none';
7
+ } else {
8
+ document.getElementById('historical-data-display-unchecked').style.display = 'block';
9
+ document.getElementById('historical-data-display-checked').style.display = 'none';
10
+ }
11
+ }
@@ -13,9 +13,20 @@ class HomeController < ApplicationController
13
13
  organization.synchronized_entities[entity] = !!params["#{entity}"]
14
14
  end
15
15
  organization.sync_enabled = organization.synchronized_entities.values.any?
16
+
17
+ unless organization.historical_data
18
+ historical_data = !!params['historical-data']
19
+ if historical_data
20
+ organization.date_filtering_limit = nil
21
+ organization.historical_data = true
22
+ else
23
+ organization.date_filtering_limit ||= Time.now
24
+ end
25
+ end
26
+
16
27
  organization.save
17
28
 
18
- if !old_sync_state
29
+ if !old_sync_state && organization.sync_enabled
19
30
  Maestrano::Connector::Rails::SynchronizationJob.perform_later(organization, {})
20
31
  flash[:info] = 'Congrats, you\'re all set up! Your data are now being synced'
21
32
  end
@@ -57,6 +57,17 @@
57
57
  .col-md-5.text-right
58
58
  - if v && @organization.oauth_uid && @organization.sync_enabled
59
59
  = link_to "Force a synchronization for #{k.to_s.humanize.pluralize} only", home_synchronize_path(opts: {only_entities: [k.to_s]}), method: :post, class: "btn btn-warning btn-sm"
60
+ .spacer1
61
+ .row
62
+ .col-md-4
63
+ %label{:for => 'historical-data'} Synchronize my historical data
64
+ .col-md-2
65
+ %input{type: 'checkbox', id: 'historical-data', name: 'historical-data', checked: @organization.historical_data, onchange: 'historicalDataDisplay();', disabled: @organization.historical_data}
66
+ .col-md-6
67
+ %small#historical-data-display-unchecked{style: "display: #{@organization.historical_data ? 'none' : 'block'}"} Only data created after #{(@organization.date_filtering_limit && @organization.date_filtering_limit.utc || Time.now.utc).to_formatted_s(:long_ordinal)} will be synchronized
68
+ %small#historical-data-display-checked{style: "display: #{!@organization.historical_data ? 'none' : 'block'}"}
69
+ Synchronizing your historical data will share all data in ApplicationName. This action is not reversible. Want to know more? Check
70
+ = link_to 'here', 'https://maestrano.atlassian.net/wiki/display/UKB/How+Connec%21+manages+Historical+Data+Sharing'
60
71
  .spacer1
61
72
  .row
62
73
  .col-md-2.col-md-offset-10.text-center.link-step-action
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: maestrano-connector-rails 1.0.4 ruby lib
5
+ # stub: maestrano-connector-rails 1.1.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "maestrano-connector-rails"
9
- s.version = "1.0.4"
9
+ s.version = "1.1.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["Pierre Berard"]
14
- s.date = "2016-06-07"
14
+ s.date = "2016-06-17"
15
15
  s.description = "Maestrano is the next generation marketplace for SME applications. See https://maestrano.com for details."
16
16
  s.email = "pierre.berard@maestrano.com"
17
17
  s.executables = ["rails"]
@@ -56,7 +56,6 @@ Gem::Specification.new do |s|
56
56
  "app/models/maestrano/connector/rails/user_organization_rel.rb",
57
57
  "bin/rails",
58
58
  "config/routes.rb",
59
- "db/20160524112054_add_encryption_on_oauth_keys.rb",
60
59
  "db/migrate/20151122162100_create_users.rb",
61
60
  "db/migrate/20151122162414_create_organizations.rb",
62
61
  "db/migrate/20151122162613_create_user_organization_rels.rb",
@@ -65,6 +64,8 @@ Gem::Specification.new do |s|
65
64
  "db/migrate/20160205132857_add_sync_enabled_to_organizations.rb",
66
65
  "db/migrate/20160215103120_add_name_to_id_map.rb",
67
66
  "db/migrate/20160427112250_add_inactive_to_idmaps.rb",
67
+ "db/migrate/20160614114401_add_date_filtering_limit_to_organization.rb",
68
+ "db/migrate/20160614160654_add_encryption_on_oauth_keys.rb",
68
69
  "lib/generators/connector/USAGE",
69
70
  "lib/generators/connector/complex_entity_generator.rb",
70
71
  "lib/generators/connector/install_generator.rb",
@@ -78,6 +79,7 @@ Gem::Specification.new do |s|
78
79
  "lib/generators/connector/templates/example_entity.rb",
79
80
  "lib/generators/connector/templates/example_entity_spec.rb",
80
81
  "lib/generators/connector/templates/external.rb",
82
+ "lib/generators/connector/templates/home.js",
81
83
  "lib/generators/connector/templates/home_controller.rb",
82
84
  "lib/generators/connector/templates/home_controller_spec.rb",
83
85
  "lib/generators/connector/templates/home_index.haml",
@@ -145,6 +147,11 @@ Gem::Specification.new do |s|
145
147
  "spec/dummy/config/locales/en.yml",
146
148
  "spec/dummy/config/routes.rb",
147
149
  "spec/dummy/config/secrets.yml",
150
+ "spec/dummy/config/settings.yml",
151
+ "spec/dummy/config/settings/development.yml",
152
+ "spec/dummy/config/settings/production.yml",
153
+ "spec/dummy/config/settings/test.yml",
154
+ "spec/dummy/config/settings/uat.yml",
148
155
  "spec/dummy/db/schema.rb",
149
156
  "spec/dummy/lib/assets/.keep",
150
157
  "spec/dummy/log/.keep",
@@ -174,6 +181,7 @@ Gem::Specification.new do |s|
174
181
  "spec/routing/connec_routing_spec.rb",
175
182
  "spec/spec_helper.rb",
176
183
  "template/Procfile",
184
+ "template/application.yml.sample",
177
185
  "template/database.yml",
178
186
  "template/factories.rb",
179
187
  "template/gitignore",
@@ -204,6 +212,8 @@ Gem::Specification.new do |s|
204
212
  s.add_runtime_dependency(%q<haml-rails>, [">= 0"])
205
213
  s.add_runtime_dependency(%q<bootstrap-sass>, [">= 0"])
206
214
  s.add_runtime_dependency(%q<autoprefixer-rails>, [">= 0"])
215
+ s.add_runtime_dependency(%q<attr_encrypted>, ["~> 1.4.0"])
216
+ s.add_runtime_dependency(%q<config>, [">= 0"])
207
217
  s.add_development_dependency(%q<shoulda>, [">= 0"])
208
218
  s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
209
219
  s.add_development_dependency(%q<bundler>, ["~> 1.0"])
@@ -213,6 +223,7 @@ Gem::Specification.new do |s|
213
223
  s.add_development_dependency(%q<factory_girl_rails>, [">= 0"])
214
224
  s.add_development_dependency(%q<sqlite3>, [">= 0"])
215
225
  s.add_development_dependency(%q<shoulda-matchers>, [">= 0"])
226
+ s.add_development_dependency(%q<timecop>, [">= 0"])
216
227
  else
217
228
  s.add_dependency(%q<maestrano-rails>, [">= 0"])
218
229
  s.add_dependency(%q<hash_mapper>, [">= 0"])
@@ -220,6 +231,8 @@ Gem::Specification.new do |s|
220
231
  s.add_dependency(%q<haml-rails>, [">= 0"])
221
232
  s.add_dependency(%q<bootstrap-sass>, [">= 0"])
222
233
  s.add_dependency(%q<autoprefixer-rails>, [">= 0"])
234
+ s.add_dependency(%q<attr_encrypted>, ["~> 1.4.0"])
235
+ s.add_dependency(%q<config>, [">= 0"])
223
236
  s.add_dependency(%q<shoulda>, [">= 0"])
224
237
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
225
238
  s.add_dependency(%q<bundler>, ["~> 1.0"])
@@ -229,6 +242,7 @@ Gem::Specification.new do |s|
229
242
  s.add_dependency(%q<factory_girl_rails>, [">= 0"])
230
243
  s.add_dependency(%q<sqlite3>, [">= 0"])
231
244
  s.add_dependency(%q<shoulda-matchers>, [">= 0"])
245
+ s.add_dependency(%q<timecop>, [">= 0"])
232
246
  end
233
247
  else
234
248
  s.add_dependency(%q<maestrano-rails>, [">= 0"])
@@ -237,6 +251,8 @@ Gem::Specification.new do |s|
237
251
  s.add_dependency(%q<haml-rails>, [">= 0"])
238
252
  s.add_dependency(%q<bootstrap-sass>, [">= 0"])
239
253
  s.add_dependency(%q<autoprefixer-rails>, [">= 0"])
254
+ s.add_dependency(%q<attr_encrypted>, ["~> 1.4.0"])
255
+ s.add_dependency(%q<config>, [">= 0"])
240
256
  s.add_dependency(%q<shoulda>, [">= 0"])
241
257
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
242
258
  s.add_dependency(%q<bundler>, ["~> 1.0"])
@@ -246,6 +262,7 @@ Gem::Specification.new do |s|
246
262
  s.add_dependency(%q<factory_girl_rails>, [">= 0"])
247
263
  s.add_dependency(%q<sqlite3>, [">= 0"])
248
264
  s.add_dependency(%q<shoulda-matchers>, [">= 0"])
265
+ s.add_dependency(%q<timecop>, [">= 0"])
249
266
  end
250
267
  end
251
268
 
@@ -1,3 +1,9 @@
1
+ ## 1.0.4
2
+
3
+ ### Fixes
4
+ * Fix uneeded creation of idmap for Connec! entities following a failure
5
+ * Fix Connec! pagination
6
+
1
7
  ## 1.0.3
2
8
 
3
9
  ### Fixes
@@ -0,0 +1,37 @@
1
+ encryption_key1: <%= ENV['encryption_key1'] %>
2
+ encryption_key2: <%= ENV['encryption_key2'] %>
3
+
4
+ # tenant config
5
+ default:
6
+ environment: 'production'
7
+ x509_certificate: "-----BEGIN CERTIFICATE-----\nMIIDezCCAuSgAwIBAgIJAPFpcH2rW0pyMA0GCSqGSIb3DQEBBQUAMIGGMQswCQYD\nVQQGEwJBVTEMMAoGA1UECBMDTlNXMQ8wDQYDVQQHEwZTeWRuZXkxGjAYBgNVBAoT\nEU1hZXN0cmFubyBQdHkgTHRkMRYwFAYDVQQDEw1tYWVzdHJhbm8uY29tMSQwIgYJ\nKoZIhvcNAQkBFhVzdXBwb3J0QG1hZXN0cmFuby5jb20wHhcNMTQwMTA0MDUyNDEw\nWhcNMzMxMjMwMDUyNDEwWjCBhjELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEP\nMA0GA1UEBxMGU3lkbmV5MRowGAYDVQQKExFNYWVzdHJhbm8gUHR5IEx0ZDEWMBQG\nA1UEAxMNbWFlc3RyYW5vLmNvbTEkMCIGCSqGSIb3DQEJARYVc3VwcG9ydEBtYWVz\ndHJhbm8uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD3feNNn2xfEz5/\nQvkBIu2keh9NNhobpre8U4r1qC7h7OeInTldmxGL4cLHw4ZAqKbJVrlFWqNevM5V\nZBkDe4mjuVkK6rYK1ZK7eVk59BicRksVKRmdhXbANk/C5sESUsQv1wLZyrF5Iq8m\na9Oy4oYrIsEF2uHzCouTKM5n+O4DkwIDAQABo4HuMIHrMB0GA1UdDgQWBBSd/X0L\n/Pq+ZkHvItMtLnxMCAMdhjCBuwYDVR0jBIGzMIGwgBSd/X0L/Pq+ZkHvItMtLnxM\nCAMdhqGBjKSBiTCBhjELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEPMA0GA1UE\nBxMGU3lkbmV5MRowGAYDVQQKExFNYWVzdHJhbm8gUHR5IEx0ZDEWMBQGA1UEAxMN\nbWFlc3RyYW5vLmNvbTEkMCIGCSqGSIb3DQEJARYVc3VwcG9ydEBtYWVzdHJhbm8u\nY29tggkA8WlwfatbSnIwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQDE\nhe/18oRh8EqIhOl0bPk6BG49AkjhZZezrRJkCFp4dZxaBjwZTddwo8O5KHwkFGdy\nyLiPV326dtvXoKa9RFJvoJiSTQLEn5mO1NzWYnBMLtrDWojOe6Ltvn3x0HVo/iHh\nJShjAn6ZYX43Tjl1YXDd1H9O+7/VgEWAQQ32v8p5lA==\n-----END CERTIFICATE-----"
8
+ x509_fingerprint: '2f:57:71:e4:40:19:57:37:a6:2c:f0:c5:82:52:2f:2e:41:b7:9d:7e'
9
+ api_id: 'connec_api_id'
10
+ api_key: 'connec_api_key'
11
+ sso_init_path: '/maestrano/auth/saml/init/default'
12
+ sso_consume_path: '/maestrano/auth/saml/consume/default'
13
+ api_host: 'https://maestrano.com'
14
+ connec_host: 'https://api-connec.maestrano.com'
15
+ webhook:
16
+ account:
17
+ groups_path: '/maestrano/account/groups/:id/default'
18
+ group_users_path: '/maestrano/account/groups/:group_id/users/:id/default'
19
+ connec:
20
+ notifications_path: '/maestrano/connec/notifications/default'
21
+
22
+ maestrano-uat:
23
+ environment: 'uat'
24
+ x509_certificate: "-----BEGIN CERTIFICATE-----\nMIIDezCCAuSgAwIBAgIJAMzy+weDPp7qMA0GCSqGSIb3DQEBBQUAMIGGMQswCQYD\nVQQGEwJBVTEMMAoGA1UECBMDTlNXMQ8wDQYDVQQHEwZTeWRuZXkxGjAYBgNVBAoT\nEU1hZXN0cmFubyBQdHkgTHRkMRYwFAYDVQQDEw1tYWVzdHJhbm8uY29tMSQwIgYJ\nKoZIhvcNAQkBFhVzdXBwb3J0QG1hZXN0cmFuby5jb20wHhcNMTQwMTA0MDUyMzE0\nWhcNMzMxMjMwMDUyMzE0WjCBhjELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEP\nMA0GA1UEBxMGU3lkbmV5MRowGAYDVQQKExFNYWVzdHJhbm8gUHR5IEx0ZDEWMBQG\nA1UEAxMNbWFlc3RyYW5vLmNvbTEkMCIGCSqGSIb3DQEJARYVc3VwcG9ydEBtYWVz\ndHJhbm8uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+2uyQeAOc/iro\nhCyT33RkkWfTGeJ8E/mu9F5ORWoCZ/h2J+QDuzuc69Rf1LoO4wZVQ8LBeWOqMBYz\notYFUIPlPfIBXDNL/stHkpg28WLDpoJM+46WpTAgp89YKgwdAoYODHiUOcO/uXOO\n2i9Ekoa+kxbvBzDJf7uuR/io6GERXwIDAQABo4HuMIHrMB0GA1UdDgQWBBTGRDBT\nie5+fHkB0+SZ5g3WY/D2RTCBuwYDVR0jBIGzMIGwgBTGRDBTie5+fHkB0+SZ5g3W\nY/D2RaGBjKSBiTCBhjELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEPMA0GA1UE\nBxMGU3lkbmV5MRowGAYDVQQKExFNYWVzdHJhbm8gUHR5IEx0ZDEWMBQGA1UEAxMN\nbWFlc3RyYW5vLmNvbTEkMCIGCSqGSIb3DQEJARYVc3VwcG9ydEBtYWVzdHJhbm8u\nY29tggkAzPL7B4M+nuowDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAw\nRxg3rZrML//xbsS3FFXguzXiiNQAvA4KrMWhGh3jVrtzAlN1/okFNy6zuN8gzdKD\nYw2n0c/u3cSpUutIVZOkwQuPCMC1hoP7Ilat6icVewNcHayLBxKgRxpBhr5Sc4av\n3HOW5Bi/eyC7IjeBTbTnpziApEC7uUsBou2rlKmTGw==\n-----END CERTIFICATE-----"
25
+ x509_fingerprint: '8a:1e:2e:76:c4:67:80:68:6c:81:18:f7:d3:29:5d:77:f8:79:54:2f'
26
+ api_id: 'maestrano_uat_connec_api_id'
27
+ api_key: 'maestrano_uat_connec_api_key'
28
+ sso_consume_path: '/maestrano/auth/saml/consume/maestrano-uat'
29
+ sso_init_path: '/maestrano/auth/saml/init/maestrano-uat'
30
+ api_host: 'https://uat.maestrano.io'
31
+ connec_host: 'https://api-connec-uat.maestrano.io'
32
+ webhook:
33
+ account:
34
+ groups_path: '/maestrano/account/groups/:id/maestrano-uat'
35
+ group_users_path: '/maestrano/account/groups/:group_id/users/:id/maestrano-uat'
36
+ connec:
37
+ notifications_path: '/maestrano/connec/notifications/maestrano-uat'
@@ -0,0 +1,12 @@
1
+ app_host: 'http://localhost:3001/'
2
+
3
+ encryption_key1: 'This is a key that is 256 bits!!'
4
+ encryption_key2: 'This is a key that is 256 bits!!'
5
+
6
+ default:
7
+ api_host: 'https://maestrano.com'
8
+ connec_host: 'https://api-connec.maestrano.com'
9
+
10
+ maestrano-uat:
11
+ api_host: 'https://uat.maestrano.io'
12
+ connec_host: 'https://api-connec-uat.maestrano.io'
@@ -0,0 +1 @@
1
+ app_host: 'https://connector-xxx.herokuapp.com/'
@@ -0,0 +1,12 @@
1
+ app_host: 'http://localhost:3001/'
2
+
3
+ encryption_key1: 'This is a key that is 256 bits!!'
4
+ encryption_key2: 'This is a key that is 256 bits!!'
5
+
6
+ default:
7
+ environment: 'local'
8
+ api_host: 'http://localhost:3000'
9
+
10
+ maestrano-uat:
11
+ environment: 'local'
12
+ api_host: 'http://localhost:3000'
@@ -0,0 +1 @@
1
+ app_host: 'https://connector-xxx-uat.herokuapp.com/'
@@ -11,7 +11,7 @@
11
11
  #
12
12
  # It's strongly recommended that you check this file into your version control system.
13
13
 
14
- ActiveRecord::Schema.define(version: 20160427120963) do
14
+ ActiveRecord::Schema.define(version: 20160614152139) do
15
15
 
16
16
  create_table "id_maps", force: :cascade do |t|
17
17
  t.string "connec_id"
@@ -42,13 +42,18 @@ ActiveRecord::Schema.define(version: 20160427120963) do
42
42
  t.string "oauth_provider"
43
43
  t.string "oauth_uid"
44
44
  t.string "oauth_name"
45
- t.string "oauth_token"
46
- t.string "refresh_token"
45
+ t.string "encrypted_oauth_token"
46
+ t.string "encrypted_refresh_token"
47
47
  t.string "instance_url"
48
48
  t.string "synchronized_entities"
49
- t.datetime "created_at", null: false
50
- t.datetime "updated_at", null: false
51
- t.boolean "sync_enabled", default: false
49
+ t.datetime "created_at", null: false
50
+ t.datetime "updated_at", null: false
51
+ t.boolean "sync_enabled", default: false
52
+ t.datetime "date_filtering_limit"
53
+ t.string "encrypted_oauth_token_iv"
54
+ t.string "encrypted_oauth_token_salt"
55
+ t.string "encrypted_refresh_token_iv"
56
+ t.string "encrypted_refresh_token_salt"
52
57
  end
53
58
 
54
59
  add_index "organizations", ["uid", "tenant"], name: "orga_uid_index"
@@ -64,7 +64,9 @@ describe 'connec to the external application' do
64
64
  ],
65
65
  "is_customer" => false,
66
66
  "is_supplier" => true,
67
- "is_lead" => false
67
+ "is_lead" => false,
68
+ "updated_at" => 2.day.ago,
69
+ "created_at" => 2.day.ago
68
70
  }
69
71
  }
70
72
  let(:person) { person1 }
@@ -77,7 +79,7 @@ describe 'connec to the external application' do
77
79
  allow_any_instance_of(Entities::ConnecToExternal).to receive(:get_external_entities).and_return([])
78
80
  }
79
81
 
80
- subject { Maestrano::Connector::Rails::SynchronizationJob.new.sync_entity('connec_to_external', organization, connec_client, external_client, nil, {}) }
82
+ subject { Maestrano::Connector::Rails::SynchronizationJob.new.sync_entity('connec_to_external', organization, connec_client, external_client, organization.last_synchronization_date, {}) }
81
83
 
82
84
  describe 'a new record created in connec with all references known' do
83
85
  before {
@@ -185,4 +187,24 @@ describe 'connec to the external application' do
185
187
  }.to_not change{ Maestrano::Connector::Rails::IdMap.count }
186
188
  end
187
189
  end
190
+
191
+ describe 'an entity from before the date filtering limit' do
192
+ let(:date_filtering_limit) { 2.minute.ago }
193
+ before {
194
+ organization.update(date_filtering_limit: date_filtering_limit)
195
+ }
196
+
197
+ it 'calls get_connec_entities with a date even if there is no last sync' do
198
+ expect_any_instance_of(Entities::ConnecToExternal).to receive(:get_connec_entities).with(date_filtering_limit).and_return([])
199
+ subject
200
+ end
201
+
202
+ it 'pushes nothing and creates no idmap' do
203
+ expect_any_instance_of(Entities::ConnecToExternal).to_not receive(:create_external_entity)
204
+ expect_any_instance_of(Entities::ConnecToExternal).to_not receive(:update_external_entity)
205
+ expect{
206
+ subject
207
+ }.to_not change{ Maestrano::Connector::Rails::IdMap.count }
208
+ end
209
+ end
188
210
  end
@@ -76,6 +76,10 @@ describe Maestrano::Connector::Rails::Entity do
76
76
  it { expect{ subject.last_update_date_from_external_entity_hash(nil) }.to raise_error('Not implemented') }
77
77
  end
78
78
 
79
+ describe 'creation_date_from_external_entity_hash' do
80
+ it { expect{ subject.creation_date_from_external_entity_hash(nil) }.to raise_error('Not implemented') }
81
+ end
82
+
79
83
  # Entity specific methods
80
84
  describe 'singleton?' do
81
85
  it 'is false by default' do
@@ -166,7 +170,7 @@ describe Maestrano::Connector::Rails::Entity do
166
170
 
167
171
  # Connec! methods
168
172
  describe 'connec_methods' do
169
- let(:sync) { create(:synchronization) }
173
+ let(:sync) { create(:synchronization, organization: organization) }
170
174
 
171
175
  describe 'filter_connec_entities' do
172
176
  it 'does nothing by default' do
@@ -211,7 +215,7 @@ describe Maestrano::Connector::Rails::Entity do
211
215
  let(:opts) { {full_sync: true} }
212
216
  it 'performs a full get' do
213
217
  expect(connec_client).to receive(:get).with("#{connec_name.downcase.pluralize}?")
214
- subject.get_connec_entities(sync)
218
+ subject.get_connec_entities(sync.updated_at)
215
219
  end
216
220
  end
217
221
 
@@ -226,7 +230,7 @@ describe Maestrano::Connector::Rails::Entity do
226
230
  it 'performs a time limited get' do
227
231
  uri_param = {"$filter" => "updated_at gt '#{sync.updated_at.iso8601}'"}.to_query
228
232
  expect(connec_client).to receive(:get).with("#{connec_name.downcase.pluralize}?#{uri_param}")
229
- subject.get_connec_entities(sync)
233
+ subject.get_connec_entities(sync.updated_at)
230
234
  end
231
235
  end
232
236
 
@@ -235,21 +239,21 @@ describe Maestrano::Connector::Rails::Entity do
235
239
  subject.instance_variable_set(:@opts, {full_sync: true, :$filter => "code eq 'PEO12'"})
236
240
  uri_param = {'$filter'=>'code eq \'PEO12\''}.to_query
237
241
  expect(connec_client).to receive(:get).with("#{connec_name.downcase.pluralize}?#{uri_param}")
238
- subject.get_connec_entities(sync)
242
+ subject.get_connec_entities(sync.updated_at)
239
243
  end
240
244
 
241
245
  it 'support filter option for time limited sync' do
242
246
  subject.instance_variable_set(:@opts, {:$filter => "code eq 'PEO12'"})
243
247
  uri_param = {"$filter"=>"updated_at gt '#{sync.updated_at.iso8601}' and code eq 'PEO12'"}.to_query
244
248
  expect(connec_client).to receive(:get).with("#{connec_name.downcase.pluralize}?#{uri_param}")
245
- subject.get_connec_entities(sync)
249
+ subject.get_connec_entities(sync.updated_at)
246
250
  end
247
251
 
248
252
  it 'support orderby option for time limited sync' do
249
253
  subject.instance_variable_set(:@opts, {:$orderby => "name asc"})
250
254
  uri_param = {"$orderby"=>"name asc", "$filter"=>"updated_at gt '#{sync.updated_at.iso8601}'"}.to_query
251
255
  expect(connec_client).to receive(:get).with("#{connec_name.downcase.pluralize}?#{uri_param}")
252
- subject.get_connec_entities(sync)
256
+ subject.get_connec_entities(sync.updated_at)
253
257
  end
254
258
  end
255
259
 
@@ -620,6 +624,7 @@ describe Maestrano::Connector::Rails::Entity do
620
624
  before {
621
625
  allow(subject.class).to receive(:id_from_external_entity_hash).and_return(id)
622
626
  allow(subject.class).to receive(:last_update_date_from_external_entity_hash).and_return(date)
627
+ allow(subject.class).to receive(:creation_date_from_external_entity_hash).and_return(date)
623
628
  }
624
629
 
625
630
  describe 'consolidate_and_map_data' do
@@ -726,8 +731,8 @@ describe Maestrano::Connector::Rails::Entity do
726
731
  let(:id2) { nil }
727
732
  let(:connec_id1) { 'connec-id-1' }
728
733
  let(:connec_id2) { 'connec-id-2' }
729
- let(:entity1) { {'id' => id1, 'name' => 'John', 'updated_at' => date} }
730
- let(:entity2) { {'id' => id2, 'name' => 'Jane', 'updated_at' => date} }
734
+ let(:entity1) { {'id' => id1, 'name' => 'John', 'updated_at' => date, 'created_at' => date} }
735
+ let(:entity2) { {'id' => id2, 'name' => 'Jane', 'updated_at' => date, 'created_at' => date} }
731
736
  let(:entity_without_refs) { {} }
732
737
  let(:entities) { [entity1, entity2] }
733
738
  before {
@@ -788,6 +793,24 @@ describe Maestrano::Connector::Rails::Entity do
788
793
  end
789
794
  end
790
795
 
796
+ context 'when before date_filtering_limit' do
797
+ before {
798
+ organization.update(date_filtering_limit: 5.minutes.ago)
799
+ }
800
+
801
+ it 'discards the entity' do
802
+ expect(subject.consolidate_and_map_connec_entities(entities, [], [], external_name)).to eql([])
803
+ end
804
+
805
+ context 'with full synchronization opts' do
806
+ let(:opts) { {full_sync: true} }
807
+
808
+ it 'keeps the entity' do
809
+ expect(subject.consolidate_and_map_connec_entities(entities, [], [], external_name)).to eql([{entity: {mapped: 'entity'}, idmap: idmap1}])
810
+ end
811
+ end
812
+ end
813
+
791
814
  end
792
815
 
793
816
  context 'when conflict' do
@@ -906,6 +929,24 @@ describe Maestrano::Connector::Rails::Entity do
906
929
  end
907
930
  end
908
931
 
932
+ context 'when before date_filtering_limit' do
933
+ before {
934
+ organization.update(date_filtering_limit: 5.minutes.ago)
935
+ }
936
+
937
+ it 'discards the entity' do
938
+ expect(subject.consolidate_and_map_external_entities([entity], connec_name)).to eql([])
939
+ end
940
+
941
+ context 'with full synchronization opts' do
942
+ let(:opts) { {full_sync: true} }
943
+
944
+ it 'keeps the entity' do
945
+ expect(subject.consolidate_and_map_external_entities([entity], connec_name)).to eql([{entity: {mapped: 'ext_entity'}, idmap: idmap}])
946
+ end
947
+ end
948
+ end
949
+
909
950
  end
910
951
  end
911
952
  end
@@ -137,12 +137,45 @@ describe Maestrano::Connector::Rails::Organization do
137
137
  describe 'last_successful_synchronization' do
138
138
  let!(:running_sync) { create(:synchronization, organization: subject, status: 'RUNNING') }
139
139
  let!(:failed_sync) { create(:synchronization, organization: subject, status: 'ERROR') }
140
- let!(:success_sync) { create(:synchronization, organization: subject, status: 'SUCCESS') }
140
+ let!(:success_sync) { create(:synchronization, organization: subject, status: 'SUCCESS', updated_at: 1.minute.ago) }
141
141
  let!(:success_sync2) { create(:synchronization, organization: subject, status: 'SUCCESS', updated_at: 3.hours.ago) }
142
142
  let!(:partial) { create(:synchronization, organization: subject, status: 'SUCCESS', partial: true) }
143
143
 
144
144
  it { expect(subject.last_successful_synchronization).to eql(success_sync) }
145
145
  end
146
+
147
+ describe 'last_synchronization_date' do
148
+ let(:date) { 2.days.ago }
149
+
150
+ context 'with date_filtering_limit' do
151
+ before {
152
+ subject.date_filtering_limit = date
153
+ }
154
+
155
+ it { expect(subject.last_synchronization_date).to eql(date) }
156
+ end
157
+
158
+ context 'with sync' do
159
+ let!(:success_sync) { create(:synchronization, organization: subject, status: 'SUCCESS') }
160
+
161
+ it { expect(subject.last_synchronization_date.to_date).to eql(success_sync.updated_at.to_date) }
162
+ end
163
+
164
+ context 'with both' do
165
+ let!(:success_sync) { create(:synchronization, organization: subject, status: 'SUCCESS') }
166
+ before {
167
+ subject.date_filtering_limit = date
168
+ }
169
+
170
+ it 'returns the sync date' do
171
+ expect(subject.last_synchronization_date.to_date).to eql(success_sync.updated_at.to_date)
172
+ end
173
+ end
174
+
175
+ context 'with none' do
176
+ it { expect(subject.last_synchronization_date).to eql(nil) }
177
+ end
178
+ end
146
179
  end
147
180
 
148
181
 
@@ -72,25 +72,34 @@ describe Maestrano::Connector::Rails::Synchronization do
72
72
  end
73
73
  end
74
74
 
75
- describe 'clean_synchronizations' do
75
+ describe 'clean_synchronizations on creation' do
76
76
  let!(:organization) { create(:organization) }
77
- let!(:sync) { create(:synchronization, organization: organization) }
78
- let!(:sync2) { create(:synchronization, organization: organization) }
79
77
 
80
78
  context 'when less than 100 syncs' do
79
+ before {
80
+ 2.times do
81
+ create(:synchronization, organization: organization)
82
+ end
83
+ }
84
+
81
85
  it 'does nothing' do
82
- expect{ sync.clean_synchronizations }.to_not change{ organization.synchronizations.count }
86
+ expect{ organization.synchronizations.create(status: 'RUNNING') }.to change{ organization.synchronizations.count }.by(1)
83
87
  end
84
88
  end
85
89
 
86
90
  context 'when more than 100 syncs' do
87
91
  before {
88
- allow_any_instance_of(ActiveRecord::Associations::CollectionProxy).to receive(:count).and_return(102)
92
+ 100.times do
93
+ create(:synchronization, organization: organization)
94
+ end
89
95
  }
90
96
 
91
- it 'destroy the idmaps' do
92
- expect{ sync.clean_synchronizations }.to change{ Maestrano::Connector::Rails::Synchronization.count }.by(-2)
97
+ it 'destroy the right syncs' do
98
+ sync = organization.synchronizations.create(status: 'RUNNING')
99
+ expect(Maestrano::Connector::Rails::Synchronization.count).to eql(100)
100
+ expect(Maestrano::Connector::Rails::Synchronization.all.map(&:id)).to eql([*2..101])
93
101
  end
102
+
94
103
  end
95
104
  end
96
105
  end
@@ -5,6 +5,7 @@ require 'rspec/rails'
5
5
  require 'factory_girl_rails'
6
6
  require 'shoulda/matchers'
7
7
  require 'simplecov'
8
+ require 'timecop'
8
9
  SimpleCov.start
9
10
 
10
11
  Rails.backtrace_cleaner.remove_silencers!
@@ -0,0 +1,5 @@
1
+ connec_api_id: ''
2
+ connec_api_key: ''
3
+
4
+ encryption_key1: ''
5
+ encryption_key2: ''
@@ -47,7 +47,7 @@ gem 'uglifier', '>= 1.3.0'
47
47
 
48
48
  gem 'maestrano-connector-rails'
49
49
  gem 'config'
50
- # gem 'attr_encrypted', '~> 3.0.0'
50
+ gem 'attr_encrypted', '~> 1.4.0'
51
51
 
52
52
  # Background jobs
53
53
  gem 'sinatra', :require => nil
@@ -113,6 +113,8 @@ after_bundle do
113
113
  end
114
114
  copy_file 'settings/settings.yml', 'config/settings.yml'
115
115
 
116
+ copy_file 'application.yml.sample', 'config/application.yml.sample'
117
+
116
118
  application do <<-RUBY
117
119
  config.generators do |g|
118
120
  g.test_framework :rspec, fixture: false
@@ -1,6 +1,7 @@
1
1
  app_host: 'http://localhost:3001/'
2
2
 
3
- # encryption_key: 'This is a key that is 256 bits!!'
3
+ encryption_key1: 'This is a key that is 256 bits!!'
4
+ encryption_key2: 'This is a key that is 256 bits!!'
4
5
 
5
6
  default:
6
7
  api_host: 'https://maestrano.com'
@@ -1,4 +1,5 @@
1
- # encryption_key: <%= ENV['encryption_key'] %>
1
+ encryption_key1: <%= ENV['encryption_key1'] %>
2
+ encryption_key2: <%= ENV['encryption_key2'] %>
2
3
 
3
4
  # tenant config
4
5
  default:
@@ -1,6 +1,7 @@
1
1
  app_host: 'http://localhost:3001/'
2
2
 
3
- # encryption_key: 'This is a key that is 256 bits!!'
3
+ encryption_key1: 'This is a key that is 256 bits!!'
4
+ encryption_key2: 'This is a key that is 256 bits!!'
4
5
 
5
6
  default:
6
7
  environment: 'local'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: maestrano-connector-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pierre Berard
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-06-07 00:00:00.000000000 Z
11
+ date: 2016-06-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: maestrano-rails
@@ -94,6 +94,34 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: attr_encrypted
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 1.4.0
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 1.4.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: config
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
97
125
  - !ruby/object:Gem::Dependency
98
126
  name: shoulda
99
127
  requirement: !ruby/object:Gem::Requirement
@@ -220,6 +248,20 @@ dependencies:
220
248
  - - ">="
221
249
  - !ruby/object:Gem::Version
222
250
  version: '0'
251
+ - !ruby/object:Gem::Dependency
252
+ name: timecop
253
+ requirement: !ruby/object:Gem::Requirement
254
+ requirements:
255
+ - - ">="
256
+ - !ruby/object:Gem::Version
257
+ version: '0'
258
+ type: :development
259
+ prerelease: false
260
+ version_requirements: !ruby/object:Gem::Requirement
261
+ requirements:
262
+ - - ">="
263
+ - !ruby/object:Gem::Version
264
+ version: '0'
223
265
  description: Maestrano is the next generation marketplace for SME applications. See
224
266
  https://maestrano.com for details.
225
267
  email: pierre.berard@maestrano.com
@@ -266,7 +308,6 @@ files:
266
308
  - app/models/maestrano/connector/rails/user_organization_rel.rb
267
309
  - bin/rails
268
310
  - config/routes.rb
269
- - db/20160524112054_add_encryption_on_oauth_keys.rb
270
311
  - db/migrate/20151122162100_create_users.rb
271
312
  - db/migrate/20151122162414_create_organizations.rb
272
313
  - db/migrate/20151122162613_create_user_organization_rels.rb
@@ -275,6 +316,8 @@ files:
275
316
  - db/migrate/20160205132857_add_sync_enabled_to_organizations.rb
276
317
  - db/migrate/20160215103120_add_name_to_id_map.rb
277
318
  - db/migrate/20160427112250_add_inactive_to_idmaps.rb
319
+ - db/migrate/20160614114401_add_date_filtering_limit_to_organization.rb
320
+ - db/migrate/20160614160654_add_encryption_on_oauth_keys.rb
278
321
  - lib/generators/connector/USAGE
279
322
  - lib/generators/connector/complex_entity_generator.rb
280
323
  - lib/generators/connector/install_generator.rb
@@ -288,6 +331,7 @@ files:
288
331
  - lib/generators/connector/templates/example_entity.rb
289
332
  - lib/generators/connector/templates/example_entity_spec.rb
290
333
  - lib/generators/connector/templates/external.rb
334
+ - lib/generators/connector/templates/home.js
291
335
  - lib/generators/connector/templates/home_controller.rb
292
336
  - lib/generators/connector/templates/home_controller_spec.rb
293
337
  - lib/generators/connector/templates/home_index.haml
@@ -355,6 +399,11 @@ files:
355
399
  - spec/dummy/config/locales/en.yml
356
400
  - spec/dummy/config/routes.rb
357
401
  - spec/dummy/config/secrets.yml
402
+ - spec/dummy/config/settings.yml
403
+ - spec/dummy/config/settings/development.yml
404
+ - spec/dummy/config/settings/production.yml
405
+ - spec/dummy/config/settings/test.yml
406
+ - spec/dummy/config/settings/uat.yml
358
407
  - spec/dummy/db/schema.rb
359
408
  - spec/dummy/lib/assets/.keep
360
409
  - spec/dummy/log/.keep
@@ -384,6 +433,7 @@ files:
384
433
  - spec/routing/connec_routing_spec.rb
385
434
  - spec/spec_helper.rb
386
435
  - template/Procfile
436
+ - template/application.yml.sample
387
437
  - template/database.yml
388
438
  - template/factories.rb
389
439
  - template/gitignore
@@ -1,8 +0,0 @@
1
- class AddEncryptionOnOauthKeys < ActiveRecord::Migration
2
- def change
3
- rename_column :organizations, :oauth_token, :encrypted_oauth_token
4
- add_column :organizations, :encrypted_oauth_token_iv, :string
5
- rename_column :organizations, :refresh_token, :encrypted_refresh_token
6
- add_column :organizations, :encrypted_refresh_token_iv, :string
7
- end
8
- end