Migration Guides
- Marco Bagnasco (Unlicensed)
- Bruno Chauvet (Unlicensed)
Owned by Marco Bagnasco (Unlicensed)
Step to step guide when migrating to a different version of the Maestrano Connector Framework
Migrating to version 2.1
Step-by-step guide
- In addition to the framework version, add the following gems to your
Gemfile
:webmock
group :test do gem 'webmock' end
- Download these 2 png files (used in the front-end) and add them to app/assets/images/logos
- to_external.png
- to_connec.png Replace your
app/assets/javascripts/home.js
:home.js Expand sourcefunction historicalDataDisplay() { if (document.getElementById('historical-data').checked) { $('#myModal').modal('show'); document.getElementById('historical-data-display-checked').style.display = 'block'; document.getElementById('historical-data-display-unchecked').style.display = 'none'; } else { document.getElementById('historical-data-display-unchecked').style.display = 'block'; document.getElementById('historical-data-display-checked').style.display = 'none'; } } var checkHistorical function closeModal(sender) { checkHistorical = sender.id == 'confirm' $('#myModal').modal('hide'); } $(function () { $('[data-toggle="tooltip"]').tooltip() }) $(document).ready(function(){ $("#myModal").on('hidden.bs.modal', function (e) { if (!checkHistorical) { document.getElementById('historical-data').checked = false historicalDataDisplay() } checkHistorical = false }); });
Append these lines to
app/assets/stylesheets/layout.sass
layout.sass Expand source.image height: 30 margin-left: -25px .small-image height: 20px
Your
home_controller.rb
should look like this:home_controller.rb Expand source# frozen_string_literal: true class HomeController < ApplicationController def update return redirect_to(:back) unless is_admin # Update list of entities to synchronize current_organization.synchronized_entities.keys.each do |entity| current_organization.synchronized_entities[entity][:can_push_to_connec] = params[entity.to_s]["to_connec"] == "1" current_organization.synchronized_entities[entity][:can_push_to_external] = params[entity.to_s]["to_external"] == "1" end full_sync = params['historical-data'].present? && !current_organization.historical_data opts = {full_sync: full_sync} current_organization.sync_enabled = current_organization.synchronized_entities.values.any? { |settings| settings.values.any? { |v| v } } current_organization.enable_historical_data(params['historical-data'].present?) trigger_sync = current_organization.sync_enabled current_organization.save # Trigger sync only if the sync has been enabled start_synchronization(opts) if trigger_sync redirect_to(:back) end def synchronize return redirect_to(:back) unless is_admin Maestrano::Connector::Rails::SynchronizationJob.perform_later(current_organization.id, (params['opts'] || {}).merge(forced: true)) flash[:info] = 'Synchronization requested' redirect_to(:back) end def redirect_to_external redirect_to 'https://your_application.com' end private def start_synchronization(opts) Maestrano::Connector::Rails::SynchronizationJob.perform_later(current_organization.id, opts) flash[:info] = 'Congrats, you\'re all set up! Your data are now being synced' if current_organization.sync_enabled_changed? end end
Refer to this
html.haml
file for the modal and layout:Front End Customization
Based on the specific requirements of the connector, this file might have to be changed. (i.e. Authentication form)
index.html.haml Expand source!!! .home .banners .row .col-md-10.col-md-offset-2 %h2 YourApp Connector %p -if current_organization Link your company <strong>#{current_organization.name} (#{current_organization.uid})</strong> to YourApp to get your business in synch. Check the status of your connection on this screen. -else Link your account to YourApp to get your business in synch. Check the status of your connection on this screen. .container - if current_user - unless is_admin .row .col-md-12.alert.alert-warning Only administrators can modify the application settings .row.link-step{class: "#{current_organization.oauth_uid ? 'done' : 'todo'}"} .col-md-1.text-center.link-step-number %span.badge.link-step-badge 1 .col-md-6.link-step-description %h - if current_organization.oauth_uid Your YourApp account <strong>#{current_organization.oauth_name} (#{current_organization.oauth_uid})</strong> is currently linked - else Your YourApp account is not linked %br .col-md-4.col-md-offset-8.text-center.link-step-action - if current_organization.oauth_uid = link_to "Disconnect", signout_omniauth_path(organization_id: current_organization.id), class: "btn btn-warning btn-lg #{is_admin ? '' : 'disabled'}" - else - if is_admin .col-md-12.col-md-offset-1.text-center %small Your YourApp email and password are not stored by Maestrano = render 'authentication_form' %br %small If you don’t have an account #{link_to 'create yours here', Maestrano::Connector::Rails::External.create_account_link(current_organization || nil)} .spacer1 .row.link-step{class: "#{(current_organization.sync_enabled && current_organization.synchronized_entities.values.any?) ? 'done' : 'todo'}"} = form_tag home_update_path(id: current_organization.id), method: :put do .col-md-1.text-center.link-step-number %span.badge.link-step-badge 2 .col-md-9.link-step-description %p You can customize which entities are synchronized by the connector: %p (#{image_tag "logos/to_connec.png", class: "small-image"} : from YourApp to Connec! and #{image_tag "logos/to_external.png", class: "small-image"} : from Connec! to YourApp) .spacer1 .row .col-md-11.col-md-offset-1.center .row .col-md-1 =image_tag "logos/to_connec.png", class: "image" .col-md-1 =image_tag "logos/to_external.png", class: "image" .col-md-4 YourApp wording .col-md-3 Universal wording .spacer1 .row .col-md-11.col-md-offset-1 - current_organization.displayable_synchronized_entities.each do |k, v| .row.sync-entity .col-md-1.link-step-action #{check_box("#{k}", "to_connec", {checked: (v[:can_push_to_connec] || v[:can_push_to_external]) && !current_organization.pull_disabled, onclick: "return !#{k}_to_external.checked;", disabled: current_organization.pull_disabled})} .col-md-1.link-step-action #{check_box("#{k}", "to_external", {checked: v[:can_push_to_external] && !current_organization.push_disabled, onchange: "#{k}_to_connec.checked = #{!current_organization.pull_disabled};", disabled: current_organization.push_disabled})} %label.col-md-8{:for => "#{k}", style: 'padding-top: 5px;'} .col-md-6 #{v[:external_name]} .col-md-6 #{v[:connec_name]} -if is_admin .col-md-2.text-right - if v && current_organization.oauth_uid && current_organization.sync_enabled = link_to 'Force a synchronization', home_synchronize_path(opts: {only_entities: [k.to_s]}), method: :post, class: 'btn btn-warning btn-sm', title: "Force a synchronization for #{v[:external_name]} only", 'data-toggle' => 'tooltip', 'data-placement' => 'right' .spacer2 .row %h Chose whether to synchronize your historical data: .spacer1 .row .col-md-4.col-md-offset-1 %label{:for => 'historical-data'} Synchronize my historical data .col-md-1 #myModal.modal.fade{:role => "dialog"} .modal-dialog .modal-content .modal-header %button.close{"data-dismiss" => "modal", :type => "button"} × %h4.modal-title Warning! .modal-body %p %b All data created prior to the date you linked YourApp %b will be synchronised both ways. %p It means that: %br \- all data from applications you already have linked to the platform will be sent to your YourApp account %br \- all exisiting data from YourApp will be sent to your other applications %br %p If you have been manually copying records in multiple applications, %b you risk seeing duplicates arising! %p %b This action cannot be undone at any time! .modal-footer %button.btn.btn-primary{id: 'confirm', :type => "button", onclick: "closeModal(confirm);"} Confirm %button.btn.btn-secondary{id: 'close', :type => "button", onclick: "closeModal(close);"} Close %input{type: 'checkbox', id: 'historical-data', name: 'historical-data', checked: current_organization.historical_data, onchange: 'historicalDataDisplay();', disabled: current_organization.historical_data} .col-md-6 %small#historical-data-display-unchecked{style: "display: #{current_organization.historical_data ? 'none' : 'block'}"} Only data created after #{(current_organization.date_filtering_limit && current_organization.date_filtering_limit.utc || Time.now.utc).to_formatted_s(:long_ordinal)} will be synchronized %small#historical-data-display-checked{style: "display: #{!current_organization.historical_data ? 'none' : 'block'}"} Synchronizing your historical data will share all data in YourApp. This action is not reversible. Want to know more? Check #{link_to 'here', 'https://maestrano.atlassian.net/wiki/display/UKB/How+Connec%21+manages+Historical+Data+Sharing'} .spacer1 .row .col-md-2.col-md-offset-10.text-center.link-step-action =submit_tag "#{current_organization.sync_enabled ? 'Synchronize' : 'Start synchronizing!'}", class: "btn btn-lg btn-warning #{current_organization.oauth_uid ? '' : 'disabled'} text-sm" -if current_organization.oauth_uid && current_organization.sync_enabled .spacer2 .row .col-md-4.col-md-offset-4.text-center = link_to 'Go to YourApp', home_redirect_to_external_path, class: 'btn btn-lg btn-primary' - else .row .col-md-4.col-md-offset-4.center = 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'
Add these 2 migrations:
UpdateOrganizationMetadata Expand sourceclass UpdateOrganizationMetadata < ActiveRecord::Migration def change add_column :organizations, :push_disabled, :boolean add_column :organizations, :pull_disabled, :boolean change_column :organizations, :synchronized_entities, :text # Migration to update the way we handle synchronized_entities for data sharing. # Before : synchronized_entities = {company: true} # After: synchronized_entities = {company: {can_push_to_connec: true, can_push_to_external: true}} #We also add metadata from MnoHub Maestrano::Connector::Rails::Organization.all.each do |o| o.reset_synchronized_entities o.enable_historical_data(true) if o.push_disabled end end end
AddMetadataToIdMap Expand sourceclass AddMetadataToIdMap < ActiveRecord::Migration def change add_column :id_maps, :metadata, :text end end
Add these to
spec_helper.rb
:spec_helper Expand sourcerequire 'webmock/rspec' ... config.before(:each) do stub_request(:get, %r(https://maestrano.com/api/v1/account/groups/\w+)) .with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization'=>'Basic Og==', 'User-Agent'=>'Ruby'}) .to_return({status: 200, body: "{}", headers: {}}) end
Refer to this file to fix
home_controller_spec
:home_controller_spec Expand sourcerequire 'spec_helper' describe HomeController, type: :controller do let(:back_path) { home_index_path } before(:each) do request.env["HTTP_REFERER"] = back_path end describe 'index' do subject { get :index } it { expect(subject).to be_success } end describe 'update' do let(:user) { create(:user) } 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}}) } before { allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_user).and_return(user) } before { allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_organization).and_return(organization) } subject { put :update, id: organization.id, 'people' => {to_connec: '1', to_external: '1'}, 'item' => {}, 'lala' => {} } context 'when user is not admin' do before { allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:is_admin).and_return(false) } it { expect(subject).to redirect_to back_path } end context 'when user is admin' do before { allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:is_admin).and_return(true) } it { expect(subject).to redirect_to back_path } it 'updates organization synchronized_entities' do subject organization.reload 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}) end it 'updates organization sync_enabled' do subject organization.reload expect(organization.sync_enabled).to eq true end context 'when removing all entities' do subject { put :update, id: organization.id, 'people' => {}, 'item' => {} } before { organization.update(sync_enabled: true) } it 'set sync_enabled to false' do subject organization.reload expect(organization.sync_enabled).to eq false end end end end describe 'synchronize' do let(:user) { create(:user) } 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}}) } before { allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_user).and_return(user) } before { allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:current_organization).and_return(organization) } subject { post :synchronize } context 'when user is not admin' do before { allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:is_admin).and_return(false) } it { expect(subject).to redirect_to back_path } it 'does nothing' do expect(Maestrano::Connector::Rails::SynchronizationJob).to_not receive(:perform_later) subject end end context 'when user is admin' do before { allow_any_instance_of(Maestrano::Connector::Rails::SessionHelper).to receive(:is_admin).and_return(true) } it { expect(subject).to redirect_to back_path } context 'with opts' do subject { post :synchronize, opts: {'opts' => 'some_opts'} } it 'calls perform_later with opts' do expect(Maestrano::Connector::Rails::SynchronizationJob).to receive(:perform_later).with(organization.id, 'opts' => 'some_opts', forced: true) subject end end context 'without opts' do subject { post :synchronize} it 'calls perform_later with empty opts hash' do expect(Maestrano::Connector::Rails::SynchronizationJob).to receive(:perform_later).with(organization.id, forced: true) subject end end end end describe 'redirect_to_external' do subject { get :redirect_to_external } context 'otherwise' do it {expect(subject).to redirect_to('https://your_application.com')} end end end
- Download these 2 png files (used in the front-end) and add them to app/assets/images/logos