Skip to content

Commit

Permalink
Merge pull request openfoodfoundation#12254 from mkllnk/dfc-create-va…
Browse files Browse the repository at this point in the history
…riant

Add spree_product_uri to SuppliedProduct
  • Loading branch information
dacook authored Mar 13, 2024
2 parents 82c444b + d253eff commit c4a8bf2
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,14 @@ def create

return head :bad_request unless supplied_product

variant = SuppliedProductBuilder.import_variant(supplied_product)
variant = SuppliedProductBuilder.import_variant(
supplied_product,
current_enterprise,
)
product = variant.product

if product.new_record?
product.supplier = current_enterprise
product.save!
end

if variant.new_record?
variant.save!
end
product.save! if product.new_record?
variant.save! if variant.new_record?

supplied_product = SuppliedProductBuilder.supplied_product(variant)
render json: DfcIo.export(supplied_product)
Expand Down
30 changes: 26 additions & 4 deletions engines/dfc_provider/app/services/supplied_product_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,27 @@ def self.supplied_product(variant)
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,
name: variant.product_and_full_name,
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
def self.import_variant(supplied_product, supplier)
product = referenced_spree_product(supplied_product, supplier)

if product_id.present?
product = Spree::Product.find(product_id)
if product
Spree::Variant.new(
product:,
price: 0,
Expand All @@ -31,11 +35,29 @@ def self.import_variant(supplied_product)
end
else
product = import_product(supplied_product)
product.supplier = supplier
product.ensure_standard_variant
product.variants.first
end
end

def self.referenced_spree_product(supplied_product, supplier)
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))

supplier.supplied_products.find_by(id: params["spree_product_id"])
elsif id.present?
supplier.supplied_products.find_by(id:)
end
end

def self.import_product(supplied_product)
Spree::Product.new(
name: supplied_product.name,
Expand Down
9 changes: 7 additions & 2 deletions engines/dfc_provider/lib/dfc_provider/supplied_product.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 30 additions & 0 deletions engines/dfc_provider/spec/requests/supplied_products_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,36 @@
'"ofn:spree_product_id":90000'
)
end

context "when supplying spree_product_uri matching the host" do
it "creates a variant for the existing product" do |example|
supplied_product[:'ofn:spree_product_uri'] =
"http://test.host/api/dfc/enterprises/10000?spree_product_id=90000"
supplied_product[:'dfc-b:hasQuantity'][:'dfc-b:value'] = 6

expect {
submit_request(example.metadata)
product.variants.reload
}
.to change { product.variants.count }.by(1)

# Creates a variant for existing product
variant_id = json_response["@id"].split("/").last.to_i
new_variant = Spree::Variant.find(variant_id)
expect(product.variants).to include(new_variant)
expect(new_variant.unit_value).to eq 6

# Insert static value to keep documentation deterministic:
response.body.gsub!(
"supplied_products/#{variant_id}",
"supplied_products/10001"
)
.gsub!(
%r{active_storage/[0-9A-Za-z/=-]*/logo-white.png},
"active_storage/url/logo-white.png",
)
end
end
end
end
end
Expand Down
203 changes: 200 additions & 3 deletions engines/dfc_provider/spec/services/supplied_product_builder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@

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,
Expand Down Expand Up @@ -79,6 +84,14 @@

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
Expand Down Expand Up @@ -130,4 +143,188 @@
end
end
end

describe ".import_variant" do
let(:imported_variant) { builder.import_variant(supplied_product, supplier) }
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, supplier) }

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, supplier) }
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 spree_product_uri doesn't 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, supplier) }
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 product of another enterprise" do
variant.save!
create(:product, id: 8, supplier: create(:enterprise))

supplied_product.spree_product_uri =
"http://test.host/api/dfc/enterprises/7?spree_product_id=8"
expect(result).to eq nil
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
Loading

0 comments on commit c4a8bf2

Please sign in to comment.