From 30e8f9eb2834a7002f950dd0bab0cad7ac858cac Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Tue, 20 Feb 2024 16:58:42 +1100 Subject: [PATCH] Importing products from DFC catalog Technical demonstration of a complete product export-import roundtrip which we could now do between OFN instances. --- .../admin/dfc_product_imports_controller.rb | 48 ++++++++++++++++++- .../admin/dfc_product_imports/index.html.haml | 3 +- .../app/services/supplied_product_builder.rb | 2 + spec/system/admin/dfc_product_import_spec.rb | 22 ++++++++- 4 files changed, 71 insertions(+), 4 deletions(-) diff --git a/app/controllers/admin/dfc_product_imports_controller.rb b/app/controllers/admin/dfc_product_imports_controller.rb index 000dfc9bf4..d76e42e704 100644 --- a/app/controllers/admin/dfc_product_imports_controller.rb +++ b/app/controllers/admin/dfc_product_imports_controller.rb @@ -1,8 +1,10 @@ # frozen_string_literal: true +require "private_address_check" +require "private_address_check/tcpsocket_ext" + module Admin class DfcProductImportsController < Spree::Admin::BaseController - # Define model class for `can?` permissions: def model_class self.class @@ -12,8 +14,52 @@ module Admin # The plan: # # * Fetch DFC catalog as JSON from URL. + enterprise = OpenFoodNetwork::Permissions.new(spree_current_user) + .managed_product_enterprises.is_primary_producer + .find(params.require(:enterprise_id)) + + catalog_url = params.require(:catalog_url) + + json_catalog = fetch_catalog(catalog_url) + graph = DfcIo.import(json_catalog) + # * First step: import all products for given enterprise. # * Second step: render table and let user decide which ones to import. + imported = graph.map do |subject| + import_product(subject, enterprise) + end + + @count = imported.compact.count + end + + private + + def fetch_catalog(url) + connection = Faraday.new( + request: { timeout: 30 }, + headers: { + 'Content-Type' => 'application/json', + 'Authorization' => "Bearer #{spree_current_user.oidc_account.token}", + } + ) + response = PrivateAddressCheck.only_public_connections do + connection.get(url) + end + + response.body + end + + # Most of this code is the same as in the DfcProvider::SuppliedProductsController. + def import_product(subject, enterprise) + return unless subject.is_a? DataFoodConsortium::Connector::SuppliedProduct + + variant = SuppliedProductBuilder.import_variant(subject, enterprise) + product = variant.product + + product.save! if product.new_record? + variant.save! if variant.new_record? + + variant end end end diff --git a/app/views/admin/dfc_product_imports/index.html.haml b/app/views/admin/dfc_product_imports/index.html.haml index 635129cd94..2b285ac8bc 100644 --- a/app/views/admin/dfc_product_imports/index.html.haml +++ b/app/views/admin/dfc_product_imports/index.html.haml @@ -1,2 +1,3 @@ %h2 Importing a DFC product catalog -%p Catalog size: 0 +%p Imported products: += @count diff --git a/engines/dfc_provider/app/services/supplied_product_builder.rb b/engines/dfc_provider/app/services/supplied_product_builder.rb index 4e2b3d947b..db5195d53f 100644 --- a/engines/dfc_provider/app/services/supplied_product_builder.rb +++ b/engines/dfc_provider/app/services/supplied_product_builder.rb @@ -87,6 +87,8 @@ class SuppliedProductBuilder < DfcBuilder end def self.taxon(supplied_product) + return unless supplied_product.productType + dfc_id = supplied_product.productType.semanticId Spree::Taxon.find_by(dfc_id: ) end diff --git a/spec/system/admin/dfc_product_import_spec.rb b/spec/system/admin/dfc_product_import_spec.rb index d279e5b228..d3aea24884 100644 --- a/spec/system/admin/dfc_product_import_spec.rb +++ b/spec/system/admin/dfc_product_import_spec.rb @@ -1,13 +1,20 @@ # frozen_string_literal: false require 'system_helper' +require_relative '../../../engines/dfc_provider/spec/support/authorization_helper' describe "DFC Product Import" do + include AuthorizationHelper + let(:user) { create(:oidc_user, owned_enterprises: [enterprise]) } let(:enterprise) { create(:enterprise) } + let(:source_product) { create(:product, supplier: enterprise) } before do login_as user + source_product # to be imported + allow(PrivateAddressCheck).to receive(:private_address?).and_return(false) + user.oidc_account.update!(token: allow_token_for(email: user.email)) end it "imports from given catalog" do @@ -17,10 +24,21 @@ describe "DFC Product Import" do # We are testing against our own catalog for now but we want to replace # this with the URL of another app when available. - fill_in "catalog_url", with: "/api/dfc/enterprises/#{enterprise.id}/supplied_products" + host = Rails.application.default_url_options[:host] + url = "http://#{host}/api/dfc/enterprises/#{enterprise.id}/catalog_items" + fill_in "catalog_url", with: url - click_button "Import" + # By feeding our own catalog to the import, we are effectively cloning the + # products. But the DFC product references the spree_product_id which + # make the importer create a variant for that product instead of creating + # a new independent product. + expect { + click_button "Import" + }.to change { + source_product.variants.count + }.by(1) expect(page).to have_content "Importing a DFC product catalog" + expect(page).to have_content "Imported products: 1" end end