maestrano-connector-rails 2.0.2.pre.RC9 → 2.0.2.pre.RC10
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/.gitignore +2 -1
- data/Rakefile +1 -1
- data/app/jobs/maestrano/connector/rails/concerns/synchronization_job.rb +153 -0
- data/app/jobs/maestrano/connector/rails/synchronization_job.rb +1 -144
- data/app/models/maestrano/connector/rails/concerns/connector_logger.rb +1 -1
- data/app/models/maestrano/connector/rails/concerns/entity.rb +3 -3
- data/app/models/maestrano/connector/rails/concerns/id_map.rb +8 -0
- data/app/models/maestrano/connector/rails/concerns/organization.rb +161 -0
- data/app/models/maestrano/connector/rails/concerns/synchronization.rb +54 -0
- data/app/models/maestrano/connector/rails/concerns/user.rb +27 -0
- data/app/models/maestrano/connector/rails/id_map.rb +1 -2
- data/app/models/maestrano/connector/rails/organization.rb +1 -154
- data/app/models/maestrano/connector/rails/synchronization.rb +1 -46
- data/app/models/maestrano/connector/rails/user.rb +1 -21
- data/config/routes.rb +2 -2
- data/lib/generators/connector/install_generator.rb +5 -0
- data/lib/generators/connector/templates/home_controller.rb +1 -1
- data/lib/generators/connector/templates/home_controller_spec.rb +20 -12
- data/lib/generators/connector/templates/home_index.haml +1 -7
- data/lib/generators/connector/templates/layouts.haml +2 -2
- data/lib/generators/connector/templates/logos/to_connec.png +0 -0
- data/lib/generators/connector/templates/logos/to_external.png +0 -0
- data/lib/maestrano/connector/rails/version.rb +1 -1
- data/maestrano-connector-rails.gemspec +1 -0
- metadata +24 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 65e017e72e894da3e1fadf4eda7d24580fe90176
|
4
|
+
data.tar.gz: 09168f2321092da1b57490468516225190cd9e64
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cb5e4891f48db210ddf0a2881fe3cffaabe1850b42eebfb0988f02f1ef49a13f93984ba4f59b7bed1c6a26a740bc08023e173c25dfa3f4f3661aa01514cc50db
|
7
|
+
data.tar.gz: baea5af0aca2aac129786700ea1abf1df7a2d1969821cd54f97185c22d3ad338ead03a75d6d13e57915bc11ab2f2d63a9115949decbce5b57b4d21a3c4364630
|
data/.gitignore
CHANGED
data/Rakefile
CHANGED
@@ -0,0 +1,153 @@
|
|
1
|
+
module Maestrano::Connector::Rails::Concerns::SynchronizationJob
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
queue_as :default
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def enqueued?(organization_id)
|
10
|
+
Maestrano::Connector::Rails::SynchronizationJob.find_job(organization_id).present? || Maestrano::Connector::Rails::SynchronizationJob.find_running_job(organization_id).present?
|
11
|
+
end
|
12
|
+
|
13
|
+
def find_job(organization_id)
|
14
|
+
queue = Sidekiq::Queue.new(:default)
|
15
|
+
queue.find do |job|
|
16
|
+
organization_id == job.item['args'][0]['arguments'].first
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_running_job(organization_id)
|
21
|
+
Sidekiq::Workers.new.find do |_, _, work|
|
22
|
+
work['queue'] == 'default' && work['payload']['args'][0]['arguments'].first == organization_id
|
23
|
+
end
|
24
|
+
rescue
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Supported options:
|
30
|
+
# * :forced => true synchronization has been triggered manually
|
31
|
+
# * :only_entities => [person, tasks_list]
|
32
|
+
# * :full_sync => true synchronization is performed without date filtering
|
33
|
+
# * :connec_preemption => true|false : preemption is always|never given to connec in case of conflict (if not set, the most recently updated entity is kept)
|
34
|
+
def perform(organization_id, opts = {})
|
35
|
+
organization = Maestrano::Connector::Rails::Organization.find(organization_id)
|
36
|
+
return unless organization&.sync_enabled
|
37
|
+
|
38
|
+
# Check if previous synchronization is still running
|
39
|
+
if Maestrano::Connector::Rails::Synchronization.where(organization_id: organization.id, status: 'RUNNING').where(created_at: (30.minutes.ago..Time.now.utc)).exists?
|
40
|
+
Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, 'Synchronization skipped: Previous synchronization is still running')
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
# Check if recovery mode: last 3 synchronizations have failed
|
45
|
+
if !opts[:forced] && organization.last_three_synchronizations_failed? \
|
46
|
+
&& organization.synchronizations.order(created_at: :desc).limit(1).first.updated_at > 1.day.ago
|
47
|
+
Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, 'Synchronization skipped: Recovery mode (three previous synchronizations have failed)')
|
48
|
+
return
|
49
|
+
end
|
50
|
+
|
51
|
+
# Trigger synchronization
|
52
|
+
Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Start synchronization, opts=#{opts}")
|
53
|
+
current_synchronization = Maestrano::Connector::Rails::Synchronization.create_running(organization)
|
54
|
+
|
55
|
+
begin
|
56
|
+
last_synchronization = organization.last_successful_synchronization
|
57
|
+
last_synchronization_date = organization.last_synchronization_date
|
58
|
+
connec_client = Maestrano::Connector::Rails::ConnecHelper.get_client(organization)
|
59
|
+
external_client = Maestrano::Connector::Rails::External.get_client(organization)
|
60
|
+
|
61
|
+
# First synchronization should be from external to Connec! only to let the smart merging works
|
62
|
+
# We do a doube sync: only from external, then only from connec!
|
63
|
+
# We also do batched sync as the first one can be quite huge
|
64
|
+
if last_synchronization.nil?
|
65
|
+
Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, 'First synchronization ever. Doing two half syncs to allow smart merging to work its magic.')
|
66
|
+
organization.synchronized_entities.each do |entity, settings|
|
67
|
+
next unless settings[:can_push_to_connec] || settings[:can_push_to_external]
|
68
|
+
Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "First synchronization ever. Doing half sync from external for #{entity}.")
|
69
|
+
first_sync_entity(entity.to_s, organization, connec_client, external_client, last_synchronization_date, opts, true)
|
70
|
+
Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "First synchronization ever. Doing half sync from Connec! for #{entity}.")
|
71
|
+
first_sync_entity(entity.to_s, organization, connec_client, external_client, last_synchronization_date, opts, false)
|
72
|
+
end
|
73
|
+
elsif opts[:only_entities]
|
74
|
+
Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Synchronization is partial and will synchronize only #{opts[:only_entities].join(' ')}")
|
75
|
+
# The synchronization is marked as partial and will not be considered as the last-synchronization for the next sync
|
76
|
+
current_synchronization.mark_as_partial
|
77
|
+
opts[:only_entities].each do |entity|
|
78
|
+
sync_entity(entity, organization, connec_client, external_client, last_synchronization_date, opts)
|
79
|
+
end
|
80
|
+
else
|
81
|
+
organization.synchronized_entities.each do |entity, settings|
|
82
|
+
next unless settings[:can_push_to_connec] || settings[:can_push_to_external]
|
83
|
+
sync_entity(entity.to_s, organization, connec_client, external_client, last_synchronization_date, opts)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Finished synchronization, organization=#{organization.uid}, status=success")
|
88
|
+
current_synchronization.mark_as_success
|
89
|
+
rescue => e
|
90
|
+
Maestrano::Connector::Rails::ConnectorLogger.log('info', organization, "Finished synchronization, organization=#{organization.uid}, status=error, message=#{e.message} backtrace=#{e.backtrace.join("\n\t")}")
|
91
|
+
current_synchronization.mark_as_error(e.message)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def sync_entity(entity_name, organization, connec_client, external_client, last_synchronization_date, opts)
|
96
|
+
entity_instance = instanciate_entity(entity_name, organization, connec_client, external_client, opts)
|
97
|
+
|
98
|
+
perform_sync(entity_instance, last_synchronization_date)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Does a batched sync on either external or connec!
|
102
|
+
def first_sync_entity(entity_name, organization, connec_client, external_client, last_synchronization_date, opts, external = true)
|
103
|
+
limit = Settings.first_sync_batch_size || 50
|
104
|
+
skip = 0
|
105
|
+
entities_count = limit
|
106
|
+
last_first_record = nil
|
107
|
+
|
108
|
+
h = {__limit: limit}
|
109
|
+
external ? h[:__skip_connec] = true : h[:__skip_external] = true
|
110
|
+
entity_instance = instanciate_entity(entity_name, organization, connec_client, external_client, opts.merge(h))
|
111
|
+
|
112
|
+
# IF entities_count > limit
|
113
|
+
# This first sync feature is probably not implemented in the connector
|
114
|
+
# because it fetched more than the expected number of entities
|
115
|
+
# No need to fetch it a second Time
|
116
|
+
# ELSIF entities_count < limit
|
117
|
+
# No more entities to fetch
|
118
|
+
while entities_count == limit
|
119
|
+
entity_instance.opts_merge!(__skip: skip)
|
120
|
+
|
121
|
+
perform_hash = perform_sync(entity_instance, last_synchronization_date, external)
|
122
|
+
entities_count = perform_hash[:count]
|
123
|
+
|
124
|
+
# Safety: if the connector does not implement batched calls but has exactly limit entities
|
125
|
+
# There is a risk of infinite loop
|
126
|
+
# We're comparing the first record to check that it is different
|
127
|
+
first_record = Digest::MD5.hexdigest(perform_hash[:first].to_s)
|
128
|
+
break if last_first_record && first_record == last_first_record
|
129
|
+
last_first_record = first_record
|
130
|
+
|
131
|
+
skip += limit
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def instanciate_entity(entity_name, organization, connec_client, external_client, opts)
|
138
|
+
"Entities::#{entity_name.titleize.split.join}".constantize.new(organization, connec_client, external_client, opts.dup)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Perform the sync and return the entities_count for either external or connec
|
142
|
+
def perform_sync(entity_instance, last_synchronization_date, external = true)
|
143
|
+
entity_instance.before_sync(last_synchronization_date)
|
144
|
+
external_entities = entity_instance.get_external_entities_wrapper(last_synchronization_date)
|
145
|
+
connec_entities = entity_instance.get_connec_entities(last_synchronization_date)
|
146
|
+
mapped_entities = entity_instance.consolidate_and_map_data(connec_entities, external_entities)
|
147
|
+
entity_instance.push_entities_to_external(mapped_entities[:connec_entities])
|
148
|
+
entity_instance.push_entities_to_connec(mapped_entities[:external_entities])
|
149
|
+
entity_instance.after_sync(last_synchronization_date)
|
150
|
+
|
151
|
+
entity_instance.class.count_and_first(external ? external_entities : connec_entities)
|
152
|
+
end
|
153
|
+
end
|
@@ -1,148 +1,5 @@
|
|
1
1
|
module Maestrano::Connector::Rails
|
2
2
|
class SynchronizationJob < ::ActiveJob::Base
|
3
|
-
|
4
|
-
|
5
|
-
# Supported options:
|
6
|
-
# * :forced => true synchronization has been triggered manually
|
7
|
-
# * :only_entities => [person, tasks_list]
|
8
|
-
# * :full_sync => true synchronization is performed without date filtering
|
9
|
-
# * :connec_preemption => true|false : preemption is always|never given to connec in case of conflict (if not set, the most recently updated entity is kept)
|
10
|
-
def perform(organization_id, opts = {})
|
11
|
-
organization = Maestrano::Connector::Rails::Organization.find(organization_id)
|
12
|
-
return unless organization&.sync_enabled
|
13
|
-
# Check if previous synchronization is still running
|
14
|
-
if Synchronization.where(organization_id: organization.id, status: 'RUNNING').where(created_at: (30.minutes.ago..Time.now)).exists?
|
15
|
-
ConnectorLogger.log('info', organization, 'Synchronization skipped: Previous synchronization is still running')
|
16
|
-
return
|
17
|
-
end
|
18
|
-
|
19
|
-
# Check if recovery mode: last 3 synchronizations have failed
|
20
|
-
if !opts[:forced] && organization.last_three_synchronizations_failed? \
|
21
|
-
&& organization.synchronizations.order(created_at: :desc).limit(1).first.updated_at > 1.day.ago
|
22
|
-
ConnectorLogger.log('info', organization, 'Synchronization skipped: Recovery mode (three previous synchronizations have failed)')
|
23
|
-
return
|
24
|
-
end
|
25
|
-
|
26
|
-
# Trigger synchronization
|
27
|
-
ConnectorLogger.log('info', organization, "Start synchronization, opts=#{opts}")
|
28
|
-
current_synchronization = Synchronization.create_running(organization)
|
29
|
-
|
30
|
-
begin
|
31
|
-
last_synchronization = organization.last_successful_synchronization
|
32
|
-
last_synchronization_date = organization.last_synchronization_date
|
33
|
-
connec_client = ConnecHelper.get_client(organization)
|
34
|
-
external_client = External.get_client(organization)
|
35
|
-
|
36
|
-
# First synchronization should be from external to Connec! only to let the smart merging works
|
37
|
-
# We do a doube sync: only from external, then only from connec!
|
38
|
-
# We also do batched sync as the first one can be quite huge
|
39
|
-
if last_synchronization.nil?
|
40
|
-
ConnectorLogger.log('info', organization, 'First synchronization ever. Doing two half syncs to allow smart merging to work its magic.')
|
41
|
-
organization.synchronized_entities.each do |entity, settings|
|
42
|
-
next unless settings[:can_push_to_connec] || settings[:can_push_to_external]
|
43
|
-
ConnectorLogger.log('info', organization, "First synchronization ever. Doing half sync from external for #{entity}.")
|
44
|
-
first_sync_entity(entity.to_s, organization, connec_client, external_client, last_synchronization_date, opts, true)
|
45
|
-
ConnectorLogger.log('info', organization, "First synchronization ever. Doing half sync from Connec! for #{entity}.")
|
46
|
-
first_sync_entity(entity.to_s, organization, connec_client, external_client, last_synchronization_date, opts, false)
|
47
|
-
end
|
48
|
-
elsif opts[:only_entities]
|
49
|
-
ConnectorLogger.log('info', organization, "Synchronization is partial and will synchronize only #{opts[:only_entities].join(' ')}")
|
50
|
-
# The synchronization is marked as partial and will not be considered as the last-synchronization for the next sync
|
51
|
-
current_synchronization.set_partial
|
52
|
-
opts[:only_entities].each do |entity|
|
53
|
-
sync_entity(entity, organization, connec_client, external_client, last_synchronization_date, opts)
|
54
|
-
end
|
55
|
-
else
|
56
|
-
organization.synchronized_entities.each do |entity, settings|
|
57
|
-
next unless settings[:can_push_to_connec] || settings[:can_push_to_external]
|
58
|
-
sync_entity(entity.to_s, organization, connec_client, external_client, last_synchronization_date, opts)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
ConnectorLogger.log('info', organization, "Finished synchronization, organization=#{organization.uid}, status=success")
|
63
|
-
current_synchronization.set_success
|
64
|
-
rescue => e
|
65
|
-
ConnectorLogger.log('info', organization, "Finished synchronization, organization=#{organization.uid}, status=error, message=#{e.message} backtrace=#{e.backtrace.join("\n\t")}")
|
66
|
-
current_synchronization.set_error(e.message)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def sync_entity(entity_name, organization, connec_client, external_client, last_synchronization_date, opts)
|
71
|
-
entity_instance = instanciate_entity(entity_name, organization, connec_client, external_client, opts)
|
72
|
-
|
73
|
-
perform_sync(entity_instance, last_synchronization_date)
|
74
|
-
end
|
75
|
-
|
76
|
-
# Does a batched sync on either external or connec!
|
77
|
-
def first_sync_entity(entity_name, organization, connec_client, external_client, last_synchronization_date, opts, external = true)
|
78
|
-
limit = Settings.first_sync_batch_size || 50
|
79
|
-
skip = 0
|
80
|
-
entities_count = limit
|
81
|
-
last_first_record = nil
|
82
|
-
|
83
|
-
h = {__limit: limit}
|
84
|
-
external ? h[:__skip_connec] = true : h[:__skip_external] = true
|
85
|
-
entity_instance = instanciate_entity(entity_name, organization, connec_client, external_client, opts.merge(h))
|
86
|
-
|
87
|
-
# IF entities_count > limit
|
88
|
-
# This first sync feature is probably not implemented in the connector
|
89
|
-
# because it fetched more than the expected number of entities
|
90
|
-
# No need to fetch it a second Time
|
91
|
-
# ELSIF entities_count < limit
|
92
|
-
# No more entities to fetch
|
93
|
-
while entities_count == limit
|
94
|
-
entity_instance.opts_merge!(__skip: skip)
|
95
|
-
|
96
|
-
perform_hash = perform_sync(entity_instance, last_synchronization_date, external)
|
97
|
-
entities_count = perform_hash[:count]
|
98
|
-
|
99
|
-
# Safety: if the connector does not implement batched calls but has exactly limit entities
|
100
|
-
# There is a risk of infinite loop
|
101
|
-
# We're comparing the first record to check that it is different
|
102
|
-
first_record = Digest::MD5.hexdigest(perform_hash[:first].to_s)
|
103
|
-
break if last_first_record && first_record == last_first_record
|
104
|
-
last_first_record = first_record
|
105
|
-
|
106
|
-
skip += limit
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def self.enqueued?(organization_id)
|
111
|
-
SynchronizationJob.find_job(organization_id).present? || SynchronizationJob.find_running_job(organization_id).present?
|
112
|
-
end
|
113
|
-
|
114
|
-
def self.find_job(organization_id)
|
115
|
-
queue = Sidekiq::Queue.new(:default)
|
116
|
-
queue.find do |job|
|
117
|
-
organization_id == job.item['args'][0]['arguments'].first
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
def self.find_running_job(organization_id)
|
122
|
-
Sidekiq::Workers.new.find do |_, _, work|
|
123
|
-
work['queue'] == 'default' && work['payload']['args'][0]['arguments'].first == organization_id
|
124
|
-
end
|
125
|
-
rescue
|
126
|
-
nil
|
127
|
-
end
|
128
|
-
|
129
|
-
private
|
130
|
-
|
131
|
-
def instanciate_entity(entity_name, organization, connec_client, external_client, opts)
|
132
|
-
"Entities::#{entity_name.titleize.split.join}".constantize.new(organization, connec_client, external_client, opts.dup)
|
133
|
-
end
|
134
|
-
|
135
|
-
# Perform the sync and return the entities_count for either external or connec
|
136
|
-
def perform_sync(entity_instance, last_synchronization_date, external = true)
|
137
|
-
entity_instance.before_sync(last_synchronization_date)
|
138
|
-
external_entities = entity_instance.get_external_entities_wrapper(last_synchronization_date)
|
139
|
-
connec_entities = entity_instance.get_connec_entities(last_synchronization_date)
|
140
|
-
mapped_entities = entity_instance.consolidate_and_map_data(connec_entities, external_entities)
|
141
|
-
entity_instance.push_entities_to_external(mapped_entities[:connec_entities])
|
142
|
-
entity_instance.push_entities_to_connec(mapped_entities[:external_entities])
|
143
|
-
entity_instance.after_sync(last_synchronization_date)
|
144
|
-
|
145
|
-
entity_instance.class.count_and_first(external ? external_entities : connec_entities)
|
146
|
-
end
|
3
|
+
include Maestrano::Connector::Rails::Concerns::SynchronizationJob
|
147
4
|
end
|
148
5
|
end
|
@@ -4,7 +4,7 @@ module Maestrano::Connector::Rails::Concerns::ConnectorLogger
|
|
4
4
|
module ClassMethods
|
5
5
|
def log(level, organization, msg, params = {})
|
6
6
|
line = "uid=\"#{organization&.uid}\", org_uid=\"#{organization&.org_uid}\", tenant=\"#{organization&.tenant}\""
|
7
|
-
line = "#{line}, #{params.map { |k, v| "#{k}=\"#{v}\"" }.join(', ')}"
|
7
|
+
line = "#{line}, #{params.map { |k, v| "#{k}=\"#{v}\"" }.join(', ')}" if params.present?
|
8
8
|
Rails.logger.method(level).call("#{line}, message=\"#{msg}\"")
|
9
9
|
end
|
10
10
|
end
|
@@ -263,7 +263,7 @@ module Maestrano::Connector::Rails::Concerns::Entity
|
|
263
263
|
|
264
264
|
# As we're doing only POST, we use the idmaps to filter out updates
|
265
265
|
unless self.class.can_update_connec?
|
266
|
-
mapped_external_entities_with_idmaps.
|
266
|
+
mapped_external_entities_with_idmaps.reject! { |mapped_external_entity_with_idmap| mapped_external_entity_with_idmap[:idmap].connec_id }
|
267
267
|
end
|
268
268
|
|
269
269
|
if self.class.currency_check_fields
|
@@ -472,7 +472,7 @@ module Maestrano::Connector::Rails::Concerns::Entity
|
|
472
472
|
Maestrano::Connector::Rails::ConnectorLogger.log('info', @organization, "Sending batch request to Connec! #{log_info} for #{self.class.normalize_connec_entity_name(connec_entity_name)}. Batch_request_size: #{batch_request[:ops].size}. Call_number: #{(start / request_per_call) + 1}")
|
473
473
|
response = @connec_client.batch(batch_request)
|
474
474
|
Maestrano::Connector::Rails::ConnectorLogger.log('debug', @organization, "Received batch response from Connec! for #{self.class.normalize_connec_entity_name(connec_entity_name)}: #{response}")
|
475
|
-
raise "No data received from Connec! when trying to send batch request #{log_info} for #{self.class.connec_entity_name.pluralize}" unless response &&
|
475
|
+
raise "No data received from Connec! when trying to send batch request #{log_info} for #{self.class.connec_entity_name.pluralize}" unless response && response.body.present?
|
476
476
|
response = JSON.parse(response.body)
|
477
477
|
|
478
478
|
# Parse batch response
|
@@ -497,7 +497,7 @@ module Maestrano::Connector::Rails::Concerns::Entity
|
|
497
497
|
Maestrano::Connector::Rails::ConnectorLogger.log('debug', @organization, "Fetching data from connec entity=#{self.class.connec_entity_name}, url=#{uri}")
|
498
498
|
response = @connec_client.get(uri)
|
499
499
|
|
500
|
-
raise "No data received from Connec! when trying to fetch #{self.class.normalized_connec_entity_name}" unless response &&
|
500
|
+
raise "No data received from Connec! when trying to fetch #{self.class.normalized_connec_entity_name}" unless response && response.body.present?
|
501
501
|
|
502
502
|
response_hash = JSON.parse(response.body)
|
503
503
|
Maestrano::Connector::Rails::ConnectorLogger.log('debug', @organization, "Received response for entity=#{self.class.connec_entity_name}, response=#{response_hash}")
|
@@ -0,0 +1,161 @@
|
|
1
|
+
module Maestrano::Connector::Rails::Concerns::Organization
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
# Enable Maestrano for this group
|
6
|
+
maestrano_group_via :provider, :uid, :tenant do |group, maestrano|
|
7
|
+
group.name = (maestrano.name.blank? ? 'Default Group name' : maestrano.name)
|
8
|
+
group.tenant = 'default' # To be set from SSO parameter
|
9
|
+
group.org_uid = maestrano.org_uid # Maestrano organization UID
|
10
|
+
|
11
|
+
# group.country_alpha2 = maestrano.country
|
12
|
+
# group.free_trial_end_at = maestrano.free_trial_end_at
|
13
|
+
# group.some_required_field = 'some-appropriate-default-value'
|
14
|
+
end
|
15
|
+
|
16
|
+
# Callbacks
|
17
|
+
before_save :update_metadata
|
18
|
+
|
19
|
+
#===================================
|
20
|
+
# Encryptions
|
21
|
+
#===================================
|
22
|
+
attr_encrypted_options[:mode] = :per_attribute_iv_and_salt
|
23
|
+
attr_encrypted :oauth_token, key: ::Settings.encryption_key1
|
24
|
+
attr_encrypted :refresh_token, key: ::Settings.encryption_key2
|
25
|
+
|
26
|
+
#===================================
|
27
|
+
# Associations
|
28
|
+
#===================================
|
29
|
+
has_many :user_organization_rels
|
30
|
+
has_many :users, through: :user_organization_rels
|
31
|
+
has_many :id_maps, dependent: :destroy
|
32
|
+
has_many :synchronizations, dependent: :destroy
|
33
|
+
|
34
|
+
#===================================
|
35
|
+
# Validation
|
36
|
+
#===================================
|
37
|
+
validates :name, presence: true
|
38
|
+
validates :tenant, presence: true
|
39
|
+
validates :uid, uniqueness: {scope: :tenant}
|
40
|
+
validates :oauth_uid, uniqueness: {allow_blank: true, message: 'This account has already been linked'}
|
41
|
+
|
42
|
+
#===================================
|
43
|
+
# Serialized field
|
44
|
+
#===================================
|
45
|
+
serialize :synchronized_entities
|
46
|
+
end
|
47
|
+
|
48
|
+
module ClassMethods
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize
|
52
|
+
super
|
53
|
+
self.synchronized_entities = {}
|
54
|
+
Maestrano::Connector::Rails::External.entities_list.each do |entity|
|
55
|
+
synchronized_entities[entity.to_sym] = {can_push_to_connec: !pull_disabled, can_push_to_external: !push_disabled}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def displayable_synchronized_entities
|
60
|
+
result = {}
|
61
|
+
synchronized_entities.each do |entity, hash|
|
62
|
+
begin
|
63
|
+
clazz = "Entities::#{entity.to_s.titleize.split.join}".constantize
|
64
|
+
rescue
|
65
|
+
next
|
66
|
+
end
|
67
|
+
result[entity] = {connec_name: clazz.public_connec_entity_name, external_name: clazz.public_external_entity_name}.merge(hash)
|
68
|
+
end
|
69
|
+
result
|
70
|
+
end
|
71
|
+
|
72
|
+
def add_member(user)
|
73
|
+
user_organization_rels.create(user: user) if tenant == user.tenant && !member?(user)
|
74
|
+
end
|
75
|
+
|
76
|
+
def member?(user)
|
77
|
+
user_organization_rels.where(user_id: user.id).count.positive?
|
78
|
+
end
|
79
|
+
|
80
|
+
def remove_member(user)
|
81
|
+
user_organization_rels.where(user_id: user.id).delete_all
|
82
|
+
end
|
83
|
+
|
84
|
+
def from_omniauth(auth)
|
85
|
+
self.oauth_provider = auth.provider
|
86
|
+
self.oauth_uid = auth.uid
|
87
|
+
self.oauth_token = auth.credentials.token
|
88
|
+
self.refresh_token = auth.credentials.refresh_token
|
89
|
+
self.instance_url = auth.credentials.instance_url
|
90
|
+
save
|
91
|
+
end
|
92
|
+
|
93
|
+
def clear_omniauth
|
94
|
+
self.oauth_uid = nil
|
95
|
+
self.oauth_token = nil
|
96
|
+
self.refresh_token = nil
|
97
|
+
self.sync_enabled = false
|
98
|
+
save
|
99
|
+
end
|
100
|
+
|
101
|
+
# Enable historical data sharing (prior to account linking)
|
102
|
+
def enable_historical_data(enabled)
|
103
|
+
# Historical data sharing cannot be unset
|
104
|
+
return if historical_data
|
105
|
+
if enabled
|
106
|
+
self.date_filtering_limit = nil
|
107
|
+
self.historical_data = true
|
108
|
+
else
|
109
|
+
self.date_filtering_limit ||= Time.now.getlocal
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def last_three_synchronizations_failed?
|
114
|
+
arr = synchronizations.last(3).map(&:error?)
|
115
|
+
arr.count == 3 && arr.uniq == [true]
|
116
|
+
end
|
117
|
+
|
118
|
+
def last_successful_synchronization
|
119
|
+
synchronizations.where(status: 'SUCCESS', partial: false).order(updated_at: :desc).first
|
120
|
+
end
|
121
|
+
|
122
|
+
def last_synchronization_date
|
123
|
+
last_successful_synchronization&.updated_at || date_filtering_limit
|
124
|
+
end
|
125
|
+
|
126
|
+
def reset_synchronized_entities(default = false)
|
127
|
+
synchronized_entities.slice!(*Maestrano::Connector::Rails::External.entities_list.map(&:to_sym))
|
128
|
+
Maestrano::Connector::Rails::External.entities_list.each do |entity|
|
129
|
+
if synchronized_entities[entity.to_sym].is_a?(Hash)
|
130
|
+
can_push_to_external = synchronized_entities[entity.to_sym][:can_push_to_external]
|
131
|
+
can_push_to_connec = synchronized_entities[entity.to_sym][:can_push_to_connec]
|
132
|
+
else
|
133
|
+
can_push_to_external = synchronized_entities[entity.to_sym]
|
134
|
+
can_push_to_connec = synchronized_entities[entity.to_sym]
|
135
|
+
end
|
136
|
+
synchronized_entities[entity.to_sym] = {can_push_to_connec: (can_push_to_connec || default) && !pull_disabled, can_push_to_external: (can_push_to_external || default) && !push_disabled}
|
137
|
+
end
|
138
|
+
save
|
139
|
+
end
|
140
|
+
|
141
|
+
def push_to_connec_enabled?(entity)
|
142
|
+
synchronized_entities.dig(Maestrano::Connector::Rails::EntityHelper.snake_name(entity), :can_push_to_connec) && entity&.class&.can_write_connec?
|
143
|
+
end
|
144
|
+
|
145
|
+
def push_to_external_enabled?(entity)
|
146
|
+
synchronized_entities.dig(Maestrano::Connector::Rails::EntityHelper.snake_name(entity), :can_push_to_external) && entity&.class&.can_write_external?
|
147
|
+
end
|
148
|
+
|
149
|
+
def set_instance_metadata
|
150
|
+
auth = {username: Maestrano[tenant].param('api.id'), password: Maestrano[tenant].param('api.key')}
|
151
|
+
res = HTTParty.get("#{Maestrano[tenant].param('api.host')}/api/v1/account/groups/#{uid}", basic_auth: auth)
|
152
|
+
response = JSON.parse(res.body)
|
153
|
+
self.push_disabled = response.dig('data', 'metadata', 'push_disabled')
|
154
|
+
self.pull_disabled = response.dig('data', 'metadata', 'pull_disabled')
|
155
|
+
end
|
156
|
+
|
157
|
+
def update_metadata
|
158
|
+
set_instance_metadata
|
159
|
+
enable_historical_data(true) if push_disabled
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Maestrano::Connector::Rails::Concerns::Synchronization
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
# Keeping only 100 synchronizations per organization
|
6
|
+
after_create :clean_synchronizations
|
7
|
+
|
8
|
+
RUNNING_STATUS = 'RUNNING'.freeze
|
9
|
+
ERROR_STATUS = 'ERROR'.freeze
|
10
|
+
SUCCESS_STATUS = 'SUCCESS'.freeze
|
11
|
+
|
12
|
+
#===================================
|
13
|
+
# Associations
|
14
|
+
#===================================
|
15
|
+
belongs_to :organization
|
16
|
+
|
17
|
+
validates :status, presence: true
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
def create_running(organization)
|
22
|
+
Maestrano::Connector::Rails::Synchronization.create(organization_id: organization.id, status: RUNNING_STATUS)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def running?
|
27
|
+
status == RUNNING_STATUS
|
28
|
+
end
|
29
|
+
|
30
|
+
def error?
|
31
|
+
status == ERROR_STATUS
|
32
|
+
end
|
33
|
+
|
34
|
+
def success?
|
35
|
+
status == SUCCESS_STATUS
|
36
|
+
end
|
37
|
+
|
38
|
+
def mark_as_success
|
39
|
+
update_attributes(status: SUCCESS_STATUS)
|
40
|
+
end
|
41
|
+
|
42
|
+
def mark_as_error(msg)
|
43
|
+
update_attributes(status: ERROR_STATUS, message: msg)
|
44
|
+
end
|
45
|
+
|
46
|
+
def mark_as_partial
|
47
|
+
update_attributes(partial: true)
|
48
|
+
end
|
49
|
+
|
50
|
+
def clean_synchronizations
|
51
|
+
count = organization.synchronizations.count
|
52
|
+
organization.synchronizations.order('id ASC').limit(count - 100).destroy_all if count > 100
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Maestrano::Connector::Rails::Concerns::User
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
# Enable Maestrano for this user
|
6
|
+
maestrano_user_via :provider, :uid, :tenant do |user, maestrano|
|
7
|
+
user.uid = maestrano.uid
|
8
|
+
user.provider = maestrano.provider
|
9
|
+
user.first_name = maestrano.first_name
|
10
|
+
user.last_name = maestrano.last_name
|
11
|
+
user.email = maestrano.email
|
12
|
+
user.tenant = 'default' # To be set from SSO parameter
|
13
|
+
end
|
14
|
+
|
15
|
+
#===================================
|
16
|
+
# Associations
|
17
|
+
#===================================
|
18
|
+
has_many :user_organization_rels, class_name: 'Maestrano::Connector::Rails::UserOrganizationRel'
|
19
|
+
has_many :organizations, through: :user_organization_rels, class_name: 'Maestrano::Connector::Rails::Organization'
|
20
|
+
|
21
|
+
#===================================
|
22
|
+
# Validation
|
23
|
+
#===================================
|
24
|
+
validates :email, presence: true
|
25
|
+
validates :tenant, presence: true
|
26
|
+
end
|
27
|
+
end
|
@@ -1,158 +1,5 @@
|
|
1
1
|
module Maestrano::Connector::Rails
|
2
2
|
class Organization < ActiveRecord::Base
|
3
|
-
|
4
|
-
maestrano_group_via :provider, :uid, :tenant do |group, maestrano|
|
5
|
-
group.name = (maestrano.name.blank? ? 'Default Group name' : maestrano.name)
|
6
|
-
group.tenant = 'default' # To be set from SSO parameter
|
7
|
-
group.org_uid = maestrano.org_uid # Maestrano organization UID
|
8
|
-
|
9
|
-
# group.country_alpha2 = maestrano.country
|
10
|
-
# group.free_trial_end_at = maestrano.free_trial_end_at
|
11
|
-
# group.some_required_field = 'some-appropriate-default-value'
|
12
|
-
end
|
13
|
-
|
14
|
-
def initialize
|
15
|
-
super
|
16
|
-
self.synchronized_entities = {}
|
17
|
-
External.entities_list.each do |entity|
|
18
|
-
self.synchronized_entities[entity.to_sym] = {can_push_to_connec: !self.pull_disabled, can_push_to_external: !self.push_disabled}
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# Callbacks
|
23
|
-
before_save :update_metadata
|
24
|
-
|
25
|
-
#===================================
|
26
|
-
# Encryptions
|
27
|
-
#===================================
|
28
|
-
attr_encrypted_options[:mode] = :per_attribute_iv_and_salt
|
29
|
-
attr_encrypted :oauth_token, key: ::Settings.encryption_key1
|
30
|
-
attr_encrypted :refresh_token, key: ::Settings.encryption_key2
|
31
|
-
|
32
|
-
#===================================
|
33
|
-
# Associations
|
34
|
-
#===================================
|
35
|
-
has_many :user_organization_rels
|
36
|
-
has_many :users, through: :user_organization_rels
|
37
|
-
has_many :id_maps, dependent: :destroy
|
38
|
-
has_many :synchronizations, dependent: :destroy
|
39
|
-
|
40
|
-
#===================================
|
41
|
-
# Validation
|
42
|
-
#===================================
|
43
|
-
validates :name, presence: true
|
44
|
-
validates :tenant, presence: true
|
45
|
-
validates :uid, uniqueness: {scope: :tenant}
|
46
|
-
validates :oauth_uid, uniqueness: {allow_blank: true, message: 'This account has already been linked'}
|
47
|
-
|
48
|
-
#===================================
|
49
|
-
# Serialized field
|
50
|
-
#===================================
|
51
|
-
serialize :synchronized_entities
|
52
|
-
|
53
|
-
def displayable_synchronized_entities
|
54
|
-
result = {}
|
55
|
-
synchronized_entities.each do |entity, hash|
|
56
|
-
begin
|
57
|
-
clazz = "Entities::#{entity.to_s.titleize.split.join}".constantize
|
58
|
-
rescue
|
59
|
-
next
|
60
|
-
end
|
61
|
-
result[entity] = {connec_name: clazz.public_connec_entity_name, external_name: clazz.public_external_entity_name}.merge(hash)
|
62
|
-
end
|
63
|
-
result
|
64
|
-
end
|
65
|
-
|
66
|
-
def add_member(user)
|
67
|
-
user_organization_rels.create(user: user) if tenant == user.tenant && !member?(user)
|
68
|
-
end
|
69
|
-
|
70
|
-
def member?(user)
|
71
|
-
user_organization_rels.where(user_id: user.id).count.positive?
|
72
|
-
end
|
73
|
-
|
74
|
-
def remove_member(user)
|
75
|
-
user_organization_rels.where(user_id: user.id).delete_all
|
76
|
-
end
|
77
|
-
|
78
|
-
def from_omniauth(auth)
|
79
|
-
self.oauth_provider = auth.provider
|
80
|
-
self.oauth_uid = auth.uid
|
81
|
-
self.oauth_token = auth.credentials.token
|
82
|
-
self.refresh_token = auth.credentials.refresh_token
|
83
|
-
self.instance_url = auth.credentials.instance_url
|
84
|
-
self.save
|
85
|
-
end
|
86
|
-
|
87
|
-
def clear_omniauth
|
88
|
-
self.oauth_uid = nil
|
89
|
-
self.oauth_token = nil
|
90
|
-
self.refresh_token = nil
|
91
|
-
self.sync_enabled = false
|
92
|
-
self.save
|
93
|
-
end
|
94
|
-
|
95
|
-
# Enable historical data sharing (prior to account linking)
|
96
|
-
def enable_historical_data(enabled)
|
97
|
-
# Historical data sharing cannot be unset
|
98
|
-
return if self.historical_data
|
99
|
-
|
100
|
-
if enabled
|
101
|
-
self.date_filtering_limit = nil
|
102
|
-
self.historical_data = true
|
103
|
-
else
|
104
|
-
self.date_filtering_limit ||= Time.now.getlocal
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def last_three_synchronizations_failed?
|
109
|
-
arr = synchronizations.last(3).map(&:error?)
|
110
|
-
arr.count == 3 && arr.uniq == [true]
|
111
|
-
end
|
112
|
-
|
113
|
-
def last_successful_synchronization
|
114
|
-
synchronizations.where(status: 'SUCCESS', partial: false).order(updated_at: :desc).first
|
115
|
-
end
|
116
|
-
|
117
|
-
def last_synchronization_date
|
118
|
-
last_successful_synchronization&.updated_at || date_filtering_limit
|
119
|
-
end
|
120
|
-
|
121
|
-
def reset_synchronized_entities(default = false)
|
122
|
-
synchronized_entities.slice!(*External.entities_list.map(&:to_sym))
|
123
|
-
External.entities_list.each do |entity|
|
124
|
-
if synchronized_entities[entity.to_sym].is_a?(Hash)
|
125
|
-
can_push_to_external = synchronized_entities[entity.to_sym][:can_push_to_external]
|
126
|
-
can_push_to_connec = synchronized_entities[entity.to_sym][:can_push_to_connec]
|
127
|
-
else
|
128
|
-
can_push_to_external = synchronized_entities[entity.to_sym]
|
129
|
-
can_push_to_connec = synchronized_entities[entity.to_sym]
|
130
|
-
end
|
131
|
-
synchronized_entities[entity.to_sym] = {can_push_to_connec: (can_push_to_connec || default) && !pull_disabled, can_push_to_external: (can_push_to_external || default) && !push_disabled}
|
132
|
-
end
|
133
|
-
save
|
134
|
-
end
|
135
|
-
|
136
|
-
def push_to_connec_enabled?(entity)
|
137
|
-
synchronized_entities.dig(EntityHelper.snake_name(entity), :can_push_to_connec) && entity&.class&.can_write_connec?
|
138
|
-
end
|
139
|
-
|
140
|
-
def push_to_external_enabled?(entity)
|
141
|
-
synchronized_entities.dig(EntityHelper.snake_name(entity), :can_push_to_external) && entity&.class&.can_write_external?
|
142
|
-
end
|
143
|
-
|
144
|
-
def set_instance_metadata
|
145
|
-
auth = {username: Maestrano[tenant].param('api.id'), password: Maestrano[tenant].param('api.key')}
|
146
|
-
res = HTTParty.get("#{Maestrano[tenant].param('api.host')}/api/v1/account/groups/#{uid}", basic_auth: auth)
|
147
|
-
response = JSON.parse(res.body)
|
148
|
-
|
149
|
-
self.push_disabled = response.dig('data', 'metadata', 'push_disabled')
|
150
|
-
self.pull_disabled = response.dig('data', 'metadata', 'pull_disabled')
|
151
|
-
end
|
152
|
-
|
153
|
-
def update_metadata
|
154
|
-
self.set_instance_metadata
|
155
|
-
self.enable_historical_data(true) if self.push_disabled
|
156
|
-
end
|
3
|
+
include Maestrano::Connector::Rails::Concerns::Organization
|
157
4
|
end
|
158
5
|
end
|
@@ -1,50 +1,5 @@
|
|
1
1
|
module Maestrano::Connector::Rails
|
2
2
|
class Synchronization < ActiveRecord::Base
|
3
|
-
|
4
|
-
after_create :clean_synchronizations
|
5
|
-
|
6
|
-
RUNNING_STATUS = 'RUNNING'.freeze
|
7
|
-
ERROR_STATUS = 'ERROR'.freeze
|
8
|
-
SUCCESS_STATUS = 'SUCCESS'.freeze
|
9
|
-
|
10
|
-
#===================================
|
11
|
-
# Associations
|
12
|
-
#===================================
|
13
|
-
belongs_to :organization
|
14
|
-
|
15
|
-
validates :status, presence: true
|
16
|
-
|
17
|
-
def running?
|
18
|
-
status == RUNNING_STATUS
|
19
|
-
end
|
20
|
-
|
21
|
-
def error?
|
22
|
-
status == ERROR_STATUS
|
23
|
-
end
|
24
|
-
|
25
|
-
def success?
|
26
|
-
status == SUCCESS_STATUS
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.create_running(organization)
|
30
|
-
Synchronization.create(organization_id: organization.id, status: RUNNING_STATUS)
|
31
|
-
end
|
32
|
-
|
33
|
-
def set_success
|
34
|
-
update_attributes(status: SUCCESS_STATUS)
|
35
|
-
end
|
36
|
-
|
37
|
-
def set_error(msg)
|
38
|
-
update_attributes(status: ERROR_STATUS, message: msg)
|
39
|
-
end
|
40
|
-
|
41
|
-
def set_partial
|
42
|
-
update_attributes(partial: true)
|
43
|
-
end
|
44
|
-
|
45
|
-
def clean_synchronizations
|
46
|
-
count = organization.synchronizations.count
|
47
|
-
organization.synchronizations.order('id ASC').limit(count - 100).destroy_all if count > 100
|
48
|
-
end
|
3
|
+
include Maestrano::Connector::Rails::Concerns::Synchronization
|
49
4
|
end
|
50
5
|
end
|
@@ -1,25 +1,5 @@
|
|
1
1
|
module Maestrano::Connector::Rails
|
2
2
|
class User < ActiveRecord::Base
|
3
|
-
|
4
|
-
maestrano_user_via :provider, :uid, :tenant do |user, maestrano|
|
5
|
-
user.uid = maestrano.uid
|
6
|
-
user.provider = maestrano.provider
|
7
|
-
user.first_name = maestrano.first_name
|
8
|
-
user.last_name = maestrano.last_name
|
9
|
-
user.email = maestrano.email
|
10
|
-
user.tenant = 'default' # To be set from SSO parameter
|
11
|
-
end
|
12
|
-
|
13
|
-
#===================================
|
14
|
-
# Associations
|
15
|
-
#===================================
|
16
|
-
has_many :user_organization_rels
|
17
|
-
has_many :organizations, through: :user_organization_rels
|
18
|
-
|
19
|
-
#===================================
|
20
|
-
# Validation
|
21
|
-
#===================================
|
22
|
-
validates :email, presence: true
|
23
|
-
validates :tenant, presence: true
|
3
|
+
include Maestrano::Connector::Rails::Concerns::User
|
24
4
|
end
|
25
5
|
end
|
data/config/routes.rb
CHANGED
@@ -4,13 +4,13 @@ Maestrano::Connector::Rails::Engine.routes.draw do
|
|
4
4
|
get 'version', to: 'version#index'
|
5
5
|
|
6
6
|
namespace :maestrano do
|
7
|
-
match 'signout', to: 'sessions#destroy', as: 'signout', via: [
|
7
|
+
match 'signout', to: 'sessions#destroy', as: 'signout', via: %i[get post]
|
8
8
|
post 'connec/notifications/:tenant' => 'connec#notifications'
|
9
9
|
|
10
10
|
resources :dependancies, only: [:index]
|
11
11
|
|
12
12
|
scope ':tenant' do
|
13
|
-
resources :synchronizations, only: [
|
13
|
+
resources :synchronizations, only: %i[show create] do
|
14
14
|
collection do
|
15
15
|
put :toggle_sync
|
16
16
|
put :update_metadata
|
@@ -33,6 +33,11 @@ module Connector
|
|
33
33
|
copy_file 'example_entity_spec.rb', 'spec/models/entities/example_entitiy.rb'
|
34
34
|
end
|
35
35
|
|
36
|
+
def copy_icons_and_logos
|
37
|
+
copy_file 'logos/to_connec.png', 'app/assets/images/logos/to_connec.png'
|
38
|
+
copy_file 'logos/to_external.png', 'spec/assets/images/logos/to_external.png'
|
39
|
+
end
|
40
|
+
|
36
41
|
def copy_controllers_and_views
|
37
42
|
copy_file 'home_controller.rb', 'app/controllers/home_controller.rb'
|
38
43
|
copy_file 'home_controller_spec.rb', 'spec/controllers/home_controller_spec.rb'
|
@@ -10,7 +10,7 @@ class HomeController < ApplicationController
|
|
10
10
|
end
|
11
11
|
full_sync = params['historical-data'].present? && !current_organization.historical_data
|
12
12
|
opts = {full_sync: full_sync}
|
13
|
-
current_organization.sync_enabled = current_organization.synchronized_entities.values.any?
|
13
|
+
current_organization.sync_enabled = current_organization.synchronized_entities.values.any? { |settings| settings.values.any? { |v| v } }
|
14
14
|
current_organization.enable_historical_data(params['historical-data'].present?)
|
15
15
|
trigger_sync = current_organization.sync_enabled
|
16
16
|
current_organization.save
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe HomeController, type: :controller do
|
4
4
|
let(:back_path) { home_index_path }
|
5
5
|
|
6
|
-
before(:each) { request.env['HTTP_REFERER'] = back_path
|
6
|
+
before(:each) { request.env['HTTP_REFERER'] = back_path}
|
7
7
|
|
8
8
|
describe 'index' do
|
9
9
|
subject { get :index }
|
@@ -13,12 +13,14 @@ describe HomeController, type: :controller do
|
|
13
13
|
|
14
14
|
describe 'update' do
|
15
15
|
let(:user) { create(:user) }
|
16
|
-
let(:organization) { create(:organization, synchronized_entities: {'people' => false, 'item' => true}) }
|
16
|
+
let(:organization) { create(:organization, synchronized_entities: {'people' => {can_push_to_connec: false, can_push_to_external: false}, 'item' => {can_push_to_connec: true, can_push_to_external: true}}) }
|
17
17
|
|
18
|
-
before
|
19
|
-
|
18
|
+
before do
|
19
|
+
allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_user).and_return(user)
|
20
|
+
allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_organization).and_return(organization)
|
21
|
+
end
|
20
22
|
|
21
|
-
subject { put :update, id: organization.id, 'people' =>
|
23
|
+
subject { put :update, id: organization.id, 'people' => {to_connec: '1', to_external: '1'}, 'item' => {}, 'lala' => {} }
|
22
24
|
|
23
25
|
context 'when user is not admin' do
|
24
26
|
before { allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:is_admin).and_return(false) }
|
@@ -34,7 +36,7 @@ describe HomeController, type: :controller do
|
|
34
36
|
it 'updates organization synchronized_entities' do
|
35
37
|
subject
|
36
38
|
organization.reload
|
37
|
-
expect(organization.synchronized_entities).to eq('people' => true, 'item' => false)
|
39
|
+
expect(organization.synchronized_entities).to eq('people' => {can_push_to_connec: true, can_push_to_external: true}, 'item' => {can_push_to_connec: false, can_push_to_external: false})
|
38
40
|
end
|
39
41
|
|
40
42
|
it 'updates organization sync_enabled' do
|
@@ -44,7 +46,7 @@ describe HomeController, type: :controller do
|
|
44
46
|
end
|
45
47
|
|
46
48
|
context 'when removing all entities' do
|
47
|
-
subject { put :update, id: organization.id, 'people' =>
|
49
|
+
subject { put :update, id: organization.id, 'people' => {}, 'item' => {} }
|
48
50
|
before { organization.update(sync_enabled: true) }
|
49
51
|
|
50
52
|
it 'set sync_enabled to false' do
|
@@ -58,10 +60,12 @@ describe HomeController, type: :controller do
|
|
58
60
|
|
59
61
|
describe 'synchronize' do
|
60
62
|
let(:user) { create(:user) }
|
61
|
-
let(:organization) { create(:organization, synchronized_entities: {'people' => false, 'item' => true}) }
|
63
|
+
let(:organization) { create(:organization, synchronized_entities: {'people' => {can_push_to_connec: false, can_push_to_external: false}, 'item' => {can_push_to_connec: true, can_push_to_external: true}}) }
|
62
64
|
|
63
|
-
before
|
64
|
-
|
65
|
+
before do
|
66
|
+
allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_user).and_return(user)
|
67
|
+
allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_organization).and_return(organization)
|
68
|
+
end
|
65
69
|
|
66
70
|
subject { post :synchronize }
|
67
71
|
|
@@ -102,10 +106,14 @@ describe HomeController, type: :controller do
|
|
102
106
|
end
|
103
107
|
|
104
108
|
describe 'redirect_to_external' do
|
109
|
+
let(:user) { create(:user) }
|
110
|
+
let(:organization) { create(:organization) }
|
105
111
|
subject { get :redirect_to_external }
|
106
112
|
|
107
|
-
|
108
|
-
|
113
|
+
before do
|
114
|
+
allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_organization).and_return(organization)
|
109
115
|
end
|
116
|
+
|
117
|
+
it {expect(subject).to redirect_to('https://somewhere.com')}
|
110
118
|
end
|
111
119
|
end
|
@@ -9,7 +9,7 @@
|
|
9
9
|
-if current_organization
|
10
10
|
Link your company <strong>#{current_organization.name} (#{current_organization.uid})</strong> to ApplicationName to get your business in synch. Check the status of your connection on this screen.
|
11
11
|
-else
|
12
|
-
|
12
|
+
ApplicationName add-on is a microservice providing data synchronization between your platform and ApplicationName
|
13
13
|
|
14
14
|
.container
|
15
15
|
- if current_user
|
@@ -127,9 +127,3 @@
|
|
127
127
|
.row
|
128
128
|
.col-md-4.col-md-offset-4.text-center
|
129
129
|
= link_to 'Go to ApplicationName', home_redirect_to_external_path, class: 'btn btn-lg btn-primary'
|
130
|
-
|
131
|
-
- else
|
132
|
-
.row
|
133
|
-
.col-md-4.col-md-offset-4.center
|
134
|
-
= link_to 'Link your Maestrano account', Maestrano::Connector::Rails::Engine.routes.url_helpers.default_maestrano_auth_saml_index_path(tenant: :default), class: 'btn btn-warning'
|
135
|
-
|
@@ -4,8 +4,8 @@
|
|
4
4
|
%html
|
5
5
|
%head
|
6
6
|
%title ApplicationName Connector
|
7
|
-
= stylesheet_link_tag 'application', media: 'all'
|
8
|
-
= javascript_include_tag 'application'
|
7
|
+
= stylesheet_link_tag 'application', media: 'all'
|
8
|
+
= javascript_include_tag 'application'
|
9
9
|
%script{src: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js', integrity: 'sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa', crossorigin: 'anonymous'}
|
10
10
|
= csrf_meta_tags
|
11
11
|
%body
|
Binary file
|
Binary file
|
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
|
|
31
31
|
s.add_runtime_dependency('bootstrap-sass')
|
32
32
|
s.add_runtime_dependency('config')
|
33
33
|
s.add_runtime_dependency('figaro')
|
34
|
+
s.add_runtime_dependency('jquery-rails')
|
34
35
|
s.add_runtime_dependency('haml-rails')
|
35
36
|
s.add_runtime_dependency('hash_mapper', '>= 0.2.2')
|
36
37
|
|
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: 2.0.2.pre.
|
4
|
+
version: 2.0.2.pre.RC10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Maestrano
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-04-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -108,6 +108,20 @@ dependencies:
|
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: jquery-rails
|
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'
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
126
|
name: haml-rails
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -393,6 +407,7 @@ files:
|
|
393
407
|
- app/helpers/maestrano/connector/rails/entity_helper.rb
|
394
408
|
- app/helpers/maestrano/connector/rails/session_helper.rb
|
395
409
|
- app/jobs/maestrano/connector/rails/all_synchronizations_job.rb
|
410
|
+
- app/jobs/maestrano/connector/rails/concerns/synchronization_job.rb
|
396
411
|
- app/jobs/maestrano/connector/rails/push_to_connec_job.rb
|
397
412
|
- app/jobs/maestrano/connector/rails/push_to_connec_worker.rb
|
398
413
|
- app/jobs/maestrano/connector/rails/synchronization_job.rb
|
@@ -403,7 +418,11 @@ files:
|
|
403
418
|
- app/models/maestrano/connector/rails/concerns/entity.rb
|
404
419
|
- app/models/maestrano/connector/rails/concerns/entity_base.rb
|
405
420
|
- app/models/maestrano/connector/rails/concerns/external.rb
|
421
|
+
- app/models/maestrano/connector/rails/concerns/id_map.rb
|
422
|
+
- app/models/maestrano/connector/rails/concerns/organization.rb
|
406
423
|
- app/models/maestrano/connector/rails/concerns/sub_entity_base.rb
|
424
|
+
- app/models/maestrano/connector/rails/concerns/synchronization.rb
|
425
|
+
- app/models/maestrano/connector/rails/concerns/user.rb
|
407
426
|
- app/models/maestrano/connector/rails/connec_helper.rb
|
408
427
|
- app/models/maestrano/connector/rails/connector_logger.rb
|
409
428
|
- app/models/maestrano/connector/rails/entity.rb
|
@@ -451,6 +470,8 @@ files:
|
|
451
470
|
- lib/generators/connector/templates/home_controller_spec.rb
|
452
471
|
- lib/generators/connector/templates/home_index.haml
|
453
472
|
- lib/generators/connector/templates/layouts.haml
|
473
|
+
- lib/generators/connector/templates/logos/to_connec.png
|
474
|
+
- lib/generators/connector/templates/logos/to_external.png
|
454
475
|
- lib/generators/connector/templates/oauth_controller.rb
|
455
476
|
- lib/generators/connector/templates/shared_entities_controller.rb
|
456
477
|
- lib/generators/connector/templates/shared_entities_controller_spec.rb
|
@@ -490,7 +511,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
490
511
|
version: 1.3.1
|
491
512
|
requirements: []
|
492
513
|
rubyforge_project:
|
493
|
-
rubygems_version: 2.
|
514
|
+
rubygems_version: 2.5.1
|
494
515
|
signing_key:
|
495
516
|
specification_version: 4
|
496
517
|
summary: Rails framework to build connector with Maestrano
|