maestrano-connector-rails 0.2.16 → 0.2.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +27 -0
  4. data/VERSION +1 -1
  5. data/app/helpers/maestrano/connector/rails/session_helper.rb +5 -1
  6. data/app/models/maestrano/connector/rails/complex_entity.rb +14 -33
  7. data/app/models/maestrano/connector/rails/concerns/entity.rb +161 -73
  8. data/app/models/maestrano/connector/rails/sub_entity_base.rb +44 -4
  9. data/lib/generators/connector/install_generator.rb +20 -24
  10. data/lib/generators/connector/templates/home_controller.rb +33 -10
  11. data/lib/generators/connector/templates/home_controller_spec.rb +141 -0
  12. data/lib/generators/connector/templates/home_index.haml +103 -0
  13. data/lib/generators/connector/templates/layouts.haml +45 -0
  14. data/lib/generators/connector/templates/oauth_controller.rb +3 -6
  15. data/lib/generators/connector/templates/shared_entities_controller.rb +7 -0
  16. data/lib/generators/connector/templates/shared_entities_controller_spec.rb +23 -0
  17. data/lib/generators/connector/templates/shared_entities_index.haml +41 -0
  18. data/lib/generators/connector/templates/stylesheets/application.sass +24 -0
  19. data/lib/generators/connector/templates/stylesheets/banners.sass +59 -0
  20. data/lib/generators/connector/templates/stylesheets/home.sass +25 -0
  21. data/lib/generators/connector/templates/stylesheets/layout.sass +125 -0
  22. data/lib/generators/connector/templates/stylesheets/spacers.sass +46 -0
  23. data/lib/generators/connector/templates/stylesheets/variables.sass +57 -0
  24. data/lib/generators/connector/templates/sychronizations_controller_spec.rb +22 -0
  25. data/lib/generators/connector/templates/synchronizations_controller.rb +7 -0
  26. data/lib/generators/connector/templates/synchronizations_index.haml +42 -0
  27. data/maestrano-connector-rails.gemspec +29 -6
  28. data/pkg/maestrano-connector-rails-0.2.16.gem +0 -0
  29. data/spec/models/complex_entity_spec.rb +46 -12
  30. data/spec/models/entity_spec.rb +212 -113
  31. data/spec/models/sub_entity_base_spec.rb +59 -0
  32. data/template/maestrano-connector-template.rb +4 -3
  33. data/template/routes.rb +14 -0
  34. metadata +61 -5
  35. data/lib/generators/connector/templates/admin_controller.rb +0 -58
  36. data/lib/generators/connector/templates/admin_index.html.erb +0 -55
  37. data/lib/generators/connector/templates/home_index.html.erb +0 -48
@@ -14,19 +14,59 @@ module Maestrano::Connector::Rails
14
14
  end
15
15
 
16
16
  def external_entity_name
17
- if self.external?
18
- self.entity_name
17
+ if external?
18
+ entity_name
19
19
  else
20
20
  raise "Forbidden call"
21
21
  end
22
22
  end
23
23
 
24
24
  def connec_entity_name
25
- if self.external?
25
+ if external?
26
26
  raise "Forbidden call"
27
27
  else
28
- self.entity_name
28
+ entity_name
29
29
  end
30
30
  end
31
+
32
+ def names_hash
33
+ if external?
34
+ {external_entity: entity_name.downcase}
35
+ else
36
+ {connec_entity: entity_name.downcase}
37
+ end
38
+ end
39
+
40
+ def create_idmap_from_external_entity(entity, connec_entity_name, organization)
41
+ if external?
42
+ h = names_hash.merge({
43
+ external_id: get_id_from_external_entity_hash(entity),
44
+ name: object_name_from_external_entity_hash(entity),
45
+ connec_entity: connec_entity_name.downcase,
46
+ organization_id: organization.id
47
+ })
48
+ Maestrano::Connector::Rails::IdMap.create(h)
49
+ else
50
+ raise 'Forbidden call'
51
+ end
52
+ end
53
+
54
+ def create_idmap_from_connec_entity(entity, external_entity_name, organization)
55
+ if external?
56
+ raise 'Forbidden call'
57
+ else
58
+ h = names_hash.merge({
59
+ connec_id: entity['id'],
60
+ name: object_name_from_connec_entity_hash(entity),
61
+ external_entity: external_entity_name.downcase,
62
+ organization_id: organization.id
63
+ })
64
+ Maestrano::Connector::Rails::IdMap.create(h)
65
+ end
66
+ end
67
+
68
+ def map_external_entity_with_idmap(external_entity, connec_entity_name, idmap, organization)
69
+ {entity: map_to(connec_entity_name, external_entity, organization), idmap: idmap}
70
+ end
31
71
  end
