From 4bac83dd8344c78842ff8df84c824765de4c5123 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 11 Jan 2024 15:07:20 +1100 Subject: [PATCH 1/6] Update variant name instead of product via DFC A DFC SuppliedProduct relates to a Spree::Variant and when updating its name we only want to change the name for that variant. Otherwise, when we update the name of the product, it would update the name for all variants and all the corresponding SuppliedProducts. --- engines/dfc_provider/app/services/supplied_product_builder.rb | 2 +- engines/dfc_provider/spec/requests/supplied_products_spec.rb | 2 +- swagger/dfc.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/engines/dfc_provider/app/services/supplied_product_builder.rb b/engines/dfc_provider/app/services/supplied_product_builder.rb index 2d776eafdb..143f9d341e 100644 --- a/engines/dfc_provider/app/services/supplied_product_builder.rb +++ b/engines/dfc_provider/app/services/supplied_product_builder.rb @@ -49,10 +49,10 @@ class SuppliedProductBuilder < DfcBuilder def self.apply(supplied_product, variant) variant.product.assign_attributes( - name: supplied_product.name, description: supplied_product.description, ) + variant.display_name = supplied_product.name QuantitativeValueBuilder.apply(supplied_product.quantity, variant.product) variant.unit_value = variant.product.unit_value end diff --git a/engines/dfc_provider/spec/requests/supplied_products_spec.rb b/engines/dfc_provider/spec/requests/supplied_products_spec.rb index 3e59aa2bad..8f39d971b7 100644 --- a/engines/dfc_provider/spec/requests/supplied_products_spec.rb +++ b/engines/dfc_provider/spec/requests/supplied_products_spec.rb @@ -188,7 +188,7 @@ describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_auto submit_request(example.metadata) variant.reload }.to change { variant.description }.to("DFC-Pesto updated") - .and change { variant.name }.to("Pesto novo") + .and change { variant.display_name }.to("Pesto novo") .and change { variant.unit_value }.to(17) end end diff --git a/swagger/dfc.yaml b/swagger/dfc.yaml index a7a8df3e83..cafd36a9ad 100644 --- a/swagger/dfc.yaml +++ b/swagger/dfc.yaml @@ -489,7 +489,7 @@ paths: "@context": https://www.datafoodconsortium.org "@id": http://test.host/api/dfc/enterprises/10000/supplied_products/10001 "@type": dfc-b:SuppliedProduct - dfc-b:name: Apple - 6g + dfc-b:name: Apple (6g) dfc-b:description: A delicious heritage apple dfc-b:hasType: dfc-pt:non-local-vegetable dfc-b:hasQuantity: From 8d6ae18fb6b5203fd5a02be40d2b07c04bd27986 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 11 Jan 2024 15:59:30 +1100 Subject: [PATCH 2/6] Show Offers on the DFC API --- .../dfc_provider/offers_controller.rb | 18 ++++++++++ engines/dfc_provider/config/routes.rb | 1 + .../dfc_provider/spec/requests/offers_spec.rb | 36 +++++++++++++++++++ swagger/dfc.yaml | 29 +++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 engines/dfc_provider/app/controllers/dfc_provider/offers_controller.rb create mode 100644 engines/dfc_provider/spec/requests/offers_spec.rb diff --git a/engines/dfc_provider/app/controllers/dfc_provider/offers_controller.rb b/engines/dfc_provider/app/controllers/dfc_provider/offers_controller.rb new file mode 100644 index 0000000000..6be9f004c4 --- /dev/null +++ b/engines/dfc_provider/app/controllers/dfc_provider/offers_controller.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module DfcProvider + class OffersController < DfcProvider::ApplicationController + before_action :check_enterprise + + def show + subject = DfcBuilder.offer(variant) + render json: DfcIo.export(subject) + end + + private + + def variant + @variant ||= current_enterprise.supplied_variants.find(params[:id]) + end + end +end diff --git a/engines/dfc_provider/config/routes.rb b/engines/dfc_provider/config/routes.rb index 26762340ac..24dba7201a 100644 --- a/engines/dfc_provider/config/routes.rb +++ b/engines/dfc_provider/config/routes.rb @@ -4,6 +4,7 @@ DfcProvider::Engine.routes.draw do resources :addresses, only: [:show] resources :enterprises, only: [:show] do resources :catalog_items, only: [:index, :show, :update] + resources :offers, only: [:show] resources :supplied_products, only: [:create, :show, :update] resources :social_medias, only: [:show] end diff --git a/engines/dfc_provider/spec/requests/offers_spec.rb b/engines/dfc_provider/spec/requests/offers_spec.rb new file mode 100644 index 0000000000..d29146527d --- /dev/null +++ b/engines/dfc_provider/spec/requests/offers_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require_relative "../swagger_helper" + +describe "Offers", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: true do + let!(:user) { create(:oidc_user) } + let!(:enterprise) { create(:distributor_enterprise, id: 10_000, owner: user) } + let!(:product) { + create( + :product, + id: 90_000, + supplier: enterprise, name: "Pesto", description: "Basil Pesto", + variants: [variant], + ) + } + let(:variant) { build(:base_variant, id: 10_001, unit_value: 1) } + + before { login_as user } + + path "/api/dfc/enterprises/{enterprise_id}/offers/{id}" do + parameter name: :enterprise_id, in: :path, type: :string + parameter name: :id, in: :path, type: :string + + let(:enterprise_id) { enterprise.id } + + get "Show Offer" do + produces "application/json" + + response "200", "success" do + let(:id) { variant.id } + + run_test! + end + end + end +end diff --git a/swagger/dfc.yaml b/swagger/dfc.yaml index cafd36a9ad..b791778324 100644 --- a/swagger/dfc.yaml +++ b/swagger/dfc.yaml @@ -409,6 +409,35 @@ paths: dfc-b:URL: https://facebook.com/user '404': description: not found + "/api/dfc/enterprises/{enterprise_id}/offers/{id}": + parameters: + - name: enterprise_id + in: path + required: true + schema: + type: string + - name: id + in: path + required: true + schema: + type: string + get: + summary: Show Offer + tags: + - Offers + responses: + '200': + description: success + content: + application/json: + examples: + test_example: + value: + "@context": https://www.datafoodconsortium.org + "@id": http://test.host/api/dfc/enterprises/10000/offers/10001 + "@type": dfc-b:Offer + dfc-b:hasPrice: 19.99 + dfc-b:stockLimitation: 5 "/api/dfc/persons/{id}": get: summary: Show person From af5117759370a5e8f9e9c6ef84543e50e81817e1 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 11 Jan 2024 16:34:10 +1100 Subject: [PATCH 3/6] Update price and stock through the DFC API --- .../dfc_provider/application_controller.rb | 4 +++ .../dfc_provider/offers_controller.rb | 10 +++++++ .../supplied_products_controller.rb | 4 --- .../app/services/offer_builder.rb | 8 +++++ engines/dfc_provider/config/routes.rb | 2 +- .../dfc_provider/spec/requests/offers_spec.rb | 29 +++++++++++++++++++ swagger/dfc.yaml | 18 ++++++++++++ 7 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 engines/dfc_provider/app/services/offer_builder.rb diff --git a/engines/dfc_provider/app/controllers/dfc_provider/application_controller.rb b/engines/dfc_provider/app/controllers/dfc_provider/application_controller.rb index 16a830d336..16ebe0411e 100644 --- a/engines/dfc_provider/app/controllers/dfc_provider/application_controller.rb +++ b/engines/dfc_provider/app/controllers/dfc_provider/application_controller.rb @@ -59,5 +59,9 @@ module DfcProvider def current_ability @current_ability ||= Spree::Ability.new(current_user) end + + def import + DfcIo.import(request.body) + end end end diff --git a/engines/dfc_provider/app/controllers/dfc_provider/offers_controller.rb b/engines/dfc_provider/app/controllers/dfc_provider/offers_controller.rb index 6be9f004c4..a40b96c0b6 100644 --- a/engines/dfc_provider/app/controllers/dfc_provider/offers_controller.rb +++ b/engines/dfc_provider/app/controllers/dfc_provider/offers_controller.rb @@ -9,6 +9,16 @@ module DfcProvider render json: DfcIo.export(subject) end + def update + offer = import + + return head :bad_request unless offer + + OfferBuilder.apply(offer, variant) + + variant.save! + end + private def variant diff --git a/engines/dfc_provider/app/controllers/dfc_provider/supplied_products_controller.rb b/engines/dfc_provider/app/controllers/dfc_provider/supplied_products_controller.rb index 21b4c4b59d..d4142f25d6 100644 --- a/engines/dfc_provider/app/controllers/dfc_provider/supplied_products_controller.rb +++ b/engines/dfc_provider/app/controllers/dfc_provider/supplied_products_controller.rb @@ -48,10 +48,6 @@ module DfcProvider private - def import - DfcIo.import(request.body) - end - def variant @variant ||= current_enterprise.supplied_variants.find(params[:id]) end diff --git a/engines/dfc_provider/app/services/offer_builder.rb b/engines/dfc_provider/app/services/offer_builder.rb new file mode 100644 index 0000000000..47d7c304c8 --- /dev/null +++ b/engines/dfc_provider/app/services/offer_builder.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class OfferBuilder < DfcBuilder + def self.apply(offer, variant) + variant.on_hand = offer.stockLimitation + variant.price = offer.price + end +end diff --git a/engines/dfc_provider/config/routes.rb b/engines/dfc_provider/config/routes.rb index 24dba7201a..5bde8f23ba 100644 --- a/engines/dfc_provider/config/routes.rb +++ b/engines/dfc_provider/config/routes.rb @@ -4,7 +4,7 @@ DfcProvider::Engine.routes.draw do resources :addresses, only: [:show] resources :enterprises, only: [:show] do resources :catalog_items, only: [:index, :show, :update] - resources :offers, only: [:show] + resources :offers, only: [:show, :update] resources :supplied_products, only: [:create, :show, :update] resources :social_medias, only: [:show] end diff --git a/engines/dfc_provider/spec/requests/offers_spec.rb b/engines/dfc_provider/spec/requests/offers_spec.rb index d29146527d..5fef671cc0 100644 --- a/engines/dfc_provider/spec/requests/offers_spec.rb +++ b/engines/dfc_provider/spec/requests/offers_spec.rb @@ -32,5 +32,34 @@ describe "Offers", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: true run_test! end end + + put "Update Offer" do + consumes "application/json" + + parameter name: :offer, in: :body, schema: { + example: { + '@context': "https://www.datafoodconsortium.org", + '@id': "http://test.host/api/dfc/enterprises/10000/offers/10001", + '@type': "dfc-b:Offer", + 'dfc-b:hasPrice': 9.99, + 'dfc-b:stockLimitation': 7 + } + } + + let(:id) { variant.id } + let(:offer) { |example| + example.metadata[:operation][:parameters].first[:schema][:example] + } + + response "204", "success" do + it "updates a variant" do |example| + expect { + submit_request(example.metadata) + variant.reload + }.to change { variant.price }.to(9.99) + .and change { variant.on_hand }.to(7) + end + end + end end end diff --git a/swagger/dfc.yaml b/swagger/dfc.yaml index b791778324..6751e2ec05 100644 --- a/swagger/dfc.yaml +++ b/swagger/dfc.yaml @@ -438,6 +438,24 @@ paths: "@type": dfc-b:Offer dfc-b:hasPrice: 19.99 dfc-b:stockLimitation: 5 + put: + summary: Update Offer + parameters: [] + tags: + - Offers + responses: + '204': + description: success + requestBody: + content: + application/json: + schema: + example: + "@context": https://www.datafoodconsortium.org + "@id": http://test.host/api/dfc/enterprises/10000/offers/10001 + "@type": dfc-b:Offer + dfc-b:hasPrice: 9.99 + dfc-b:stockLimitation: 7 "/api/dfc/persons/{id}": get: summary: Show person From bdff8ffea3961a026c63ba4a057734f4875c2545 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 12 Jan 2024 14:19:08 +1100 Subject: [PATCH 4/6] Spec pending setting of on_demand via DFC API The DFC Connector doesn't support importing null values. This has to wait until it's solved in the Connector or we have an urgent use case. --- .../dfc_provider/spec/requests/offers_spec.rb | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/engines/dfc_provider/spec/requests/offers_spec.rb b/engines/dfc_provider/spec/requests/offers_spec.rb index 5fef671cc0..826df7be76 100644 --- a/engines/dfc_provider/spec/requests/offers_spec.rb +++ b/engines/dfc_provider/spec/requests/offers_spec.rb @@ -47,11 +47,30 @@ describe "Offers", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: true } let(:id) { variant.id } - let(:offer) { |example| + let(:offer) { offer_example } + let(:offer_example) { |example| example.metadata[:operation][:parameters].first[:schema][:example] } response "204", "success" do + context "with missing stockLimitation" do + let(:offer) { + offer_example.dup.tap do |o| + o.delete(:'dfc-b:stockLimitation') + end + } + + it "sets the variant to on demand" do |example| + pending "DFC Connector needs to support unset values." + + expect { + submit_request(example.metadata) + variant.reload + }.to change { variant.on_demand }.to(true) + .and change { variant.on_hand }.by(0) + end + end + it "updates a variant" do |example| expect { submit_request(example.metadata) From 583ac6592055447fd8036b587da2c8a8740bd120 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 12 Jan 2024 14:39:14 +1100 Subject: [PATCH 5/6] Move building of Offer to the right module The DfcBuilder was doing everything to start with but we are moving its parts to smaller modules now. --- .../dfc_provider/offers_controller.rb | 2 +- .../dfc_provider/app/services/dfc_builder.rb | 17 +---------------- .../dfc_provider/app/services/offer_builder.rb | 13 +++++++++++++ .../spec/services/offer_builder_spec.rb | 6 +++--- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/engines/dfc_provider/app/controllers/dfc_provider/offers_controller.rb b/engines/dfc_provider/app/controllers/dfc_provider/offers_controller.rb index a40b96c0b6..437897c524 100644 --- a/engines/dfc_provider/app/controllers/dfc_provider/offers_controller.rb +++ b/engines/dfc_provider/app/controllers/dfc_provider/offers_controller.rb @@ -5,7 +5,7 @@ module DfcProvider before_action :check_enterprise def show - subject = DfcBuilder.offer(variant) + subject = OfferBuilder.build(variant) render json: DfcIo.export(subject) end diff --git a/engines/dfc_provider/app/services/dfc_builder.rb b/engines/dfc_provider/app/services/dfc_builder.rb index b3b3373172..f597762f5f 100644 --- a/engines/dfc_provider/app/services/dfc_builder.rb +++ b/engines/dfc_provider/app/services/dfc_builder.rb @@ -12,22 +12,7 @@ class DfcBuilder id, product:, sku: variant.sku, stockLimitation: stock_limitation(variant), - offers: [offer(variant)], - ) - end - - def self.offer(variant) - # We don't have an endpoint for offers yet and this URL is only a - # placeholder for now. The offer is actually affected by order cycle and - # customer tags. We'll solve that at a later stage. - enterprise_url = urls.enterprise_url(id: variant.product.supplier_id) - id = "#{enterprise_url}/offers/#{variant.id}" - offered_to = [] - - DataFoodConsortium::Connector::Offer.new( - id, offeredTo: offered_to, - price: variant.price.to_f, - stockLimitation: stock_limitation(variant), + offers: [OfferBuilder.build(variant)], ) end diff --git a/engines/dfc_provider/app/services/offer_builder.rb b/engines/dfc_provider/app/services/offer_builder.rb index 47d7c304c8..b499c1d945 100644 --- a/engines/dfc_provider/app/services/offer_builder.rb +++ b/engines/dfc_provider/app/services/offer_builder.rb @@ -1,6 +1,19 @@ # frozen_string_literal: true class OfferBuilder < DfcBuilder + def self.build(variant) + id = urls.enterprise_offer_url( + enterprise_id: variant.product.supplier_id, + id: variant.id, + ) + + DataFoodConsortium::Connector::Offer.new( + id, + price: variant.price.to_f, + stockLimitation: stock_limitation(variant), + ) + end + def self.apply(offer, variant) variant.on_hand = offer.stockLimitation variant.price = offer.price diff --git a/engines/dfc_provider/spec/services/offer_builder_spec.rb b/engines/dfc_provider/spec/services/offer_builder_spec.rb index 3d0bf3256a..15ef9c4dda 100644 --- a/engines/dfc_provider/spec/services/offer_builder_spec.rb +++ b/engines/dfc_provider/spec/services/offer_builder_spec.rb @@ -2,7 +2,7 @@ require_relative "../spec_helper" -describe DfcBuilder do +describe OfferBuilder do let(:variant) { build(:variant) } describe ".offer" do @@ -11,7 +11,7 @@ describe DfcBuilder do variant.save! variant.on_hand = 5 - offer = DfcBuilder.offer(variant) + offer = OfferBuilder.build(variant) expect(offer.stockLimitation).to eq 5 end @@ -22,7 +22,7 @@ describe DfcBuilder do variant.on_hand = 5 variant.on_demand = true - offer = DfcBuilder.offer(variant) + offer = OfferBuilder.build(variant) expect(offer.stockLimitation).to eq nil end From aa5c6f34f2c2c3c06dcb22ef4b07009119b32670 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Fri, 12 Jan 2024 14:41:17 +1100 Subject: [PATCH 6/6] Move product type definition to separate module --- engines/dfc_provider/app/services/dfc_builder.rb | 6 ------ .../dfc_provider/app/services/supplied_product_builder.rb | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/engines/dfc_provider/app/services/dfc_builder.rb b/engines/dfc_provider/app/services/dfc_builder.rb index f597762f5f..5411d8ab28 100644 --- a/engines/dfc_provider/app/services/dfc_builder.rb +++ b/engines/dfc_provider/app/services/dfc_builder.rb @@ -22,12 +22,6 @@ class DfcBuilder variant.on_demand ? nil : variant.total_on_hand end - # OFN product categories (taxons) are currently not standardised. - # This is just a dummy value for demos. - def self.product_type - DfcLoader.connector.PRODUCT_TYPES.VEGETABLE.NON_LOCAL_VEGETABLE - end - def self.urls DfcProvider::Engine.routes.url_helpers end diff --git a/engines/dfc_provider/app/services/supplied_product_builder.rb b/engines/dfc_provider/app/services/supplied_product_builder.rb index 143f9d341e..69a9871c17 100644 --- a/engines/dfc_provider/app/services/supplied_product_builder.rb +++ b/engines/dfc_provider/app/services/supplied_product_builder.rb @@ -18,6 +18,12 @@ class SuppliedProductBuilder < DfcBuilder ) end + # OFN product categories (taxons) are currently not standardised. + # This is just a dummy value for demos. + def self.product_type + DfcLoader.connector.PRODUCT_TYPES.VEGETABLE.NON_LOCAL_VEGETABLE + end + def self.import_variant(supplied_product) product_id = supplied_product.spree_product_id