Skip to content

Commit

Permalink
Merge pull request #5677 from mamhoff/promotion-finder
Browse files Browse the repository at this point in the history
Add extension point: Promotion finder
  • Loading branch information
kennyadsl authored Mar 6, 2024
2 parents cb4ec7b + 2f29de0 commit d5dd8b6
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 13 deletions.
2 changes: 1 addition & 1 deletion api/app/controllers/spree/api/promotions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def show
private

def load_promotion
@promotion = Spree::Promotion.with_coupon_code(params[:id]) || Spree::Promotion.find(params[:id])
@promotion = Spree::Config.promotions.promotion_finder_class.by_code_or_id(params[:id])
end
end
end
Expand Down
36 changes: 24 additions & 12 deletions api/spec/requests/spree/api/promotions_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,38 @@ module Spree::Api
stub_authentication!
end

let(:promotion) { create :promotion, code: '10off' }
let(:found_promotion) do
OpenStruct.new(
id: 1,
name: 'Test Promotion',
description: 'Promotion for testing purposes',
path: '/api/promotions/test-promo',
starts_at: 1.day.ago,
expires_at: 1.day.from_now,
type: "something",
usage_limit: 100,
advertise: false,
)
end

describe 'GET #show' do
subject { get spree.api_promotion_path(id) }
subject { get spree.api_promotion_path("1") }

context 'when admin' do
sign_in_as_admin!

context 'when finding by id' do
let(:id) { promotion.id }

it_behaves_like "a JSON response"
end

context 'when finding by code' do
let(:id) { promotion.codes.first.value }
context 'when finding by a promotion' do
before do
allow(Spree::Config.promotions.promotion_finder_class).to receive(:by_code_or_id).and_return(found_promotion)
end

it_behaves_like "a JSON response"
end

context 'when id does not exist' do
let(:id) { 'argh' }
before do
allow(Spree::Config.promotions.promotion_finder_class).to receive(:by_code_or_id).and_raise(ActiveRecord::RecordNotFound)
end

it 'should be 404' do
subject
Expand All @@ -55,7 +65,9 @@ module Spree::Api
end

context 'when non admin' do
let(:id) { promotion.id }
before do
allow(Spree::Config.promotions.promotion_finder_class).to receive(:by_code_or_id).and_return(found_promotion)
end

it 'should be unauthorized' do
subject
Expand Down
9 changes: 9 additions & 0 deletions core/app/models/spree/null_promotion_finder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

module Spree
module NullPromotionFinder
def self.by_code_or_id(*)
raise ActiveRecord::RecordNotFound, "No promotion system configured."
end
end
end
9 changes: 9 additions & 0 deletions core/app/models/spree/promotion_finder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

module Spree
class PromotionFinder
def self.by_code_or_id(coupon_code)
Spree::Promotion.with_coupon_code(coupon_code.to_s) || Spree::Promotion.find(coupon_code)
end
end
end
8 changes: 8 additions & 0 deletions core/lib/spree/core/null_promotion_configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ class NullPromotionConfiguration < Spree::Preferences::Configuration
# the standard coupon code handler class
# Spree::NullPromotionHandler.
class_name_attribute :coupon_code_handler_class, default: 'Spree::NullPromotionHandler'

# Allows providing a different promotion finder.
# @!attribute [rw] promotion_finder_class
# @see Spree::NullPromotionFinder
# @return [Class] an object that conforms to the API of
# the standard promotion finder class
# Spree::NullPromotionFinder.
class_name_attribute :promotion_finder_class, default: 'Spree::NullPromotionFinder'
end
end
end
3 changes: 3 additions & 0 deletions core/lib/spree/core/promotion_configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ class PromotionConfiguration < Spree::Preferences::Configuration
# promotion_adjuster_class allows extensions to provide their own Promotion Adjuster
class_name_attribute :promotion_adjuster_class, default: 'Spree::Promotion::OrderAdjustmentsRecalculator'

# promotion_finder_class allows extensions to provide their own Promotion Finder
class_name_attribute :promotion_finder_class, default: 'Spree::PromotionFinder'

# Allows providing a different shipping promotion handler.
# @!attribute [rw] shipping_promotion_handler_class
# @see Spree::PromotionHandler::Shipping
Expand Down
4 changes: 4 additions & 0 deletions core/spec/lib/spree/core/null_promotion_configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@
it "uses the null coupon code handler class by default" do
expect(config.coupon_code_handler_class).to eq Spree::NullPromotionHandler
end

it "uses the null promotion finder class by default" do
expect(config.promotion_finder_class).to eq Spree::NullPromotionFinder
end
end
4 changes: 4 additions & 0 deletions core/spec/lib/spree/core/promotion_configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
expect(config.shipping_promotion_handler_class).to eq Spree::PromotionHandler::Shipping
end

it "uses promotion finder class by default" do
expect(config.promotion_finder_class).to eq Spree::PromotionFinder
end

describe "#calculators" do
subject { config.calculators[promotion_action] }

Expand Down
11 changes: 11 additions & 0 deletions core/spec/models/spree/null_promotion_finder_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

require "rails_helper"

RSpec.describe Spree::NullPromotionFinder do
describe ".by_code_or_id" do
it "raises ActiveRecord::RecordNotFound" do
expect { described_class.by_code_or_id("promo") }.to raise_error(ActiveRecord::RecordNotFound)
end
end
end
23 changes: 23 additions & 0 deletions core/spec/models/spree/promotion_finder_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

require "rails_helper"

RSpec.describe Spree::PromotionFinder do
describe ".by_code_or_id" do
let!(:promotion) { create(:promotion, code: "promo") }

it "finds a promotion by its code" do
expect(described_class.by_code_or_id("promo")).to eq promotion
end

it "finds a promotion by its ID" do
expect(described_class.by_code_or_id(promotion.id)).to eq promotion
end

context "when the promotion does not exist" do
it "raises an error" do
expect { described_class.by_code_or_id("nonexistent") }.to raise_error(ActiveRecord::RecordNotFound)
end
end
end
end

0 comments on commit d5dd8b6

Please sign in to comment.