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.
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