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.
- checksums.yaml +4 -4
- data/Gemfile +3 -2
- data/LICENSE +1 -1
- data/VERSION +1 -1
- data/app/controllers/maestrano/connec_controller.rb +3 -3
- data/app/jobs/maestrano/connector/rails/all_synchronizations_job.rb +1 -1
- data/app/jobs/maestrano/connector/rails/push_to_connec_job.rb +3 -3
- data/app/jobs/maestrano/connector/rails/synchronization_job.rb +9 -8
- data/app/models/maestrano/connector/rails/concerns/complex_entity.rb +6 -6
- data/app/models/maestrano/connector/rails/concerns/entity.rb +24 -11
- data/app/models/maestrano/connector/rails/organization.rb +7 -2
- data/app/models/maestrano/connector/rails/synchronization.rb +1 -1
- data/db/migrate/20160614114401_add_date_filtering_limit_to_organization.rb +11 -0
- data/db/migrate/20160614160654_add_encryption_on_oauth_keys.rb +19 -0
- data/lib/generators/connector/install_generator.rb +1 -0
- data/lib/generators/connector/templates/complex_entity_example/person.rb +1 -1
- data/lib/generators/connector/templates/entity.rb +9 -3
- data/lib/generators/connector/templates/home.js +11 -0
- data/lib/generators/connector/templates/home_controller.rb +12 -1
- data/lib/generators/connector/templates/home_index.haml +11 -0
- data/maestrano-connector-rails.gemspec +21 -4
- data/release_notes.md +6 -0
- data/spec/dummy/config/settings.yml +37 -0
- data/spec/dummy/config/settings/development.yml +12 -0
- data/spec/dummy/config/settings/production.yml +1 -0
- data/spec/dummy/config/settings/test.yml +12 -0
- data/spec/dummy/config/settings/uat.yml +1 -0
- data/spec/dummy/db/schema.rb +11 -6
- data/spec/integration/connec_to_external_spec.rb +24 -2
- data/spec/models/entity_spec.rb +49 -8
- data/spec/models/organization_spec.rb +34 -1
- data/spec/models/synchronization_spec.rb +16 -7
- data/spec/spec_helper.rb +1 -0
- data/template/application.yml.sample +5 -0
- data/template/maestrano-connector-template.rb +3 -1
- data/template/settings/development.yml +2 -1
- data/template/settings/settings.yml +2 -1
- data/template/settings/test.yml +2 -1
- metadata +53 -3
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d686a6327d19c9928de390fdff97b8a49314ee12
|
4
|
+
data.tar.gz: 24766745312ecf5dbdc9cabd262602ecaee2883f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
11
|
-
|
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
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0
|
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
|
-
|
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(
|
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(
|
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(
|
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
|
-
|
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(
|
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(
|
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,
|
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,
|
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,
|
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,
|
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(
|
69
|
-
external_entities = entity_instance.get_external_entities_wrapper(
|
70
|
-
connec_entities = entity_instance.get_connec_entities(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
144
|
+
def before_sync(last_synchronization_date)
|
145
145
|
# Does nothing by default
|
146
146
|
end
|
147
147
|
|
148
|
-
def after_sync(
|
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(
|
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
|
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 '#{
|
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(
|
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(
|
228
|
+
get_external_entities(last_synchronization_date)
|
225
229
|
end
|
226
230
|
|
227
|
-
def get_external_entities(
|
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(
|
401
|
+
def before_sync(last_synchronization_date)
|
393
402
|
# Does nothing by default
|
394
403
|
end
|
395
404
|
|
396
|
-
def after_sync(
|
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
|
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
|
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
|
-
|
24
|
-
|
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
|
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(
|
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
|
15
|
-
# It should also implements an option to do a full synchronization when @opts[:full_sync] == true or when there is no
|
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
|
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
|
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-
|
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
|
|
data/release_notes.md
CHANGED
@@ -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/'
|
data/spec/dummy/db/schema.rb
CHANGED
@@ -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:
|
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 "
|
46
|
-
t.string "
|
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",
|
50
|
-
t.datetime "updated_at",
|
51
|
-
t.boolean "sync_enabled",
|
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,
|
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
|
data/spec/models/entity_spec.rb
CHANGED
@@ -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{
|
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
|
-
|
92
|
+
100.times do
|
93
|
+
create(:synchronization, organization: organization)
|
94
|
+
end
|
89
95
|
}
|
90
96
|
|
91
|
-
it 'destroy the
|
92
|
-
|
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
|
data/spec/spec_helper.rb
CHANGED
@@ -47,7 +47,7 @@ gem 'uglifier', '>= 1.3.0'
|
|
47
47
|
|
48
48
|
gem 'maestrano-connector-rails'
|
49
49
|
gem 'config'
|
50
|
-
|
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
|
data/template/settings/test.yml
CHANGED
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
|
+
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-
|
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
|