32
72
  end
@@ -21,25 +21,6 @@ module Connector
21
21
  end
22
22
  end
23
23
 
24
- def include_routes
25
- sentinel = 'Rails.application.routes.draw do'
26
- code_lines = [
27
- "mount Maestrano::Connector::Rails::Engine, at: '/'\n",
28
- "root 'home#index'",
29
- "get 'home/index' => 'home#index'",
30
- "get 'admin/index' => 'admin#index'",
31
- "put 'admin/update' => 'admin#update'",
32
- "post 'admin/synchronize' => 'admin#synchronize'",
33
- "put 'admin/toggle_sync' => 'admin#toggle_sync'"
34
- ]
35
-
36
- in_root do
37
- gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
38
- "#{match}\n #{code_lines.join("\n ")}\n"
39
- end
40
- end
41
- end
42
-
43
24
  def copy_entity
44
25
  copy_file 'entity.rb', 'app/models/maestrano/connector/rails/entity.rb'
45
26
  end
@@ -52,14 +33,29 @@ module Connector
52
33
  copy_file 'example_entity.rb', 'app/models/entities/example_entitiy.rb'
53
34
  end
54
35
 
55
- def copy_home
36
+ def copy_controllers_and_views
56
37
  copy_file 'home_controller.rb', 'app/controllers/home_controller.rb'
57
- copy_file 'home_index.html.erb', 'app/views/home/index.html.erb'
38
+ copy_file 'home_controller_spec.rb', 'spec/controllers/home_controller_spec.rb'
39
+ copy_file 'home_index.haml', 'app/views/home/index.html.haml'
40
+
41
+ copy_file 'synchronizations_controller.rb', 'app/controllers/synchronizations_controller.rb'
42
+ copy_file 'synchronizations_controller_spec.rb', 'spec/controllers/synchronizations_controller_spec.rb'
43
+ copy_file 'synchronizations_index.haml', 'app/views/synchronizations/index.html.haml'
44
+
45
+ copy_file 'shared_entities_controller.rb', 'app/controllers/shared_entities_controller.rb'
46
+ copy_file 'shared_entities_controller_spec.rb', 'spec/controllers/shared_entities_controller_spec.rb'
47
+ copy_file 'shared_entities_index.haml', 'app/views/shared_entities/index.html.haml'
48
+
49
+ copy_file 'layouts.haml', 'app/views/layouts/application.html.haml'
58
50
  end
59
51
 
60
- def copy_admin_view
61
- copy_file 'admin_controller.rb', 'app/controllers/admin_controller.rb'
62
- copy_file 'admin_index.html.erb', 'app/views/admin/index.html.erb'
52
+ def copy_stylesheets
53
+ copy_file 'stylesheets/application.sass', 'app/assets/stylesheets/application.sass'
54
+ copy_file 'stylesheets/banners.sass', 'app/assets/stylesheets/banners.sass'
55
+ copy_file 'stylesheets/home.sass', 'app/assets/stylesheets/home.sass'
56
+ copy_file 'stylesheets/layout.sass', 'app/assets/stylesheets/layout.sass'
57
+ copy_file 'stylesheets/spacers.sass', 'app/assets/stylesheets/spacers.sass'
58
+ copy_file 'stylesheets/variables.sass', 'app/assets/stylesheets/variables.sass'
63
59
  end
64
60
 
65
61
  def copy_oauth_controller
@@ -1,17 +1,40 @@
1
- # TODO
2
- # This controller is given as an example of a possible home
3
- # Home funcitons :
4
- # * Display generic information about the connector (mostly for not connected users)
5
- # * Link the connector to a Maestrano organization
6
- # * Acces the last synchronization and a synchronization history (with their status)
7
1
  class HomeController < ApplicationController
8
2
  def index
