From 302bdfd62861c93dd17e84f82b1fbe459634466a Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 21 Apr 2016 14:40:52 +1000 Subject: [PATCH] Implementing FilterProducts rules in the frontend --- app/controllers/shop_controller.rb | 27 +++++- app/serializers/api/variant_serializer.rb | 5 +- lib/open_food_network/scope_product_to_hub.rb | 2 +- lib/open_food_network/scope_variant_to_hub.rb | 4 + spec/controllers/shop_controller_spec.rb | 88 ++++++++++++++++++- 5 files changed, 121 insertions(+), 5 deletions(-) diff --git a/app/controllers/shop_controller.rb b/app/controllers/shop_controller.rb index 55b6bcc02b..b0324f14e0 100644 --- a/app/controllers/shop_controller.rb +++ b/app/controllers/shop_controller.rb @@ -11,7 +11,11 @@ class ShopController < BaseController def products begin - products_json = OpenFoodNetwork::CachedProductsRenderer.new(current_distributor, current_order_cycle).products_json + renderer = OpenFoodNetwork::CachedProductsRenderer.new(current_distributor, current_order_cycle) + + # If we add any more filtering logic, we should probably + # move it all to a lib class like 'CachedProductsFilterer' + products_json = filtered_json(renderer.products_json) render json: products_json @@ -33,4 +37,25 @@ class ShopController < BaseController end end + private + + def filtered_json(products_json) + tag_rules = relevant_tag_rules + return apply_tag_rules(tag_rules, products_json) if tag_rules.any? + products_json + end + + def apply_tag_rules(tag_rules, products_json) + products_hash = JSON.parse(products_json) + current_distributor.apply_tag_rules( + rules: tag_rules, + subject: products_hash, + customer_tags: current_order.andand.customer.andand.tag_list || [] + ) + JSON.unparse(products_hash) + end + + def relevant_tag_rules + TagRule.for(current_distributor).of_type("FilterProducts") + end end diff --git a/app/serializers/api/variant_serializer.rb b/app/serializers/api/variant_serializer.rb index 6908eaf84f..6a38fe9b93 100644 --- a/app/serializers/api/variant_serializer.rb +++ b/app/serializers/api/variant_serializer.rb @@ -1,6 +1,7 @@ class Api::VariantSerializer < ActiveModel::Serializer - attributes :id, :is_master, :count_on_hand, :name_to_display, :unit_to_display, - :options_text, :on_demand, :price, :fees, :price_with_fees, :product_name + attributes :id, :is_master, :count_on_hand, :name_to_display, :unit_to_display + attributes :options_text, :on_demand, :price, :fees, :price_with_fees, :product_name + attributes :tag_list def price object.price diff --git a/lib/open_food_network/scope_product_to_hub.rb b/lib/open_food_network/scope_product_to_hub.rb index d3e018f295..98ffbd84ff 100644 --- a/lib/open_food_network/scope_product_to_hub.rb +++ b/lib/open_food_network/scope_product_to_hub.rb @@ -4,7 +4,7 @@ module OpenFoodNetwork class ScopeProductToHub def initialize(hub) @hub = hub - @variant_overrides = VariantOverride.indexed @hub + @variant_overrides = VariantOverride.indexed(@hub) end def scope(product) diff --git a/lib/open_food_network/scope_variant_to_hub.rb b/lib/open_food_network/scope_variant_to_hub.rb index 37a2455616..dadcfbd9a8 100644 --- a/lib/open_food_network/scope_variant_to_hub.rb +++ b/lib/open_food_network/scope_variant_to_hub.rb @@ -50,6 +50,10 @@ module OpenFoodNetwork def sku @variant_override.andand.sku || super end + + def tag_list + @variant_override.andand.tag_list || [] + end end end end diff --git a/spec/controllers/shop_controller_spec.rb b/spec/controllers/shop_controller_spec.rb index a4ddab3ad3..bd0dfaae78 100644 --- a/spec/controllers/shop_controller_spec.rb +++ b/spec/controllers/shop_controller_spec.rb @@ -8,7 +8,6 @@ describe ShopController do response.should redirect_to root_path end - describe "with a distributor in place" do before do controller.stub(:current_distributor).and_return distributor @@ -94,5 +93,92 @@ describe ShopController do end end end + + describe "determining rule relevance" do + let(:products_json) { double(:products_json) } + + before do + # allow(controller).to receive(:products_json) { products_json } + allow(controller).to receive(:relevant_tag_rules) { relevant_tag_rules } + allow(controller).to receive(:apply_tag_rules) { "some filtered json" } + end + + context "when no relevant rules exist" do + let(:relevant_tag_rules) { [] } + + before { allow(controller).to receive(:relevant_rules) { relevant_rules } } + + it "does not attempt to apply any rules" do + controller.send(:filtered_json, products_json) + expect(expect(controller).to_not have_received(:apply_tag_rules)) + end + + it "returns products as JSON" do + expect(controller.send(:filtered_json, products_json)).to eq products_json + end + end + + context "when relevant rules exist" do + let(:tag_rule) { create(:filter_products_tag_rule, preferred_customer_tags: "tag1", preferred_variant_tags: "tag1", preferred_matched_variants_visibility: "hidden" ) } + let(:relevant_tag_rules) { [tag_rule] } + + it "attempts to apply any rules" do + controller.send(:filtered_json, products_json) + expect(controller).to have_received(:apply_tag_rules).with(relevant_tag_rules, products_json) + end + + it "returns filtered JSON" do + expect(controller.send(:filtered_json, products_json)).to eq "some filtered json" + end + end + end + + describe "applying tag rules" do + let(:product1) { { id: 1, name: 'product 1', "variants" => [{ id: 4, "tag_list" => ["tag1"] }] } } + let(:product2) { { id: 2, name: 'product 2', "variants" => [{ id: 5, "tag_list" => ["tag1"] }, {id: 9, "tag_list" => ["tag2"]}] } } + let(:product3) { { id: 3, name: 'product 3', "variants" => [{ id: 6, "tag_list" => ["tag3"] }] } } + let!(:products_array) { [product1, product2, product3] } + let!(:products_json) { JSON.unparse( products_array ) } + let(:tag_rule) { create(:filter_products_tag_rule, preferred_customer_tags: "tag1", preferred_variant_tags: "tag1", preferred_matched_variants_visibility: "hidden" ) } + let(:relevant_tag_rules) { [tag_rule] } + + before do + allow(controller).to receive(:current_order) { order } + allow(tag_rule).to receive(:set_context) + allow(tag_rule).to receive(:apply) + allow(distributor).to receive(:apply_tag_rules).and_call_original + end + + context "when a current order with a customer does not exist" do + let(:order) { double(:order, customer: nil) } + + it "sets the context customer_tags as an empty array" do + controller.send(:apply_tag_rules, relevant_tag_rules, products_json) + expect(distributor).to have_received(:apply_tag_rules).with(rules: relevant_tag_rules, subject: JSON.parse(products_json), :customer_tags=>[]) + end + end + + context "when a customer does exist" do + let(:order) { double(:order, customer: double(:customer, tag_list: ["tag1", "tag2"])) } + + it "sets the context customer_tags" do + controller.send(:apply_tag_rules, relevant_tag_rules, products_json) + expect(distributor).to have_received(:apply_tag_rules).with(rules: relevant_tag_rules, subject: JSON.parse(products_json), :customer_tags=>["tag1", "tag2"]) + end + + context "applies the rule" do + before do + allow(tag_rule).to receive(:set_context).and_call_original + allow(tag_rule).to receive(:apply).and_call_original + end + + it "applies the rule" do + result = controller.send(:apply_tag_rules, relevant_tag_rules, products_json) + expect(tag_rule).to have_received(:apply) + expect(result).to eq JSON.unparse([{ id: 2, name: 'product 2', variants: [{id: 9, tag_list: ["tag2"]}] }, product3]) + end + end + end + end end end