From df6e5536615aafd82726298fd8ee6dc746c6be61 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 17 Sep 2025 11:52:31 +1000 Subject: [PATCH] Add SuppliedProducts index endpoint --- .../supplied_products_controller.rb | 24 ++++++++- engines/dfc_provider/config/routes.rb | 1 + .../spec/requests/supplied_products_spec.rb | 53 ++++++++++++++++++- swagger/dfc.yaml | 46 ++++++++++++++++ 4 files changed, 122 insertions(+), 2 deletions(-) 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 51857b80b4..9ccd6ebb66 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 @@ -4,11 +4,33 @@ # SuppliedProducts are products that are managed by an enterprise. module DfcProvider class SuppliedProductsController < DfcProvider::ApplicationController - before_action :check_enterprise + before_action :check_enterprise, except: :index rescue_from JSON::LD::JsonLdError::LoadingDocumentFailed, with: -> do head :bad_request end + def index + # WARNING! + # + # For DFC platforms accessing this with scoped permissions: + # We rely on the ReadEnterprise scope to list enterprises and + # assume that the ReadProducts scope has been granted as well. + # + # This will be correct for the first iteration of the DFC Permissions + # module but needs to be revised later. + enterprises = current_user.enterprises.map do |enterprise| + EnterpriseBuilder.enterprise(enterprise) + end + catalog_items = enterprises.flat_map(&:catalogItems) + + render json: DfcIo.export( + *catalog_items, + *catalog_items.map(&:product), + *catalog_items.map(&:product).flat_map(&:isVariantOf), + *catalog_items.flat_map(&:offers), + ) + end + def create supplied_product = import&.first diff --git a/engines/dfc_provider/config/routes.rb b/engines/dfc_provider/config/routes.rb index 28870be6b2..e92e9ca450 100644 --- a/engines/dfc_provider/config/routes.rb +++ b/engines/dfc_provider/config/routes.rb @@ -13,6 +13,7 @@ DfcProvider::Engine.routes.draw do resources :affiliated_by, only: [:create, :destroy], module: 'enterprise_groups' end resources :persons, only: [:show] + resources :supplied_products, only: [:index] resources :product_groups, only: [:show] resource :affiliate_sales_data, only: [:show] diff --git a/engines/dfc_provider/spec/requests/supplied_products_spec.rb b/engines/dfc_provider/spec/requests/supplied_products_spec.rb index a316154159..8f74da912c 100644 --- a/engines/dfc_provider/spec/requests/supplied_products_spec.rb +++ b/engines/dfc_provider/spec/requests/supplied_products_spec.rb @@ -14,7 +14,11 @@ RSpec.describe "SuppliedProducts", swagger_doc: "dfc.yaml" do ) } let(:variant) { - build(:base_variant, id: 10_001, unit_value: 1, primary_taxon: taxon, supplier: enterprise) + build( + :base_variant, + id: 10_001, sku: "BP", unit_value: 1, + primary_taxon: taxon, supplier: enterprise, + ) } let(:taxon) { build( @@ -34,6 +38,53 @@ RSpec.describe "SuppliedProducts", swagger_doc: "dfc.yaml" do before { login_as user } + path "/api/dfc/supplied_products" do + get "Index SuppliedProducts" do + produces "application/json" + + response "200", "success" do + context "as platform user" do + include_context "authenticated as platform" + + context "without permissions" do + run_test! do + expect(response.body).to eq "" + end + end + + context "with access to products" do + before do + DfcPermission.create!( + user:, enterprise_id: 10_000, + scope: "ReadEnterprise", grantee: "cqcm-dev", + ) + DfcPermission.create!( + user:, enterprise_id: 10_000, + scope: "ReadProducts", grantee: "cqcm-dev", + ) + end + + run_test! do + expect(response.body).to include "Pesto" + end + end + end + + context "as user owning two enterprises" do + run_test! do + expect(response.body).to include "Pesto" + + # Insert static value to keep documentation deterministic: + response.body.gsub!( + %r{active_storage/[0-9A-Za-z/=-]*/logo-white.png}, + "active_storage/url/logo-white.png", + ) + end + end + end + end + end + path "/api/dfc/enterprises/{enterprise_id}/supplied_products" do parameter name: :enterprise_id, in: :path, type: :string diff --git a/swagger/dfc.yaml b/swagger/dfc.yaml index b0d0c0901b..146edf8736 100644 --- a/swagger/dfc.yaml +++ b/swagger/dfc.yaml @@ -810,6 +810,52 @@ paths: dfc-b:URL: https://facebook.com/user '404': description: not found + "/api/dfc/supplied_products": + get: + summary: Index SuppliedProducts + tags: + - SuppliedProducts + responses: + '200': + description: success + content: + application/json: + examples: + test_example: + value: + "@context": https://www.datafoodconsortium.org + "@graph": + - "@id": http://test.host/api/dfc/enterprises/10000/catalog_items/10001 + "@type": dfc-b:CatalogItem + dfc-b:references: http://test.host/api/dfc/enterprises/10000/supplied_products/10001 + dfc-b:sku: BP + dfc-b:stockLimitation: 5 + dfc-b:offeredThrough: http://test.host/api/dfc/enterprises/10000/offers/10001 + - "@id": http://test.host/api/dfc/enterprises/10000/supplied_products/10001 + "@type": dfc-b:SuppliedProduct + dfc-b:name: Pesto - 1g + dfc-b:description: Basil Pesto + dfc-b:hasType: dfc-pt:processed-vegetable + dfc-b:hasQuantity: + "@type": dfc-b:QuantitativeValue + dfc-b:hasUnit: dfc-m:Gram + dfc-b:value: 1.0 + dfc-b:image: http://test.host/rails/active_storage/url/logo-white.png + dfc-b:isVariantOf: http://test.host/api/dfc/product_groups/90000 + 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/product_groups/90000 + "@type": dfc-b:SuppliedProduct + dfc-b:name: Pesto + dfc-b:hasVariant: http://test.host/api/dfc/enterprises/10000/supplied_products/10001 + - "@id": http://test.host/api/dfc/enterprises/10000/offers/10001 + "@type": dfc-b:Offer + dfc-b:hasPrice: + "@type": dfc-b:Price + dfc-b:value: 19.99 + dfc-b:hasUnit: dfc-m:AustralianDollar + dfc-b:stockLimitation: 5 "/api/dfc/enterprises/{enterprise_id}/supplied_products": parameters: - name: enterprise_id