From 9d16b4b4795b8946321f8057b29e8c6922e197fd Mon Sep 17 00:00:00 2001 From: Andy Scott Date: Wed, 22 Jun 2016 17:43:30 +0100 Subject: [PATCH] Removal of import rake task --- lib/intercom-rails.rb | 1 - lib/intercom-rails/config.rb | 1 - lib/intercom-rails/exceptions.rb | 2 - lib/intercom-rails/import.rb | 180 ------------------ lib/intercom-rails/intercom.rake | 12 -- lib/intercom-rails/version.rb | 2 +- .../intercom/config/config_generator.rb | 2 - .../intercom/config/intercom.rb.erb | 10 - spec/import_network_spec.rb | 118 ------------ spec/import_spec.rb | 127 ------------ spec/import_spec_helper.rb | 100 ---------- 11 files changed, 1 insertion(+), 554 deletions(-) delete mode 100644 lib/intercom-rails/import.rb delete mode 100644 lib/intercom-rails/intercom.rake delete mode 100644 spec/import_network_spec.rb delete mode 100644 spec/import_spec.rb delete mode 100644 spec/import_spec_helper.rb diff --git a/lib/intercom-rails.rb b/lib/intercom-rails.rb index d7bc90c..3720de3 100644 --- a/lib/intercom-rails.rb +++ b/lib/intercom-rails.rb @@ -9,7 +9,6 @@ require 'intercom-rails/shutdown_helper' require 'intercom-rails/auto_include_filter' require 'intercom-rails/config' -require 'intercom-rails/import' require 'intercom-rails/railtie' if defined? Rails::Railtie module IntercomRails diff --git a/lib/intercom-rails/config.rb b/lib/intercom-rails/config.rb index 64067dc..751d949 100644 --- a/lib/intercom-rails/config.rb +++ b/lib/intercom-rails/config.rb @@ -94,7 +94,6 @@ def self.reset! config_accessor :app_id config_accessor :session_duration config_accessor :api_secret - config_accessor :api_key config_accessor :library_url config_accessor :enabled_environments, &ARRAY_VALIDATOR config_accessor :include_for_logged_out_users diff --git a/lib/intercom-rails/exceptions.rb b/lib/intercom-rails/exceptions.rb index fbeda85..a5ebc87 100644 --- a/lib/intercom-rails/exceptions.rb +++ b/lib/intercom-rails/exceptions.rb @@ -1,8 +1,6 @@ module IntercomRails class Error < StandardError; end - class ImportError < Error; end - class IntercomAPIError < Error; end class NoUserFoundError < Error; end class NoCompanyFoundError < Error; end diff --git a/lib/intercom-rails/import.rb b/lib/intercom-rails/import.rb deleted file mode 100644 index 906811c..0000000 --- a/lib/intercom-rails/import.rb +++ /dev/null @@ -1,180 +0,0 @@ -require 'net/http' -require 'json' -require 'uri' - -module IntercomRails - - class Import - - def self.bulk_create_api_endpoint - host = (ENV['INTERCOM_RAILS_DEV'] ? "http://api.intercom.dev" : "https://api.intercom.io") - URI.parse(host + "/v1/users/bulk_create") - end - - def self.run(*args) - new(*args).run - end - - attr_reader :uri, :http, :max_batch_size - attr_accessor :failed, :total_sent - - def initialize(options = {}) - @uri = Import.bulk_create_api_endpoint - @http = Net::HTTP.new(@uri.host, @uri.port) - @failed = [] - @total_sent = 0 - @max_batch_size = [(options[:max_batch_size] || 100), 100].min - - @status_enabled = !!options[:status_enabled] - - if uri.scheme == 'https' - http.use_ssl = true - http.ca_file = File.join(File.dirname(__FILE__), '../data/cacert.pem') - http.verify_mode = OpenSSL::SSL::VERIFY_PEER - end - end - - # Check for ActiveRecord OR Mongoid - def active_record?(user_klass) - (defined?(ActiveRecord::Base) && (user_klass < ActiveRecord::Base)) - end - - def mongoid?(user_klass) - (defined?(Mongoid::Document) && (user_klass < Mongoid::Document)) - end - - def supported_orm?(user_klass) - active_record?(user_klass) || mongoid?(user_klass) - end - - def assert_runnable - raise ImportError, "You can only import your users from your production environment" unless (Rails.env.production? || Rails.env.staging?) - raise ImportError, "We couldn't find your user class, please set one in config/initializers/intercom_rails.rb" unless user_klass.present? - info "Found user class: #{user_klass}" - raise ImportError, "Only ActiveRecord and Mongoid models are supported" unless supported_orm?(user_klass) - raise ImportError, "Please add an Intercom API Key to config/initializers/intercom.rb" unless IntercomRails.config.api_key.present? - info "Intercom API key found" - end - - def run - assert_runnable - - info "Sending users in batches of #{max_batch_size}:" - batches do |batch, number_in_batch| - failures = send_users(batch)['failed'] - self.failed += failures - self.total_sent += number_in_batch - - progress '.' * (number_in_batch - failures.count) - progress 'F' * failures.count - end - info "Successfully created #{self.total_sent - self.failed.count} users", :new_line => true - info "Failed to create #{self.failed.count} #{(self.failed.count == 1) ? 'user' : 'users'}, this is likely due to bad data" unless failed.count.zero? - - self - end - - def total_failed - self.failed.count - end - - private - - def batches - if active_record?(user_klass) - user_klass.find_in_batches(:batch_size => max_batch_size) do |users| - users_for_wire = map_to_users_for_wire(users) - - yield(prepare_batch(users_for_wire), users_for_wire.count) unless users_for_wire.count.zero? - end - elsif mongoid?(user_klass) - 0.step(user_klass.all.count, max_batch_size) do |offset| - users_for_wire = map_to_users_for_wire(user_klass.limit(max_batch_size).skip(offset)) - yield(prepare_batch(users_for_wire), users_for_wire.count) unless users_for_wire.count.zero? - end - end - end - - def map_to_users_for_wire(users) - users.map do |u| - user_proxy = Proxy::User.new(u) - next unless user_proxy.valid? - - for_wire = user_proxy.to_hash - companies = Proxy::Company.companies_for_user(user_proxy) - for_wire.merge!(:companies => companies.map(&:to_hash)) unless companies.nil? - - for_wire - end.compact - end - - def prepare_batch(batch) - {:users => batch}.to_json - end - - def user_klass - if IntercomRails.config.user.model.present? - IntercomRails.config.user.model.call - else - user_klass_name if user_klass_name.class != Module - end - rescue NameError - # Rails lazy loads constants, so this is how we check - nil - end - - def user_klass_name - User - end - - def send_users(users) - request = Net::HTTP::Post.new(uri.request_uri) - request.basic_auth(IntercomRails.config.app_id, IntercomRails.config.api_key) - request["Content-Type"] = "application/json" - request.body = users - - response = perform_request(request) - JSON.parse(response.body) - end - - MAX_REQUEST_ATTEMPTS = 3 - def perform_request(request, attempts = 0, error = {}) - if (attempts > 0) && (attempts < MAX_REQUEST_ATTEMPTS) - sleep(0.5) - elsif error.present? - raise error[:exception] if error[:exception] - raise exception_for_failed_response(error[:failed_response]) - end - - response = http.request(request) - - return response if successful_response?(response) - perform_request(request, attempts + 1, :failed_response => response) - rescue Timeout::Error, Errno::ECONNREFUSED, EOFError => e - perform_request(request, attempts + 1, :exception => e) - end - - def successful_response?(response) - raise ImportError, "App ID or API Key are incorrect, please check them in config/initializers/intercom.rb" if response.code == '403' - ['200', '201'].include?(response.code) - end - - def exception_for_failed_response(response) - code = response.code - IntercomAPIError.new("The Intercom API request failed with the code: #{code}, after #{MAX_REQUEST_ATTEMPTS} attempts.") - end - - def status_enabled? - @status_enabled - end - - def progress(str) - print(str) if status_enabled? - end - - def info(str, options = {}) - puts "#{"\n" if options[:new_line]}* #{str}" if status_enabled? - end - - end -end diff --git a/lib/intercom-rails/intercom.rake b/lib/intercom-rails/intercom.rake deleted file mode 100644 index d44c438..0000000 --- a/lib/intercom-rails/intercom.rake +++ /dev/null @@ -1,12 +0,0 @@ -namespace :intercom do - - desc "Import your users into intercom" - task :import => :environment do - begin - IntercomRails::Import.run(:status_enabled => true) - rescue IntercomRails::ImportError => e - puts e.message - end - end - -end diff --git a/lib/intercom-rails/version.rb b/lib/intercom-rails/version.rb index 4e64f21..ad249fc 100644 --- a/lib/intercom-rails/version.rb +++ b/lib/intercom-rails/version.rb @@ -1,3 +1,3 @@ module IntercomRails - VERSION = "0.2.35" + VERSION = "0.2.36" end diff --git a/lib/rails/generators/intercom/config/config_generator.rb b/lib/rails/generators/intercom/config/config_generator.rb index ded5fd2..098997f 100644 --- a/lib/rails/generators/intercom/config/config_generator.rb +++ b/lib/rails/generators/intercom/config/config_generator.rb @@ -8,14 +8,12 @@ def self.source_root argument :app_id, :desc => "Your Intercom app-id, which can be found here: https://app.intercom.io/apps/api_keys" argument :api_secret, :desc => "Your Intercom api-secret, used for secure mode", :optional => true - argument :api_key, :desc => "An Intercom API key, for various rake tasks", :optional => true argument :session_duration, :desc => "user session duration, this should match your app", :optional => true FALSEY_RESPONSES = ['n', 'no'] def create_config_file @app_id = app_id @api_secret = api_secret - @api_key = api_key @session_duration = session_duration @include_for_logged_out_users = false diff --git a/lib/rails/generators/intercom/config/intercom.rb.erb b/lib/rails/generators/intercom/config/intercom.rb.erb index c482c65..1869ca2 100644 --- a/lib/rails/generators/intercom/config/intercom.rb.erb +++ b/lib/rails/generators/intercom/config/intercom.rb.erb @@ -20,16 +20,6 @@ IntercomRails.config do |config| # config.api_secret = "..." <%- end -%> - # == Intercom API Key - # This is required for some Intercom rake tasks like importing your users; - # you can generate one at https://app.intercom.io/apps/api_keys. - # - <%- if @api_key -%> - config.api_key = "<%= @api_key %>" - <%- else -%> - # config.api_key = "..." - <%- end -%> - # == Enabled Environments # Which environments is auto inclusion of the Javascript enabled for # diff --git a/spec/import_network_spec.rb b/spec/import_network_spec.rb deleted file mode 100644 index 71d83c7..0000000 --- a/spec/import_network_spec.rb +++ /dev/null @@ -1,118 +0,0 @@ -require 'import_spec_helper' -require 'sinatra/base' - -class MockIntercom < Sinatra::Base - set :server, 'thin' - - before do - content_type 'application/json' - end - - get '/health_check' do - content_type 'plain/text' - 'hello world' - end - - post '/all_successful' do - {:failed => []}.to_json - end - - post '/one_failure' do - {:failed => ['ben@intercom.io']}.to_json - end - - post '/bad_auth' do - status 403 - {"error" => {"type" => "not_authenticated", "message" => "HTTP Basic: Access denied."}}.to_json - end - - post '/500_error' do - status 500 - {"error" => {"type" => "server_error", "message" => "Danger deploy, gone wrong?"}}.to_json - end - -end - -class MockIntercomRunner - - def self.start_mock_intercom - suppressing_stderr do - - @pid = fork do - MockIntercom.run!(:port => 46837) do |server| - server.silent = true - end - end - - response = nil - uri = URI.parse("http://localhost:46837/health_check") - - begin - response = Net::HTTP.get_response(uri).body until (response == 'hello world') - rescue Errno::ECONNREFUSED - sleep(0.5) - retry - end - end - end - - def self.stop_mock_intercom - suppressing_stderr do - Process.kill('INT', @pid) - Process.wait(@pid) rescue SystemError - end - end -end - -describe IntercomRails::Import do - context 'with mock intercom server' do - before(:all) do - MockIntercomRunner.start_mock_intercom - end - after(:all) do - MockIntercomRunner.stop_mock_intercom - end - - let(:import) { IntercomRails::Import.new } - - def set_api_path(path) - allow(IntercomRails::Import).to receive(:bulk_create_api_endpoint).and_return(URI.parse("http://localhost:46837/#{path}")) - end - - it 'succeeds with no failures' do - set_api_path "all_successful" - import.run - expect(import.failed).to eq([]) - expect(import.total_sent).to eq(2) - end - - it 'excludes users if necessary' do - set_api_path "all_successful" - IntercomRails.config.user.exclude_if = Proc.new {|user| user.email.start_with?('ben')} - import.run - expect(import.failed).to eq([]) - expect(import.total_sent).to eq(1) - end - - it 'handles one failure' do - set_api_path "one_failure" - import.run - expect(import.failed).to eq(['ben@intercom.io']) - expect(import.total_sent).to eq(2) - end - - it 'handles bad_auth' do - set_api_path "bad_auth" - expect { import.run }.to raise_error(IntercomRails::ImportError) do |error| - expect(error.message).to eq("App ID or API Key are incorrect, please check them in config/initializers/intercom.rb") - end - end - - it 'handles server errors' do - set_api_path "500_error" - expect { import.run }.to raise_error(IntercomRails::IntercomAPIError) do |error| - expect(error.message).to eq("The Intercom API request failed with the code: 500, after 3 attempts.") - end - end - end -end diff --git a/spec/import_spec.rb b/spec/import_spec.rb deleted file mode 100644 index a52b639..0000000 --- a/spec/import_spec.rb +++ /dev/null @@ -1,127 +0,0 @@ -require 'import_spec_helper' - -describe IntercomRails::Import do - context 'misconfiguration' do - it 'raises error if not production environment' do - allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new("development")) - - expect { IntercomRails::Import.run }.to raise_error(IntercomRails::ImportError) do |error| - expect(error.message).to eq("You can only import your users from your production environment") - end - end - - it 'raises error if no user class found' do - allow_any_instance_of(IntercomRails::Import).to receive(:user_klass).and_return(nil) - - expect { IntercomRails::Import.run }.to raise_error(IntercomRails::ImportError) do |error| - expect(error.message).to eq("We couldn't find your user class, please set one in config/initializers/intercom_rails.rb") - end - end - - it 'raises error if unsupported user class' do - allow_any_instance_of(IntercomRails::Import).to receive(:user_klass).and_return(Class) - - expect { IntercomRails::Import.run }.to raise_error(IntercomRails::ImportError) do |error| - expect(error.message).to eq("Only ActiveRecord and Mongoid models are supported") - end - end - - it 'raises error if no api_key set' do - IntercomRails.config.api_key = nil - expect { IntercomRails::Import.run }.to raise_error(IntercomRails::ImportError) do |error| - expect(error.message).to eq("Please add an Intercom API Key to config/initializers/intercom.rb") - end - end - - it 'does not try to use a module as a user class' do - allow_any_instance_of(IntercomRails::Import).to receive(:user_klass_name).and_return(NotARealUser) - expect(IntercomRails::Import.new.send(:user_klass)).to eq nil - end - end - - context 'mongoid' do - it "imports" do - IntercomRails.config.user.model = proc { ExampleMongoidUserModel } - import = IntercomRails::Import.new - expect(import).to receive(:map_to_users_for_wire).with(ExampleMongoidUserModel.all).and_call_original - expect(import).to receive(:send_users).and_return('failed' => []) - import.run - end - end - - context 'status output' do - it 'prints details of what it is doing' do - import = IntercomRails::Import.new(:status_enabled => true) - expect(import).to receive(:send_users).and_return('failed' => [1]) - expect(import).to receive(:batches).and_yield(nil, 3) - - expect(capturing_stdout { import.run }).to eq(<<-output -* Found user class: User -* Intercom API key found -* Sending users in batches of 100: -..F -* Successfully created 2 users -* Failed to create 1 user, this is likely due to bad data - output - ) - end - end - - context 'batch size' do - it 'has a default' do - expect(IntercomRails::Import.new.max_batch_size).to eq(100) - end - it 'is settable' do - expect(IntercomRails::Import.new(:max_batch_size => 50).max_batch_size).to eq(50) - end - it 'has a hard limit' do - expect(IntercomRails::Import.new(:max_batch_size => 101).max_batch_size).to eq(100) - end - end - - context 'companies' do - it 'prepares companies for import' do - import = IntercomRails::Import.new - u = dummy_user - u.instance_eval do - def apps - [dummy_company] - end - end - - allow(User).to receive(:find_in_batches).and_yield([u]) - - IntercomRails.config.user.company_association = Proc.new { |user| user.apps } - - prepare_for_batch_users = nil - allow(import).to receive(:prepare_batch) {|users| prepare_for_batch_users = users} - allow(import).to receive(:send_users).and_return('failed' => []) - - import.run - - expect(prepare_for_batch_users[0][:companies].length).to eq(1) - end - - it 'imports empty companies Array' do - import = IntercomRails::Import.new - u = dummy_user - u.instance_eval do - def apps - [] - end - end - - allow(User).to receive(:find_in_batches).and_yield([u]) - - IntercomRails.config.user.company_association = Proc.new { |user| user.apps } - - prepare_for_batch_users = nil - allow(import).to receive(:prepare_batch) {|users| prepare_for_batch_users = users} - allow(import).to receive(:send_users).and_return('failed' => []) - - import.run - - expect(prepare_for_batch_users[0][:companies].length).to eq(0) - end - end -end diff --git a/spec/import_spec_helper.rb b/spec/import_spec_helper.rb deleted file mode 100644 index 9931c41..0000000 --- a/spec/import_spec_helper.rb +++ /dev/null @@ -1,100 +0,0 @@ -require 'spec_helper' -require 'active_support/string_inquirer' - -module Rails - def self.env - ActiveSupport::StringInquirer.new("production") - end -end - -module ActiveRecord - class Base; end -end - -module NotARealUser -end - -module Mongoid - module Document - def self.included(klass) - klass.extend ClassMethods - end - - module ClassMethods - def all - @_users ||= User.all - end - - def limit(*args) - self - end - - def skip(*args) - all - end - end - end -end - -class ExampleMongoidUserModel - include Mongoid::Document -end - -class User - - attr_reader :id, :email, :name - - def initialize(options = {}) - options.each do |k,v| - instance_variable_set(:"@#{k}", v) - end - end - - MOCK_USERS = [ - {:id => 1, :email => "ben@intercom.io", :name => "Ben McRedmond"}, - {:id => 2, :email => "ciaran@intercom.io", :name => "Ciaran Lee"} - ] - - def self.find_in_batches(*args) - yield(MOCK_USERS.map {|u| new(u)}) - end - - def self.all - MOCK_USERS.map { |u| new(u) } - end - - def self.first - new(MOCK_USERS.first) - end - - def self.<(other) - other == ActiveRecord::Base - end -end - -RSpec.configure do |config| - config.before(:each) do - IntercomRails.config.api_key = "abcd" - end -end - -def capturing_stdout - $stdout.flush - old = $stdout.dup - $stdout = @output = StringIO.new - - yield - - $stdout.flush - @output.string -ensure - $stdout = old -end - -def suppressing_stderr - old = $stderr.dup - $stderr = StringIO.new - yield -ensure - $stderr = old -end