maestrano-connector-rails 2.0.2.pre.RC9 → 2.0.2.pre.RC10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (26) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/Rakefile +1 -1
  4. data/app/jobs/maestrano/connector/rails/concerns/synchronization_job.rb +153 -0
  5. data/app/jobs/maestrano/connector/rails/synchronization_job.rb +1 -144
  6. data/app/models/maestrano/connector/rails/concerns/connector_logger.rb +1 -1
  7. data/app/models/maestrano/connector/rails/concerns/entity.rb +3 -3
  8. data/app/models/maestrano/connector/rails/concerns/id_map.rb +8 -0
  9. data/app/models/maestrano/connector/rails/concerns/organization.rb +161 -0
  10. data/app/models/maestrano/connector/rails/concerns/synchronization.rb +54 -0
  11. data/app/models/maestrano/connector/rails/concerns/user.rb +27 -0
  12. data/app/models/maestrano/connector/rails/id_map.rb +1 -2
  13. data/app/models/maestrano/connector/rails/organization.rb +1 -154
  14. data/app/models/maestrano/connector/rails/synchronization.rb +1 -46
  15. data/app/models/maestrano/connector/rails/user.rb +1 -21
  16. data/config/routes.rb +2 -2
  17. data/lib/generators/connector/install_generator.rb +5 -0
  18. data/lib/generators/connector/templates/home_controller.rb +1 -1
  19. data/lib/generators/connector/templates/home_controller_spec.rb +20 -12
  20. data/lib/generators/connector/templates/home_index.haml +1 -7
  21. data/lib/generators/connector/templates/layouts.haml +2 -2
  22. data/lib/generators/connector/templates/logos/to_connec.png +0 -0
  23. data/lib/generators/connector/templates/logos/to_external.png +0 -0
  24. data/lib/maestrano/connector/rails/version.rb +1 -1
  25. data/maestrano-connector-rails.gemspec +1 -0
  26. metadata +24 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d3664893bd3a248abdb2607dd361f36fa3b8a2a4
4
- data.tar.gz: 4ee24001e70b0181b894e7f769f454a2bfc6a1fd
3
+ metadata.gz: 65e017e72e894da3e1fadf4eda7d24580fe90176
4
+ data.tar.gz: 09168f2321092da1b57490468516225190cd9e64
5
5
  SHA512:
6
- metadata.gz: f9471077316bf074b73bf7ec3e375355d07c0324d78fd52f80a09b4a8b86d78bef4fd624ef17a64f64cafc06da3ca4865d8caed932d212c4c72e0470316b6d6e
7
- data.tar.gz: d268f0fbd74269c830e0d708886abc00e11ed48dea71394ad6d8b223d16fefb838f7a504c93471dbc28ab7650d29800930aeb8c106a0b0455c804b94f6c2667d
6
+ metadata.gz: cb5e4891f48db210ddf0a2881fe3cffaabe1850b42eebfb0988f02f1ef49a13f93984ba4f59b7bed1c6a26a740bc08023e173c25dfa3f4f3661aa01514cc50db
7
+ data.tar.gz: baea5af0aca2aac129786700ea1abf1df7a2d1969821cd54f97185c22d3ad338ead03a75d6d13e57915bc11ab2f2d63a9115949decbce5b57b4d21a3c4364630
data/.gitignore CHANGED
@@ -3,4 +3,5 @@ spec/dummy/db
3
3
  spec/dummy/tmp
4
4
  /coverage
5
5
  /pkg
6
- Gemfile.lock
6
+ Gemfile.lock
7
+ **/.DS_STORE
data/Rakefile CHANGED
@@ -14,4 +14,4 @@ load 'rails/tasks/engine.rake'
14
14
  RuboCop::RakeTask.new
15
15
  RSpec::Core::RakeTask.new(spec: 'app:db:test:prepare')
16
16
 
17
- task default: [:spec, :rubocop]
17
+ task default: %i[spec rubocop]
@@ -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
- queue_as :default
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(', ')}" unless params.blank?
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.select! { |mapped_external_entity_with_idmap| !mapped_external_entity_with_idmap[:idmap].connec_id }
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 && !response.body.blank?
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 && !response.body.blank?
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,8 @@
1
+ module Maestrano::Connector::Rails::Concerns::IdMap
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ belongs_to :organization
6
+ serialize :metadata, Hash
7
+ end
8
+ end
@@ -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,6 +1,5 @@
1
1
  module Maestrano::Connector::Rails
2
2
  class IdMap < ActiveRecord::Base
3
- belongs_to :organization
4
- serialize :metadata, Hash
3
+ include Maestrano::Connector::Rails::Concerns::IdMap
5
4
  end
6
5
  end
@@ -1,158 +1,5 @@
1
1
  module Maestrano::Connector::Rails
2
2
  class Organization < ActiveRecord::Base
3
- # Enable Maestrano for this group
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
- # Keeping only 100 synchronizations per organization
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
- # Enable Maestrano for this user
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: [:get, :post]
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: [:show, :create] do
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 { allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_user).and_return(user) }
19
- before { allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_organization).and_return(organization) }
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' => true, 'item' => false, 'lala' => true }
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' => false, 'item' => false }
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 { allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_user).and_return(user) }
64
- before { allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_organization).and_return(organization) }
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
- context 'otherwise' do
108
- it { expect(subject).to redirect_to('somewhere') }
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
- Link your account to ApplicationName to get your business in synch. Check the status of your connection on this screen.
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', 'data-turbolinks-track' => true
8
- = javascript_include_tag 'application', 'data-turbolinks-track' => true
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
@@ -1,7 +1,7 @@
1
1
  module Maestrano
2
2
  module Connector
3
3
  module Rails
4
- VERSION = '2.0.2.pre.RC9'.freeze
4
+ VERSION = '2.0.2.pre.RC10'.freeze
5
5
  end
6
6
  end
7
7
  end
@@ -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.RC9
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-03-23 00:00:00.000000000 Z
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.6.8
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