From 462c763cd13f40e46daf43f187ee7169ba5512dc Mon Sep 17 00:00:00 2001 From: Gaetan Craig-Riou Date: Wed, 6 Mar 2024 14:27:33 +1100 Subject: [PATCH] Add spree_product_uri to SuppliedProduct Also update SuppliedProductBuilder and specs --- .../app/services/supplied_product_builder.rb | 27 ++- .../lib/dfc_provider/supplied_product.rb | 9 +- .../services/supplied_product_builder_spec.rb | 194 +++++++++++++++++- swagger/dfc.yaml | 4 + 4 files changed, 226 insertions(+), 8 deletions(-) diff --git a/engines/dfc_provider/app/services/supplied_product_builder.rb b/engines/dfc_provider/app/services/supplied_product_builder.rb index ea475da1ec..84b14f53af 100644 --- a/engines/dfc_provider/app/services/supplied_product_builder.rb +++ b/engines/dfc_provider/app/services/supplied_product_builder.rb @@ -6,6 +6,10 @@ class SuppliedProductBuilder < DfcBuilder enterprise_id: variant.product.supplier_id, id: variant.id, ) + product_uri = urls.enterprise_url( + variant.product.supplier_id, + spree_product_id: variant.product_id + ) DfcProvider::SuppliedProduct.new( id, @@ -13,16 +17,16 @@ class SuppliedProductBuilder < DfcBuilder description: variant.description, productType: product_type(variant), quantity: QuantitativeValueBuilder.quantity(variant), + spree_product_uri: product_uri, spree_product_id: variant.product.id, image_url: variant.product&.image&.url(:product) ) end def self.import_variant(supplied_product) - product_id = supplied_product.spree_product_id + product = referenced_spree_product(supplied_product) - if product_id.present? - product = Spree::Product.find(product_id) + if product Spree::Variant.new( product:, price: 0, @@ -36,6 +40,23 @@ class SuppliedProductBuilder < DfcBuilder end end + def self.referenced_spree_product(supplied_product) + uri = supplied_product.spree_product_uri + id = supplied_product.spree_product_id + + if uri.present? + route = Rails.application.routes.recognize_path(uri) + params = Rack::Utils.parse_nested_query(URI.parse(uri).query) + + # Check that the given URI points to us: + return unless uri == urls.enterprise_url(route.merge(params)) + + Spree::Product.find(params["spree_product_id"]) + elsif id.present? + Spree::Product.find(id) + end + end + def self.import_product(supplied_product) Spree::Product.new( name: supplied_product.name, diff --git a/engines/dfc_provider/lib/dfc_provider/supplied_product.rb b/engines/dfc_provider/lib/dfc_provider/supplied_product.rb index 8e8b258206..f2ebc9f61f 100644 --- a/engines/dfc_provider/lib/dfc_provider/supplied_product.rb +++ b/engines/dfc_provider/lib/dfc_provider/supplied_product.rb @@ -2,15 +2,20 @@ module DfcProvider class SuppliedProduct < DataFoodConsortium::Connector::SuppliedProduct - attr_accessor :spree_product_id, :image + attr_accessor :spree_product_id, :spree_product_uri, :image - def initialize(semantic_id, spree_product_id: nil, image_url: nil, **properties) + def initialize( + semantic_id, spree_product_id: nil, spree_product_uri: nil, image_url: nil, **properties + ) super(semantic_id, **properties) self.spree_product_id = spree_product_id + self.spree_product_uri = spree_product_uri self.image = image_url + # This is now replaced by spree_product_uri, keeping it for backward compatibility register_ofn_property("spree_product_id") + register_ofn_property("spree_product_uri") # Temporary solution, will be replaced by "dfc_b:image" in future version of the DFC connector register_ofn_property("image") end diff --git a/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb b/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb index b8e41df742..f4e1518735 100644 --- a/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb +++ b/engines/dfc_provider/spec/services/supplied_product_builder_spec.rb @@ -7,11 +7,16 @@ describe SuppliedProductBuilder do subject(:builder) { described_class } let(:variant) { - build(:variant, id: 5).tap do |v| - v.product.supplier_id = 7 - v.product.primary_taxon = taxon + build(:variant, id: 5, product: spree_product) + } + let(:spree_product) { + create(:product, id: 6, supplier:).tap do |p| + p.primary_taxon = taxon end } + let(:supplier) { + build(:supplier_enterprise, id: 7) + } let(:taxon) { build( :taxon, @@ -79,6 +84,14 @@ describe SuppliedProductBuilder do expect(product.image).to eq variant.product.image.url(:product) end + + it "assigns the product uri" do + product = builder.supplied_product(variant) + + expect(product.spree_product_uri).to eq( + "http://test.host/api/dfc/enterprises/7?spree_product_id=6" + ) + end end describe ".import_product" do @@ -130,4 +143,179 @@ describe SuppliedProductBuilder do end end end + + describe ".import_variant" do + let(:imported_variant) { builder.import_variant(supplied_product) } + let(:supplied_product) do + DfcProvider::SuppliedProduct.new( + "https://example.net/tomato", + name: "Tomato", + description: "Awesome tomato", + quantity: DataFoodConsortium::Connector::QuantitativeValue.new( + unit: DfcLoader.connector.MEASURES.KILOGRAM, + value: 2, + ), + productType: product_type, + ) + end + let(:product_type) { DfcLoader.connector.PRODUCT_TYPES.VEGETABLE.NON_LOCAL_VEGETABLE } + + it "creates a new Spree::Product and variant" do + expect(imported_variant).to be_a(Spree::Variant) + expect(imported_variant.id).to be_nil + + imported_product = imported_variant.product + expect(imported_product).to be_a(Spree::Product) + expect(imported_product.id).to be_nil + expect(imported_product.name).to eq("Tomato") + expect(imported_product.description).to eq("Awesome tomato") + expect(imported_product.variant_unit).to eq("weight") + end + + context "with spree_product_id supplied" do + let(:imported_variant) { builder.import_variant(supplied_product) } + + let(:supplied_product) do + DfcProvider::SuppliedProduct.new( + "https://example.net/tomato", + name: "Tomato", + description: "Better Awesome tomato", + quantity: DataFoodConsortium::Connector::QuantitativeValue.new( + unit: DfcLoader.connector.MEASURES.KILOGRAM, + value: 2, + ), + productType: product_type, + spree_product_id: variant.product.id + ) + end + let(:product_type) { DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK } + let!(:new_taxon) { + create( + :taxon, + name: "Soft Drink", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#soft-drink" + ) + } + + it "update an existing Spree::Product" do + imported_product = imported_variant.product + expect(imported_product.id).to eq(spree_product.id) + expect(imported_product.description).to eq("Better Awesome tomato") + expect(imported_product.primary_taxon).to eq(new_taxon) + end + + it "adds a new variant" do + expect(imported_variant.id).to be_nil + expect(imported_variant.product).to eq(spree_product) + expect(imported_variant.display_name).to eq("Tomato") + expect(imported_variant.unit_value).to eq(2000) + end + end + + context "with spree_product_uri supplied" do + let(:imported_variant) { builder.import_variant(supplied_product) } + let(:product_type) { DfcLoader.connector.PRODUCT_TYPES.DRINK.SOFT_DRINK } + let!(:new_taxon) { + create( + :taxon, + name: "Soft Drink", + dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#soft-drink" + ) + } + + context "when spree_product_uri match the server host" do + let(:supplied_product) do + variant.save! # referenced in spree_product_id + + DfcProvider::SuppliedProduct.new( + "https://example.net/tomato", + name: "Tomato", + description: "Better Awesome tomato", + quantity: DataFoodConsortium::Connector::QuantitativeValue.new( + unit: DfcLoader.connector.MEASURES.KILOGRAM, + value: 2, + ), + productType: product_type, + spree_product_uri: "http://test.host/api/dfc/enterprises/7?spree_product_id=6" + ) + end + + it "update an existing Spree::Product" do + imported_product = imported_variant.product + expect(imported_product.id).to eq(spree_product.id) + expect(imported_product.description).to eq("Better Awesome tomato") + expect(imported_product.primary_taxon).to eq(new_taxon) + end + + it "adds a new variant" do + expect(imported_variant.id).to be_nil + expect(imported_variant.product).to eq(spree_product) + expect(imported_variant.display_name).to eq("Tomato") + expect(imported_variant.unit_value).to eq(2000) + end + end + + context "when doesn't spree_product_uri match the server host" do + let(:supplied_product) do + DfcProvider::SuppliedProduct.new( + "https://example.net/tomato", + name: "Tomato", + description: "Awesome tomato", + quantity: DataFoodConsortium::Connector::QuantitativeValue.new( + unit: DfcLoader.connector.MEASURES.KILOGRAM, + value: 2, + ), + productType: product_type, + spree_product_uri: "http://another.host/api/dfc/enterprises/10/supplied_products/50" + ) + end + + it "creates a new Spree::Product and variant" do + expect(imported_variant).to be_a(Spree::Variant) + expect(imported_variant.id).to be_nil + + imported_product = imported_variant.product + expect(imported_product).to be_a(Spree::Product) + expect(imported_product.id).to be_nil + expect(imported_product.name).to eq("Tomato") + expect(imported_product.description).to eq("Awesome tomato") + expect(imported_product.variant_unit).to eq("weight") + end + end + end + end + + describe ".referenced_spree_product" do + let(:result) { builder.referenced_spree_product(supplied_product) } + let(:supplied_product) do + DfcProvider::SuppliedProduct.new( + "https://example.net/tomato", + name: "Tomato", + ) + end + + it "returns nil when no reference is given" do + expect(result).to eq nil + end + + it "returns a product referenced by URI" do + variant.save! + supplied_product.spree_product_uri = + "http://test.host/api/dfc/enterprises/7?spree_product_id=6" + expect(result).to eq spree_product + end + + it "doesn't return a foreign product referenced by URI" do + variant.save! + supplied_product.spree_product_uri = + "http://another.host/api/dfc/enterprises/7?spree_product_id=6" + expect(result).to eq nil + end + + it "returns a product referenced by id" do + variant.save! + supplied_product.spree_product_id = "6" + expect(result).to eq spree_product + end + end end diff --git a/swagger/dfc.yaml b/swagger/dfc.yaml index 9ba35ae272..acddec3237 100644 --- a/swagger/dfc.yaml +++ b/swagger/dfc.yaml @@ -121,6 +121,7 @@ paths: dfc-b:usageOrStorageCondition: '' dfc-b:totalTheoreticalStock: 0.0 ofn:spree_product_id: 90000 + ofn:spree_product_uri: http://test.host/api/dfc/enterprises/10000?spree_product_id=90000 - "@id": http://test.host/api/dfc/enterprises/10000/offers/10001 "@type": dfc-b:Offer dfc-b:hasPrice: 19.99 @@ -400,6 +401,7 @@ paths: dfc-b:usageOrStorageCondition: '' dfc-b:totalTheoreticalStock: 0.0 ofn:spree_product_id: 90000 + ofn:spree_product_uri: http://test.host/api/dfc/enterprises/10000?spree_product_id=90000 ofn:image: http://test.host/rails/active_storage/url/logo-white.png - "@id": http://test.host/api/dfc/enterprises/10000/catalog_items/10001 "@type": dfc-b:CatalogItem @@ -552,6 +554,7 @@ paths: dfc-b:usageOrStorageCondition: '' dfc-b:totalTheoreticalStock: 0.0 ofn:spree_product_id: 90000 + ofn:spree_product_uri: http://test.host/api/dfc/enterprises/10000?spree_product_id=142 requestBody: content: application/json: @@ -611,6 +614,7 @@ paths: dfc-b:usageOrStorageCondition: '' dfc-b:totalTheoreticalStock: 0.0 ofn:spree_product_id: 90000 + ofn:spree_product_uri: http://test.host/api/dfc/enterprises/10000?spree_product_id=90000 ofn:image: http://test.host/rails/active_storage/url/logo-white.png '404': description: not found