9
- if current_user
10
- @organization = current_organization
3
+ @organization = current_organization
4
+ end
5
+
6
+ def update
7
+ organization = Maestrano::Connector::Rails::Organization.find_by_id(params[:id])
11
8
 
12
- if @organization
13
- @synchronizations = Maestrano::Connector::Rails::Synchronization.where(organization_id: @organization.id).order(updated_at: :desc).limit(40)
9
+ if organization && is_admin?(current_user, organization)
10
+ organization.synchronized_entities.keys.each do |entity|
11
+ if !!params["#{entity}"]
12
+ organization.synchronized_entities[entity] = true
13
+ else
14
+ organization.synchronized_entities[entity] = false
15
+ end
14
16
  end
17
+ organization.save
18
+ end
19
+
20
+ redirect_to(:back)
21
+ end
22
+
23
+ def synchronize
24
+ if is_admin
25
+ Maestrano::Connector::Rails::SynchronizationJob.perform_later(current_organization, params['opts'] || {})
26
+ flash[:info] = 'Synchronization requested'
15
27
  end
28
+
29
+ redirect_to(:back)
30
+ end
31
+
32
+ def toggle_sync
33
+ if is_admin
34
+ current_organization.update(sync_enabled: !current_organization.sync_enabled)
35
+ flash[:info] = current_organization.sync_enabled ? 'Synchronization enabled' : 'Synchronization disabled'
36
+ end
37
+
38
+ redirect_to(:back)
16
39
  end
17
40
  end
