diff --git a/app/jobs/spree_mailchimp_ecommerce/update_user_job.rb b/app/jobs/spree_mailchimp_ecommerce/update_user_job.rb index 167c9c4..dd57661 100644 --- a/app/jobs/spree_mailchimp_ecommerce/update_user_job.rb +++ b/app/jobs/spree_mailchimp_ecommerce/update_user_job.rb @@ -6,6 +6,12 @@ def perform(mailchimp_user) return unless mailchimp_user gibbon_store.customers(mailchimp_user["id"]).update(body: mailchimp_user) + rescue Gibbon::MailChimpError => e + if e.status_code == 404 + gibbon_store.customers.create(body: mailchimp_user) + else + Rails.logger.error("[MAILCHIMP] Error while creating user: #{e}") + end end end end diff --git a/app/models/spree_mailchimp_ecommerce/spree/address_decorator.rb b/app/models/spree_mailchimp_ecommerce/spree/address_decorator.rb new file mode 100644 index 0000000..2b4cfde --- /dev/null +++ b/app/models/spree_mailchimp_ecommerce/spree/address_decorator.rb @@ -0,0 +1,22 @@ +module SpreeMailchimpEcommerce + module Spree + module AddressDecorator + MAILCHIMP_ATTRIBUTES = ['firstname', 'lastname', 'address1', 'address2', 'city', 'state_id', 'zipcode', 'country_id'].freeze + + def self.prepended(base) + base.after_update :update_mailchimp_user + end + + private + + def update_mailchimp_user + return if user&.bill_address_id != id + return if (previous_changes.keys & MAILCHIMP_ATTRIBUTES).empty? + + SpreeMailchimpEcommerce::UpdateUserJob.perform_later(user.mailchimp_user) + end + end + end +end + +Spree::Address.prepend(SpreeMailchimpEcommerce::Spree::AddressDecorator) diff --git a/app/models/spree_mailchimp_ecommerce/spree/user_decorator.rb b/app/models/spree_mailchimp_ecommerce/spree/user_decorator.rb index 739b205..d893ba5 100644 --- a/app/models/spree_mailchimp_ecommerce/spree/user_decorator.rb +++ b/app/models/spree_mailchimp_ecommerce/spree/user_decorator.rb @@ -1,6 +1,8 @@ module SpreeMailchimpEcommerce module Spree module UserDecorator + MAILCHIMP_ATTRIBUTES = ['firstname', 'lastname', 'email', 'bill_address_id'].freeze + def self.prepended(base) base.after_create :create_mailchimp_user base.after_update :update_mailchimp_user @@ -21,8 +23,7 @@ def create_mailchimp_user end def update_mailchimp_user - ignored_keys = %w[sign_in_count current_sign_in_at last_sign_in_at current_sign_in_ip updated_at] - return true if (previous_changes.keys - ignored_keys).empty? + return if (previous_changes.keys & MAILCHIMP_ATTRIBUTES).empty? ::SpreeMailchimpEcommerce::UpdateUserJob.perform_later(mailchimp_user) end diff --git a/app/presenters/spree_mailchimp_ecommerce/user_mailchimp_presenter.rb b/app/presenters/spree_mailchimp_ecommerce/user_mailchimp_presenter.rb index 9482fd3..823cf7d 100644 --- a/app/presenters/spree_mailchimp_ecommerce/user_mailchimp_presenter.rb +++ b/app/presenters/spree_mailchimp_ecommerce/user_mailchimp_presenter.rb @@ -11,7 +11,7 @@ def initialize(user) def json { id: Digest::MD5.hexdigest(user.email.downcase), - email_address: user.email || "", + email_address: user.email || '', opt_in_status: false, first_name: firstname, last_name: lastname, @@ -21,11 +21,11 @@ def json private def firstname - user.try(:firstname) || user&.bill_address&.firstname || "unknown firstname" + user.try(:firstname) || user.try(:first_name) || user&.bill_address&.firstname || 'unknown firstname' end def lastname - user.try(:lastname) || user&.bill_address&.lastname || "unknown lastname" + user.try(:lastname) || user.try(:last_name) || user&.bill_address&.lastname || 'unknown lastname' end def address diff --git a/spec/models/address_spec.rb b/spec/models/address_spec.rb new file mode 100644 index 0000000..73a2127 --- /dev/null +++ b/spec/models/address_spec.rb @@ -0,0 +1,66 @@ +require "spec_helper" + +describe Spree::Address, type: :model do + describe 'after update' do + context 'when address is user bill address' do + shared_examples 'SpreeMailchimpEcommerce::UpdateUserJob enqueued' do + it 'enqueues SpreeMailchimpEcommerce::UpdateUserJob' do + expect(::SpreeMailchimpEcommerce::UpdateUserJob).to have_been_enqueued.with(user.reload.mailchimp_user) + end + end + + let!(:address) { create(:address) } + let!(:user) { create(:user, bill_address: address) } + + before { address.update(user: user) } + + context 'when some of MAILCHIMP_ATTRIBUTES is updated' do + context 'when firstname attribute is updated' do + before { address.update(firstname: FFaker::Name.first_name) } + + it_behaves_like 'SpreeMailchimpEcommerce::UpdateUserJob enqueued' + end + + context 'when lastname attribute is updated' do + before { address.update(lastname: FFaker::Name.last_name) } + + it_behaves_like 'SpreeMailchimpEcommerce::UpdateUserJob enqueued' + end + end + + context 'when some other attribute is updated' do + it 'does not enqueue SpreeMailchimpEcommerce::UpdateUserJob' do + expect { address.update(phone: FFaker::PhoneNumber.short_phone_number) }.not_to change(ActiveJob::Base.queue_adapter.enqueued_jobs, :size) + end + end + end + + context 'when address is not user bill address' do + shared_examples 'SpreeMailchimpEcommerce::UpdateUserJob not enqueued' do + it 'does not enqueue SpreeMailchimpEcommerce::UpdateUserJob' do + expect(::SpreeMailchimpEcommerce::UpdateUserJob).not_to have_been_enqueued + end + end + + let!(:address) { create(:address, user: nil) } + + context 'when firstname attribute is updated' do + before { address.update(firstname: FFaker::Name.first_name) } + + it_behaves_like 'SpreeMailchimpEcommerce::UpdateUserJob not enqueued' + end + + context 'when lastname attribute is updated' do + before { address.update(lastname: FFaker::Name.last_name) } + + it_behaves_like 'SpreeMailchimpEcommerce::UpdateUserJob not enqueued' + end + + context 'when some other attribute is updated' do + before { address.update(city: FFaker::Address.city) } + + it_behaves_like 'SpreeMailchimpEcommerce::UpdateUserJob not enqueued' + end + end + end +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 4fa0f9d..8218f0c 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -3,18 +3,42 @@ describe Spree::User, type: :model do subject { build(:user_with_addresses) } - describe "mailchimp" do + describe 'after_create' do it "schedules mailchimp notification on user create" do subject.save! expect(SpreeMailchimpEcommerce::CreateUserJob).to have_been_enqueued.with(subject.mailchimp_user) end + end - it "schedules mailchimp notification on user update" do - subject.save! - subject.update(email: "new@mail.com") + describe 'after_update' do + before { subject.save } + + context 'when some of MAILCHIMP_ATTRIBUTES is updated' do + context 'when email is changed' do + before { subject.update(email: FFaker::Internet.email) } + + it 'enqueues SpreeMailchimpEcommerce::UpdateUserJob' do + binding.pry + expect(SpreeMailchimpEcommerce::UpdateUserJob).to have_been_enqueued.with(subject.mailchimp_user) + end + end + + context 'when bill address is changed' do + let(:new_address) { create(:address) } + + before { subject.update(bill_address: new_address) } + + it 'enqueues SpreeMailchimpEcommerce::UpdateUserJob' do + expect(SpreeMailchimpEcommerce::UpdateUserJob).to have_been_enqueued.with(subject.mailchimp_user) + end + end + end - expect(SpreeMailchimpEcommerce::UpdateUserJob).to have_been_enqueued.with(subject.mailchimp_user) + context 'when some other attribute is updated' do + it 'does not enqueue SpreeMailchimpEcommerce::UpdateUserJob' do + expect { subject.update(current_sign_in_ip: FFaker::Internet.ip_v4_address) }.not_to change(ActiveJob::Base.queue_adapter.enqueued_jobs, :size) + end end end diff --git a/spec/presenters/user_mailchimp_presenter_spec.rb b/spec/presenters/user_mailchimp_presenter_spec.rb new file mode 100644 index 0000000..cfa36bf --- /dev/null +++ b/spec/presenters/user_mailchimp_presenter_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +RSpec.describe SpreeMailchimpEcommerce::UserMailchimpPresenter, type: :presenter do + let(:address) { create(:address) } + let!(:user) { create(:user, bill_address: address) } + + describe '.json' do + subject { described_class.new(user).json } + + let(:result) do + { + id: Digest::MD5.hexdigest(user.email.downcase), + email_address: user.email, + opt_in_status: false, + first_name: address.firstname, + last_name: address.lastname, + address: { + address1: address.address1, + address2: address.address2, + city: address.city, + province: address.state&.name, + province_code: address.state&.abbr, + postal_code: address.zipcode, + country: address.country&.name, + country_code: address.country&.iso + } + }.as_json + end + + it 'returns serialized object' do + expect(subject).to eq(result) + end + end +end