@@ -0,0 +1,141 @@
1
+ require 'spec_helper'
2
+
3
+ describe HomeController, :type => :controller do
4
+ let(:back_path) { home_index_path }
5
+ before(:each) do
6
+ request.env["HTTP_REFERER"] = back_path
7
+ end
8
+
9
+ describe 'index' do
10
+ subject { get :index }
11
+
12
+ it { expect(subject).to be_success }
13
+ end
14
+
15
+ describe 'update' do
16
+ let(:organization) { create(:organization, synchronized_entities: {'people' => false, 'item' => true}) }
17
+ subject { put :update, id: organization.id, 'people' => true, 'item' => false, 'lala' => true }
18
+
19
+
20
+ context 'when user is not admin' do
21
+ before {
22
+ allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:is_admin?).and_return(false)
23
+ }
24
+
25
+ it { expect(subject).to redirect_to back_path }
26
+ end
27
+
28
+ context 'when user is admin' do
29
+ before {
30
+ allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:is_admin?).and_return(true)
31
+ }
32
+
33
+ it { expect(subject).to redirect_to back_path }
34
+
35
+ it 'updates organization' do
36
+ subject
37
+ organization.reload
38
+ expect(organization.synchronized_entities).to eq({'people' => true, 'item' => false})
39
+ end
40
+ end
41
+ end
42
+
43
+ describe 'synchronize' do
44
+ subject { post :synchronize }
45
+ let(:organization) { create(:organization) }
46
+ before {
47
+ allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_organization).and_return(organization)
48
+ }
49
+
50
+ context 'when user is not admin' do
51
+ before {
52
+ allow_any_instance_of(ApplicationHelper).to receive(:is_admin).and_return(false)
53
+ }
54
+
55
+ it { expect(subject).to redirect_to back_path }
56
+
57
+ it 'does nothing' do
58
+ expect(Maestrano::Connector::Rails::SynchronizationJob).to_not receive(:perform_later)
59
+ subject
60
+ end
61
+ end
62
+
63
+ context 'when user is admin' do
64
+ before {
65
+ allow_any_instance_of(ApplicationHelper).to receive(:is_admin).and_return(true)
66
+ }
67
+
68
+ it { expect(subject).to redirect_to back_path }
69
+
70
+ context 'with opts' do
71
+ subject { post :synchronize, opts: 'opts' }
72
+
73
+ it 'calls perform_later with opts' do
74
+ expect(Maestrano::Connector::Rails::SynchronizationJob).to receive(:perform_later).with(organization, 'opts')
75
+ subject
76
+ end
77
+ end
78
+
79
+ context 'without opts' do
80
+ subject { post :synchronize}
81
+
82
+ it 'calls perform_later with empty opts hash' do
83
+ expect(Maestrano::Connector::Rails::SynchronizationJob).to receive(:perform_later).with(organization, {})
84
+ subject
85
+ end
86
+ end
87
+ end
88
+
89
+ describe 'toggle_sync' do
90
+ subject { put :toggle_sync }
91
+ let(:organization) { create(:organization, sync_enabled: true) }
92
+ before {
93
+ allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_organization).and_return(organization)
94
+ }
95
+
96
+ context 'when user is not an admin' do
97
+ before {
98
+ allow_any_instance_of(ApplicationHelper).to receive(:is_admin).and_return(false)
99
+ }
100
+ it { expect(subject).to redirect_to back_path }
101
+
102
+ it 'does nothing' do
103
+ expect{ subject }.to_not change{ organization.sync_enabled }
104
+ end
105
+ end
106
+
107
+ context 'when user is admin' do
108
+ before {
109
+ allow_any_instance_of(ApplicationHelper).to receive(:is_admin).and_return(true)
110
+ }
111
+ it { expect(subject).to redirect_to back_path }
112
+
113
+ it 'change sync_enabled from true to false' do
114
+ expect{ subject }.to change{ organization.sync_enabled }.from(true).to(false)
115
+ end
116
+
117
+ it 'change sync_enabled from false to true' do
118
+ organization.update(sync_enabled: false)
119
+ expect{ subject }.to change{ organization.sync_enabled }.from(false).to(true)
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ describe 'redirect_to_external' do
126
+ subject { get :redirect_to_external }
127
+
128
+ context 'when organization has a redirect url' do
129
+ let(:organization) { create(:organization, instance_url: 'url') }
130
+ before {
131
+ allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_organization).and_return(organization)
132
+ }
133
+
134
+ it {expect(subject).to redirect_to('url')}
135
+ end
136
+
137
+ context 'otherwise' do
138
+ it {expect(subject).to redirect_to('https://login.salesforce.com')}
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,103 @@
1
+ / TODO
2
+ / Replace ApplicationName
3
+ .home
4
+ .banners
5
+ .row
6
+ .col-md-10.col-md-offset-2
7
+ %h2 ApplicationName Connector
8
+ %p
9
+ Link your Maestrano account to ApplicationName to get your business in synch. Check the status of your connection on this screen.
10
+
11
+ .container
12
+ - if current_user
13
+ - unless is_admin
14
+ .row
15
+ .col-md-12.alert.alert-warning
16
+ Only administrators can modify the application settings
17
+
18
+ .row.link-step
19
+ .col-md-1.text-center.link-step-number
20
+ %span.badge.link-step-badge
21
+ 1
22
+ .col-md-3.text-center
23
+ =image_tag "logos/default-white.png", class: 'logo'
24
+ .col-md-6.link-step-description
25
+ %h
26
+ Your Maestrano company <strong>#{@organization.name} (#{@organization.uid})</strong> is currently linked
27
+ .col-md-2.text-center.link-step-action
28
+ = link_to "Disconnect", Maestrano::Connector::Rails::Engine.routes.url_helpers.maestrano_signout_path, class: "btn btn-warning btn-lg #{is_admin ? '' : 'disabled'}"
29
+
30
+ .spacer1
31
+
32
+ .row.link-step
33
+ .col-md-1.text-center.link-step-number
34
+ %span.badge.link-step-badge
35
+ 2
36
+ .col-md-3.text-center
37
+ =image_tag "logos/external.png", class: 'logo'
38
+ .col-md-6.link-step-description
39
+ %h
40
+ - if @organization.oauth_uid
41
+ Your ApplicationName account <strong>#{@organization.oauth_name} (#{@organization.oauth_uid})</strong> is currently linked
42
+ - else
43
+ Your ApplicationName account is not linked
44
+ .col-md-2.text-center.link-step-action
45
+ - if @organization.oauth_uid
46
+ / TODO
47
+ = link_to "Disconnect", root_path, class: "btn btn-warning btn-lg #{is_admin ? '' : 'disabled'}"
48
+ - else
49
+ / TODO
50
+ = link_to "Link to ApplicationName", root_path, class: "btn btn-warning btn-lg #{is_admin ? '' : 'disabled'}"
51
+
52
+ .spacer1
53
+
54
+ .row.link-step
55
+ .col-md-1.text-center.link-step-number
56
+ %span.badge.link-step-badge
57
+ 3
58
+ .col-md-3.text-center
59
+ =image_tag "logos/sync.png", class: 'logo'
60
+ .col-md-6.link-step-description
61
+ %h
62
+ - if @organization.sync_enabled
63
+ Synchronizations are enabled
64
+ - else
65
+ Synchronizations are disabled
66
+ .col-md-2.text-center.link-step-action
67
+ = link_to "Toggle #{@organization.sync_enabled ? 'off' : 'on'}", home_toggle_sync_path, method: :put, class: "btn btn-warning btn-lg #{is_admin ? '' : 'disabled'}"
68
+
69
+ - else
70
+ .row
71
+ .col-md-4.col-md-offset-4.center
72
+ = 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'
73
+
74
+ - if @organization
75
+ .spacer2
76
+ .row
77
+ .col-md-12
78
+ %h1
79
+ Synchronized entities
80
+ %small You can customize which entities are synchronized by the connector
81
+
82
+ .spacer1
83
+ .row
84
+ .col-md-10.col-md-offset-1
85
+ = form_tag home_update_path(id: @organization.id), method: :put do
86
+ - @organization.synchronized_entities.each do |k, v|
87
+ .row.sync-entity
88
+ .col-md-1.link-step-action
89
+ %input{type: "checkbox", id: "#{k}", name: "#{k}", checked: v}
90
+ .col-md-7{style: 'padding-top: 5px;'}
91
+ %label{:for => "#{k}"} #{k.to_s.humanize}
92
+ -if is_admin
93
+ .col-md-4.text-right
94
+ - if v && @organization.oauth_uid
95
+ - if @organization.sync_enabled
96
+ = link_to "Force a synchronization for #{k.to_s.humanize.pluralize} only", home_synchronize_path(opts: {only_entities: [k.to_s]}), method: :post, class: "btn btn-warning btn-sm #{is_admin && @organization.sync_enabled ? '' : 'disabled'}"
97
+ - else
98
+ .btn.btn-warning.btn-sm.disabled= "Force a synchronization for #{k.to_s.humanize.pluralize} only"
99
+ -if is_admin
100
+ .spacer1
101
+ .row
102
+ .col-md-4.col-md-offset-8.text-right
103
+ =submit_tag :Save, class: "btn btn-primary"
@@ -0,0 +1,45 @@
1
+ / TODO
2
+ / Replace ApplicationName
3
+ !!!
4
+ %html
5
+ %head
6
+ %title ApplicationName Connector for Connec!™
7
+ = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
8
+ = javascript_include_tag 'application', 'data-turbolinks-track' => true
9
+ = csrf_meta_tags
10
+ %body
11
+ .navbar.navbar-fixed-top.navbar-inverse
12
+ .container-fluid
13
+ .collapse.navbar-collapse#maestrano-navbar-collapse
14
+ %ul.nav.navbar-nav
15
+ =image_tag "logos/default.png", class: 'external-logo'
16
+
17
+ %ul.nav.navbar-nav
18
+ %li=link_to "Status", root_path
19
+ %li=link_to "History", synchronizations_index_path
20
+ -if is_admin
21
+ %li=link_to "Mapping", shared_entities_index_path
22
+
23
+ -if current_user
24
+ %ul.nav.navbar-nav.navbar-right
25
+ %li.dropdown
26
+ %a{:href=>"#", :class=>"dropdown-toggle", :'data-toggle'=>"dropdown", :role=>"button"}
27
+ Hi #{current_user.first_name} #{current_user.last_name}
28
+
29
+ .padding-navbar
30
+
31
+ .wrapper{style: 'margin-bottom: 15px; '}
32
+ - if flash.any?
33
+ - flash.each do |flash_key, message|
34
+ .alert{:class => "alert-#{flash_key}"}
35
+ %strong= flash.discard(flash_key)
36
+
37
+ = yield
38
+
39
+ .footer
40
+ .container
41
+ .row
42
+ .col-xs-12
43
+ .copyright
44
+ © Copyright #{Time.now.year} Maestrano Pty Ltd (ABN: 80 152 564 424).
45
+ All rights reserved. Various trademarks held by their respective owners.