mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-10 03:30:22 +00:00
Merge pull request #13148 from mkllnk/dfc-variants
Add DFC product groups with variants
This commit is contained in:
@@ -48,9 +48,9 @@ module Admin
|
||||
existing_variant = @enterprise.supplied_variants.linked_to(semantic_id)
|
||||
|
||||
if existing_variant
|
||||
SuppliedProductBuilder.update_product(subject, existing_variant)
|
||||
SuppliedProductImporter.update_product(subject, existing_variant)
|
||||
else
|
||||
SuppliedProductBuilder.store_product(subject, @enterprise)
|
||||
SuppliedProductImporter.store_product(subject, @enterprise)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ module Spree
|
||||
searchable_scopes :active, :with_properties
|
||||
|
||||
has_one :image, class_name: "Spree::Image", as: :viewable, dependent: :destroy
|
||||
has_one :semantic_link, as: :subject, dependent: :delete
|
||||
|
||||
has_many :product_properties, dependent: :destroy
|
||||
has_many :properties, through: :product_properties
|
||||
|
||||
@@ -13,13 +13,15 @@ module DfcProvider
|
||||
EnterpriseBuilder.enterprise(enterprise)
|
||||
end
|
||||
person.affiliatedOrganizations = enterprises
|
||||
catalog_items = enterprises.flat_map(&:catalogItems)
|
||||
|
||||
render json: DfcIo.export(
|
||||
person,
|
||||
*person.affiliatedOrganizations,
|
||||
*person.affiliatedOrganizations.flat_map(&:catalogItems),
|
||||
*person.affiliatedOrganizations.flat_map(&:catalogItems).map(&:product),
|
||||
*person.affiliatedOrganizations.flat_map(&:catalogItems).flat_map(&:offers),
|
||||
*enterprises,
|
||||
*catalog_items,
|
||||
*catalog_items.map(&:product),
|
||||
*catalog_items.map(&:product).flat_map(&:isVariantOf),
|
||||
*catalog_items.flat_map(&:offers),
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Show Spree::Product as SuppliedProduct with variants.
|
||||
module DfcProvider
|
||||
class ProductGroupsController < DfcProvider::ApplicationController
|
||||
def show
|
||||
spree_product = permissions.visible_products.find(params[:id])
|
||||
product = ProductGroupBuilder.product_group(spree_product)
|
||||
render json: DfcIo.export(product)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def permissions
|
||||
OpenFoodNetwork::Permissions.new(current_user)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -14,7 +14,7 @@ module DfcProvider
|
||||
|
||||
return head :bad_request unless supplied_product
|
||||
|
||||
variant = SuppliedProductBuilder.store_product(
|
||||
variant = SuppliedProductImporter.store_product(
|
||||
supplied_product,
|
||||
current_enterprise,
|
||||
)
|
||||
@@ -33,7 +33,7 @@ module DfcProvider
|
||||
|
||||
return head :bad_request unless supplied_product
|
||||
|
||||
SuppliedProductBuilder.update_product(supplied_product, variant)
|
||||
SuppliedProductImporter.update_product(supplied_product, variant)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -13,9 +13,13 @@ class DfcCatalog
|
||||
@graph = graph
|
||||
end
|
||||
|
||||
# List all products in this catalog.
|
||||
# These are SuppliedProduct objects which may be grouped as variants.
|
||||
# But we don't return the parent products having variants.
|
||||
def products
|
||||
@products ||= @graph.select do |subject|
|
||||
subject.is_a? DataFoodConsortium::Connector::SuppliedProduct
|
||||
subject.is_a?(DataFoodConsortium::Connector::SuppliedProduct) &&
|
||||
subject.variants.blank?
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
23
engines/dfc_provider/app/services/product_group_builder.rb
Normal file
23
engines/dfc_provider/app/services/product_group_builder.rb
Normal file
@@ -0,0 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ProductGroupBuilder < DfcBuilder
|
||||
def self.product_group(product)
|
||||
id = urls.product_group_url(id: product.id)
|
||||
variants = product.variants.map do |spree_variant|
|
||||
SuppliedProductBuilder.semantic_id(spree_variant)
|
||||
end
|
||||
|
||||
DataFoodConsortium::Connector::SuppliedProduct.new(
|
||||
id, variants:,
|
||||
name: product.name,
|
||||
)
|
||||
end
|
||||
|
||||
def self.apply(supplied_product, spree_product)
|
||||
description = supplied_product.isVariantOf.first.try(:description) ||
|
||||
supplied_product.description
|
||||
name = supplied_product.isVariantOf.first.try(:name)
|
||||
spree_product.description = description if description.present?
|
||||
spree_product.name = name if name.present?
|
||||
end
|
||||
end
|
||||
@@ -1,118 +1,38 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SuppliedProductBuilder < DfcBuilder
|
||||
def self.supplied_product(variant)
|
||||
id = urls.enterprise_supplied_product_url(
|
||||
def self.semantic_id(variant)
|
||||
urls.enterprise_supplied_product_url(
|
||||
enterprise_id: variant.supplier_id,
|
||||
id: variant.id,
|
||||
)
|
||||
end
|
||||
|
||||
def self.supplied_product(variant)
|
||||
product_uri = urls.enterprise_url(
|
||||
variant.supplier_id,
|
||||
spree_product_id: variant.product_id
|
||||
)
|
||||
product_group = ProductGroupBuilder.product_group(variant.product)
|
||||
|
||||
DfcProvider::SuppliedProduct.new(
|
||||
id,
|
||||
semantic_id(variant),
|
||||
name: variant.product_and_full_name,
|
||||
description: variant.description,
|
||||
productType: product_type(variant),
|
||||
quantity: QuantitativeValueBuilder.quantity(variant),
|
||||
isVariantOf: [product_group],
|
||||
spree_product_uri: product_uri,
|
||||
spree_product_id: variant.product.id,
|
||||
image_url: variant.product&.image&.url(:product)
|
||||
)
|
||||
end
|
||||
|
||||
def self.store_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
|
||||
|
||||
def self.update_product(supplied_product, variant)
|
||||
apply(supplied_product, variant)
|
||||
|
||||
variant.product.save!
|
||||
variant.save!
|
||||
|
||||
variant
|
||||
end
|
||||
|
||||
def self.import_variant(supplied_product, supplier)
|
||||
product = referenced_spree_product(supplied_product, supplier)
|
||||
|
||||
if product
|
||||
Spree::Variant.new( product:, supplier:, price: 0,).tap do |variant|
|
||||
apply(supplied_product, variant)
|
||||
end
|
||||
else
|
||||
product = import_product(supplied_product, supplier)
|
||||
product.variants.first.tap { |variant| apply(supplied_product, variant) }
|
||||
end.tap do |variant|
|
||||
link = supplied_product.semanticId
|
||||
variant.semantic_links.new(semantic_id: link) if link.present?
|
||||
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, supplier)
|
||||
Spree::Product.new(
|
||||
name: supplied_product.name,
|
||||
description: supplied_product.description,
|
||||
price: 0, # will be in DFC Offer
|
||||
supplier_id: supplier.id,
|
||||
primary_taxon_id: taxon(supplied_product).id,
|
||||
image: ImageBuilder.import(supplied_product.image),
|
||||
).tap do |product|
|
||||
QuantitativeValueBuilder.apply(supplied_product.quantity, product)
|
||||
product.ensure_standard_variant
|
||||
end
|
||||
end
|
||||
|
||||
def self.apply(supplied_product, variant)
|
||||
variant.product.assign_attributes(description: supplied_product.description)
|
||||
|
||||
variant.display_name = supplied_product.name
|
||||
variant.primary_taxon = taxon(supplied_product)
|
||||
QuantitativeValueBuilder.apply(supplied_product.quantity, variant)
|
||||
|
||||
catalog_item = supplied_product&.catalogItems&.first
|
||||
offer = catalog_item&.offers&.first
|
||||
CatalogItemBuilder.apply_stock(catalog_item, variant)
|
||||
OfferBuilder.apply(offer, variant)
|
||||
end
|
||||
|
||||
def self.product_type(variant)
|
||||
taxon_dfc_id = variant.primary_taxon&.dfc_id
|
||||
|
||||
DataFoodConsortium::Connector::SKOSParser.concepts[taxon_dfc_id]
|
||||
end
|
||||
|
||||
def self.taxon(supplied_product)
|
||||
ProductTypeImporter.taxon(supplied_product.productType)
|
||||
end
|
||||
|
||||
private_class_method :product_type, :taxon
|
||||
private_class_method :product_type
|
||||
end
|
||||
|
||||
138
engines/dfc_provider/app/services/supplied_product_importer.rb
Normal file
138
engines/dfc_provider/app/services/supplied_product_importer.rb
Normal file
@@ -0,0 +1,138 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SuppliedProductImporter < DfcBuilder
|
||||
def self.store_product(subject, enterprise)
|
||||
return unless subject.is_a? DataFoodConsortium::Connector::SuppliedProduct
|
||||
|
||||
variant = import_variant(subject, enterprise)
|
||||
product = variant.product
|
||||
|
||||
product.save! if product.new_record?
|
||||
variant.save! if variant.new_record?
|
||||
|
||||
variant
|
||||
end
|
||||
|
||||
def self.update_product(supplied_product, variant)
|
||||
apply(supplied_product, variant)
|
||||
|
||||
variant.product.save!
|
||||
variant.save!
|
||||
|
||||
variant
|
||||
end
|
||||
|
||||
def self.import_variant(supplied_product, supplier)
|
||||
product = referenced_spree_product(supplied_product, supplier)
|
||||
|
||||
if product
|
||||
Spree::Variant.new( product:, supplier:, price: 0,).tap do |variant|
|
||||
apply(supplied_product, variant)
|
||||
end
|
||||
else
|
||||
product = import_product(supplied_product, supplier)
|
||||
product.variants.first.tap { |variant| apply(supplied_product, variant) }
|
||||
end.tap do |variant|
|
||||
link = supplied_product.semanticId
|
||||
variant.semantic_links.new(semantic_id: link) if link.present?
|
||||
end
|
||||
end
|
||||
|
||||
# DEPRECATION WARNING
|
||||
# Reference by custom `ofn:spree_product_id` and `ofn:spree_product_uri`
|
||||
# properties is now replaced by the official `dfc-b:isVariantOf`.
|
||||
# We will remove the old methods at some point.
|
||||
def self.referenced_spree_product(supplied_product, supplier)
|
||||
spree_product(supplied_product, supplier) ||
|
||||
spree_product_linked(supplied_product, supplier) ||
|
||||
spree_product_from_uri(supplied_product, supplier) ||
|
||||
spree_product_from_id(supplied_product, supplier)
|
||||
end
|
||||
|
||||
def self.spree_product(supplied_product, supplier)
|
||||
supplied_product.isVariantOf.lazy.map do |group|
|
||||
# We may have an object or just the id here:
|
||||
group_id = group.try(:semanticId) || group
|
||||
|
||||
id = begin
|
||||
route = Rails.application.routes.recognize_path(group_id)
|
||||
|
||||
# Check that the given URI points to us:
|
||||
next if group_id != urls.product_group_url(route)
|
||||
|
||||
route[:id]
|
||||
rescue ActionController::RoutingError
|
||||
next
|
||||
end
|
||||
|
||||
supplier.supplied_products.find_by(id:)
|
||||
end.compact.first
|
||||
end
|
||||
|
||||
def self.spree_product_linked(supplied_product, supplier)
|
||||
semantic_ids = supplied_product.isVariantOf.map do |id_or_object|
|
||||
id_or_object.try(:semanticId) || id_or_object
|
||||
end
|
||||
supplier.supplied_products.includes(:semantic_link)
|
||||
.where(semantic_link: { semantic_id: semantic_ids })
|
||||
.first
|
||||
end
|
||||
|
||||
def self.spree_product_from_uri(supplied_product, supplier)
|
||||
uri = supplied_product.spree_product_uri
|
||||
return if uri.blank?
|
||||
|
||||
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"])
|
||||
end
|
||||
|
||||
def self.spree_product_from_id(supplied_product, supplier)
|
||||
id = supplied_product.spree_product_id
|
||||
supplier.supplied_products.find_by(id:) if id.present?
|
||||
end
|
||||
|
||||
def self.import_product(supplied_product, supplier)
|
||||
Spree::Product.new(
|
||||
name: supplied_product.name,
|
||||
description: supplied_product.description,
|
||||
price: 0, # will be in DFC Offer
|
||||
supplier_id: supplier.id,
|
||||
primary_taxon_id: taxon(supplied_product).id,
|
||||
image: ImageBuilder.import(supplied_product.image),
|
||||
semantic_link: semantic_link(supplied_product),
|
||||
).tap do |product|
|
||||
QuantitativeValueBuilder.apply(supplied_product.quantity, product)
|
||||
product.ensure_standard_variant
|
||||
end
|
||||
end
|
||||
|
||||
def self.apply(supplied_product, variant)
|
||||
ProductGroupBuilder.apply(supplied_product, variant.product)
|
||||
|
||||
variant.display_name = supplied_product.name
|
||||
variant.primary_taxon = taxon(supplied_product)
|
||||
QuantitativeValueBuilder.apply(supplied_product.quantity, variant)
|
||||
|
||||
catalog_item = supplied_product&.catalogItems&.first
|
||||
offer = catalog_item&.offers&.first
|
||||
CatalogItemBuilder.apply_stock(catalog_item, variant)
|
||||
OfferBuilder.apply(offer, variant)
|
||||
end
|
||||
|
||||
def self.semantic_link(supplied_product)
|
||||
group = supplied_product.isVariantOf.first
|
||||
semantic_id = group.try(:semanticId) || semantic_id
|
||||
|
||||
SemanticLink.new(semantic_id:) if semantic_id.present?
|
||||
end
|
||||
|
||||
def self.taxon(supplied_product)
|
||||
ProductTypeImporter.taxon(supplied_product.productType)
|
||||
end
|
||||
private_class_method :taxon
|
||||
end
|
||||
@@ -12,6 +12,7 @@ DfcProvider::Engine.routes.draw do
|
||||
resources :affiliated_by, only: [:create, :destroy], module: 'enterprise_groups'
|
||||
end
|
||||
resources :persons, only: [:show]
|
||||
resources :product_groups, only: [:show]
|
||||
|
||||
resource :affiliate_sales_data, only: [:show]
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require_relative "../swagger_helper"
|
||||
|
||||
RSpec.describe "Addresses", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: true do
|
||||
RSpec.describe "Addresses", type: :request, swagger_doc: "dfc.yaml" do
|
||||
let(:user) { create(:oidc_user) }
|
||||
let(:address) { create(:address, id: 40_000) }
|
||||
let(:result) { json_response }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require_relative "../swagger_helper"
|
||||
|
||||
RSpec.describe "AffiliateSalesData", swagger_doc: "dfc.yaml", rswag_autodoc: true do
|
||||
RSpec.describe "AffiliateSalesData", swagger_doc: "dfc.yaml" do
|
||||
let(:user) { create(:oidc_user) }
|
||||
|
||||
before { login_as user }
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
require_relative "../swagger_helper"
|
||||
|
||||
RSpec.describe "CatalogItems", type: :request, swagger_doc: "dfc.yaml",
|
||||
rswag_autodoc: true do
|
||||
RSpec.describe "CatalogItems", type: :request, swagger_doc: "dfc.yaml" do
|
||||
let(:user) { create(:oidc_user, id: 12_345) }
|
||||
let(:enterprise) {
|
||||
create(
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
require_relative "../../swagger_helper"
|
||||
|
||||
RSpec.describe "EnterpriseGroups::AffiliatedBy", type: :request, swagger_doc: "dfc.yaml",
|
||||
rswag_autodoc: true do
|
||||
RSpec.describe "EnterpriseGroups::AffiliatedBy", type: :request, swagger_doc: "dfc.yaml" do
|
||||
let(:user) { create(:oidc_user, id: 12_345) }
|
||||
let(:group) {
|
||||
create(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require_relative "../swagger_helper"
|
||||
|
||||
RSpec.describe "EnterpriseGroups", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: true do
|
||||
RSpec.describe "EnterpriseGroups", type: :request, swagger_doc: "dfc.yaml" do
|
||||
let(:user) { create(:oidc_user, id: 12_345) }
|
||||
let(:group) {
|
||||
create(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require_relative "../swagger_helper"
|
||||
|
||||
RSpec.describe "Enterprises", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: true do
|
||||
RSpec.describe "Enterprises", type: :request, swagger_doc: "dfc.yaml" do
|
||||
let!(:user) { create(:oidc_user) }
|
||||
let!(:enterprise) do
|
||||
create(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require_relative "../swagger_helper"
|
||||
|
||||
RSpec.describe "Offers", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: true do
|
||||
RSpec.describe "Offers", type: :request, swagger_doc: "dfc.yaml" do
|
||||
let!(:user) { create(:oidc_user) }
|
||||
let!(:enterprise) { create(:distributor_enterprise, id: 10_000, owner: user) }
|
||||
let!(:product) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require_relative "../swagger_helper"
|
||||
|
||||
RSpec.describe "Persons", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: true do
|
||||
RSpec.describe "Persons", type: :request, swagger_doc: "dfc.yaml" do
|
||||
let(:user) { create(:oidc_user, id: 10_000) }
|
||||
let(:other_user) { create(:oidc_user) }
|
||||
|
||||
|
||||
49
engines/dfc_provider/spec/requests/product_groups_spec.rb
Normal file
49
engines/dfc_provider/spec/requests/product_groups_spec.rb
Normal file
@@ -0,0 +1,49 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../swagger_helper"
|
||||
|
||||
RSpec.describe "ProductGroups", swagger_doc: "dfc.yaml" do
|
||||
let!(:user) { create(:oidc_user) }
|
||||
let!(:enterprise) { create(:distributor_enterprise, id: 10_000, owner: user) }
|
||||
let!(:product) {
|
||||
create(
|
||||
:product_with_image,
|
||||
id: 90_000,
|
||||
name: "Pesto", description: "Basil Pesto",
|
||||
variants: [variant]
|
||||
)
|
||||
}
|
||||
let(:variant) {
|
||||
build(:base_variant, id: 10_001, unit_value: 1, primary_taxon: taxon, supplier: enterprise)
|
||||
}
|
||||
let(:taxon) {
|
||||
build(
|
||||
:taxon,
|
||||
name: "Processed Vegetable",
|
||||
dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#processed-vegetable"
|
||||
)
|
||||
}
|
||||
|
||||
before { login_as user }
|
||||
|
||||
path "/api/dfc/product_groups/{id}" do
|
||||
parameter name: :enterprise_id, in: :path, type: :string
|
||||
parameter name: :id, in: :path, type: :string
|
||||
|
||||
let(:enterprise_id) { enterprise.id }
|
||||
|
||||
get "Show ProductGroup" do
|
||||
produces "application/json"
|
||||
|
||||
response "200", "success" do
|
||||
let(:id) { product.id }
|
||||
|
||||
run_test! do
|
||||
expect(json_response["@id"]).to eq "http://test.host/api/dfc/product_groups/90000"
|
||||
|
||||
expect(json_response["dfc-b:hasVariant"]).to eq "http://test.host/api/dfc/enterprises/10000/supplied_products/10001"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require_relative "../swagger_helper"
|
||||
|
||||
RSpec.describe "SocialMedias", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: true do
|
||||
RSpec.describe "SocialMedias", type: :request, swagger_doc: "dfc.yaml" do
|
||||
let(:user) { create(:oidc_user) }
|
||||
let(:enterprise) do
|
||||
create(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require_relative "../swagger_helper"
|
||||
|
||||
RSpec.describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswag_autodoc: true do
|
||||
RSpec.describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml" do
|
||||
let!(:user) { create(:oidc_user) }
|
||||
let!(:enterprise) { create(:distributor_enterprise, id: 10_000, owner: user) }
|
||||
let!(:product) {
|
||||
@@ -191,6 +191,7 @@ RSpec.describe "SuppliedProducts", type: :request, swagger_doc: "dfc.yaml", rswa
|
||||
|
||||
run_test! do
|
||||
expect(response.body).to include variant.name
|
||||
expect(json_response["dfc-b:isVariantOf"]).to eq "http://test.host/api/dfc/product_groups/90000"
|
||||
expect(json_response["ofn:spree_product_id"]).to eq 90_000
|
||||
expect(json_response["dfc-b:hasType"]).to eq("dfc-pt:processed-vegetable")
|
||||
expect(json_response["ofn:image"]).to include("logo-white.png")
|
||||
|
||||
@@ -83,272 +83,4 @@ RSpec.describe SuppliedProductBuilder do
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".store_product" do
|
||||
let(:subject) { builder.store_product(product, supplier) }
|
||||
let(:product) {
|
||||
DfcIo.import(product_json).find do |subject|
|
||||
subject.is_a? DataFoodConsortium::Connector::SuppliedProduct
|
||||
end
|
||||
}
|
||||
let(:product_json) { ExampleJson.read("product.GET") }
|
||||
|
||||
before do
|
||||
taxon.save!
|
||||
end
|
||||
|
||||
it "stores a new Spree Product and Variant" do
|
||||
expect { subject }.to change {
|
||||
Spree::Product.count
|
||||
}.by(1)
|
||||
|
||||
expect(subject).to be_a(Spree::Variant)
|
||||
expect(subject).to be_valid
|
||||
expect(subject).to be_persisted
|
||||
expect(subject.name).to eq("Fillet Steak - 201g x 1 Steak")
|
||||
expect(subject.variant_unit).to eq("items")
|
||||
expect(subject.variant_unit_scale).to eq(nil)
|
||||
expect(subject.variant_unit_with_scale).to eq("items")
|
||||
expect(subject.unit_value).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".update_product" do
|
||||
let(:subject) { builder.update_product(product, variant) }
|
||||
let(:product) {
|
||||
DfcIo.import(product_json).find do |subject|
|
||||
subject.is_a? DataFoodConsortium::Connector::SuppliedProduct
|
||||
end
|
||||
}
|
||||
let(:product_json) { ExampleJson.read("product.GET") }
|
||||
|
||||
it "updates a variant" do
|
||||
variant # Create test data first
|
||||
|
||||
expect { subject }.not_to change {
|
||||
Spree::Variant.count
|
||||
}
|
||||
|
||||
expect(subject).to eq variant
|
||||
expect(subject.display_name).to eq "Fillet Steak - 201g x 1 Steak"
|
||||
expect(subject.variant_unit).to eq "items"
|
||||
expect(subject.unit_value).to eq 1
|
||||
expect(subject.on_demand).to eq false
|
||||
expect(subject.on_hand).to eq 11
|
||||
end
|
||||
end
|
||||
|
||||
describe ".import_product" 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,
|
||||
image: "https://cd.net/tomato.png?v=5",
|
||||
)
|
||||
end
|
||||
let(:product_type) { DfcLoader.connector.PRODUCT_TYPES.VEGETABLE.NON_LOCAL_VEGETABLE }
|
||||
let!(:taxon) {
|
||||
create(
|
||||
:taxon,
|
||||
name: "Non local vegetable",
|
||||
dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#non-local-vegetable"
|
||||
)
|
||||
}
|
||||
|
||||
before do
|
||||
stub_request(:get, "https://cd.net/tomato.png?v=5").to_return(
|
||||
status: 200,
|
||||
body: black_logo_path.read,
|
||||
)
|
||||
end
|
||||
|
||||
it "creates a new Spree::Product" do
|
||||
product = builder.import_product(supplied_product, supplier)
|
||||
|
||||
expect(product).to be_a(Spree::Product)
|
||||
expect(product.name).to eq("Tomato")
|
||||
expect(product.description).to eq("Awesome tomato")
|
||||
expect(product.variant_unit).to eq("weight")
|
||||
expect(product.image).to be_present
|
||||
expect(product.image.attachment).to be_attached
|
||||
expect(product.image.url(:product)).to match /^http.*tomato\.png/
|
||||
end
|
||||
|
||||
describe "taxon" do
|
||||
it "assigns the taxon matching the DFC product type" do
|
||||
product = builder.import_product(supplied_product, supplier)
|
||||
|
||||
expect(product.variants.first.primary_taxon).to eq(taxon)
|
||||
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,
|
||||
catalogItems: [catalog_item],
|
||||
)
|
||||
end
|
||||
let(:product_type) { DfcLoader.connector.PRODUCT_TYPES.VEGETABLE.NON_LOCAL_VEGETABLE }
|
||||
let(:catalog_item) {
|
||||
DataFoodConsortium::Connector::CatalogItem.new(
|
||||
nil,
|
||||
# On-demand is expressed as negative stock.
|
||||
# And some APIs send strings instead of numbers...
|
||||
stockLimitation: "-1",
|
||||
offers: [offer],
|
||||
)
|
||||
}
|
||||
let(:offer) {
|
||||
DataFoodConsortium::Connector::Offer.new(
|
||||
nil,
|
||||
price: DataFoodConsortium::Connector::Price.new(value: "15.50"),
|
||||
)
|
||||
}
|
||||
|
||||
it "creates a new Spree::Product and variant" do
|
||||
create(:taxon)
|
||||
|
||||
expect(imported_variant).to be_a(Spree::Variant)
|
||||
expect(imported_variant).to be_valid
|
||||
expect(imported_variant.id).to be_nil
|
||||
expect(imported_variant.semantic_links.size).to eq 1
|
||||
|
||||
link = imported_variant.semantic_links[0]
|
||||
expect(link.semantic_id).to eq "https://example.net/tomato"
|
||||
|
||||
imported_product = imported_variant.product
|
||||
expect(imported_product).to be_a(Spree::Product)
|
||||
expect(imported_product).to be_valid
|
||||
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")
|
||||
|
||||
# Stock can only be checked when persisted:
|
||||
imported_product.save!
|
||||
expect(imported_variant.price).to eq 15.50
|
||||
expect(imported_variant.on_demand).to eq true
|
||||
expect(imported_variant.on_hand).to eq 0
|
||||
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.FRUIT_JUICE }
|
||||
let!(:new_taxon) {
|
||||
create(
|
||||
:taxon,
|
||||
name: "Fruit Juice",
|
||||
dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#fruit-juice"
|
||||
)
|
||||
}
|
||||
|
||||
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_variant.primary_taxon).to eq(new_taxon)
|
||||
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_id: create(:enterprise).id)
|
||||
|
||||
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
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../spec_helper"
|
||||
|
||||
RSpec.describe SuppliedProductImporter do
|
||||
include FileHelper
|
||||
|
||||
subject(:importer) { described_class }
|
||||
let(:variant) {
|
||||
create(:variant, id: 5, product: spree_product, primary_taxon: taxon, supplier:)
|
||||
}
|
||||
let(:spree_product) {
|
||||
create(:product, id: 6)
|
||||
}
|
||||
let(:supplier) {
|
||||
create(:supplier_enterprise, id: 7)
|
||||
}
|
||||
let(:taxon) {
|
||||
build(
|
||||
:taxon,
|
||||
name: "Soft Drink",
|
||||
dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#soft-drink"
|
||||
)
|
||||
}
|
||||
|
||||
describe ".store_product" do
|
||||
let(:subject) { importer.store_product(product, supplier) }
|
||||
let(:product) {
|
||||
DfcIo.import(product_json).find do |subject|
|
||||
subject.is_a? DataFoodConsortium::Connector::SuppliedProduct
|
||||
end
|
||||
}
|
||||
let(:product_json) { ExampleJson.read("product.GET") }
|
||||
|
||||
before do
|
||||
taxon.save!
|
||||
end
|
||||
|
||||
it "stores a new Spree Product and Variant" do
|
||||
expect { subject }.to change {
|
||||
Spree::Product.count
|
||||
}.by(1)
|
||||
|
||||
expect(subject).to be_a(Spree::Variant)
|
||||
expect(subject).to be_valid
|
||||
expect(subject).to be_persisted
|
||||
expect(subject.name).to eq("Fillet Steak - 201g x 1 Steak")
|
||||
expect(subject.variant_unit).to eq("items")
|
||||
expect(subject.variant_unit_scale).to eq(nil)
|
||||
expect(subject.variant_unit_with_scale).to eq("items")
|
||||
expect(subject.unit_value).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".update_product" do
|
||||
let(:subject) { importer.update_product(product, variant) }
|
||||
let(:product) {
|
||||
DfcIo.import(product_json).find do |subject|
|
||||
subject.is_a? DataFoodConsortium::Connector::SuppliedProduct
|
||||
end
|
||||
}
|
||||
let(:product_json) { ExampleJson.read("product.GET") }
|
||||
|
||||
it "updates a variant" do
|
||||
variant # Create test data first
|
||||
|
||||
expect { subject }.not_to change {
|
||||
Spree::Variant.count
|
||||
}
|
||||
|
||||
expect(subject).to eq variant
|
||||
expect(subject.display_name).to eq "Fillet Steak - 201g x 1 Steak"
|
||||
expect(subject.variant_unit).to eq "items"
|
||||
expect(subject.unit_value).to eq 1
|
||||
expect(subject.on_demand).to eq false
|
||||
expect(subject.on_hand).to eq 11
|
||||
end
|
||||
end
|
||||
|
||||
describe ".import_product" 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,
|
||||
image: "https://cd.net/tomato.png?v=5",
|
||||
)
|
||||
end
|
||||
let(:product_type) { DfcLoader.connector.PRODUCT_TYPES.VEGETABLE.NON_LOCAL_VEGETABLE }
|
||||
let!(:taxon) {
|
||||
create(
|
||||
:taxon,
|
||||
name: "Non local vegetable",
|
||||
dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#non-local-vegetable"
|
||||
)
|
||||
}
|
||||
|
||||
before do
|
||||
stub_request(:get, "https://cd.net/tomato.png?v=5").to_return(
|
||||
status: 200,
|
||||
body: black_logo_path.read,
|
||||
)
|
||||
end
|
||||
|
||||
it "creates a new Spree::Product" do
|
||||
product = importer.import_product(supplied_product, supplier)
|
||||
|
||||
expect(product).to be_a(Spree::Product)
|
||||
expect(product.name).to eq("Tomato")
|
||||
expect(product.description).to eq("Awesome tomato")
|
||||
expect(product.variant_unit).to eq("weight")
|
||||
expect(product.image).to be_present
|
||||
expect(product.image.attachment).to be_attached
|
||||
expect(product.image.url(:product)).to match /^http.*tomato\.png/
|
||||
end
|
||||
|
||||
describe "taxon" do
|
||||
it "assigns the taxon matching the DFC product type" do
|
||||
product = importer.import_product(supplied_product, supplier)
|
||||
|
||||
expect(product.variants.first.primary_taxon).to eq(taxon)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".import_variant" do
|
||||
let(:imported_variant) { importer.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,
|
||||
catalogItems: [catalog_item],
|
||||
)
|
||||
end
|
||||
let(:product_type) { DfcLoader.connector.PRODUCT_TYPES.VEGETABLE.NON_LOCAL_VEGETABLE }
|
||||
let(:catalog_item) {
|
||||
DataFoodConsortium::Connector::CatalogItem.new(
|
||||
nil,
|
||||
# On-demand is expressed as negative stock.
|
||||
# And some APIs send strings instead of numbers...
|
||||
stockLimitation: "-1",
|
||||
offers: [offer],
|
||||
)
|
||||
}
|
||||
let(:offer) {
|
||||
DataFoodConsortium::Connector::Offer.new(
|
||||
nil,
|
||||
price: DataFoodConsortium::Connector::Price.new(value: "15.50"),
|
||||
)
|
||||
}
|
||||
|
||||
it "creates a new Spree::Product and variant" do
|
||||
create(:taxon)
|
||||
|
||||
expect(imported_variant).to be_a(Spree::Variant)
|
||||
expect(imported_variant).to be_valid
|
||||
expect(imported_variant.id).to be_nil
|
||||
expect(imported_variant.semantic_links.size).to eq 1
|
||||
|
||||
link = imported_variant.semantic_links[0]
|
||||
expect(link.semantic_id).to eq "https://example.net/tomato"
|
||||
|
||||
imported_product = imported_variant.product
|
||||
expect(imported_product).to be_a(Spree::Product)
|
||||
expect(imported_product).to be_valid
|
||||
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")
|
||||
|
||||
# Stock can only be checked when persisted:
|
||||
imported_product.save!
|
||||
expect(imported_variant.price).to eq 15.50
|
||||
expect(imported_variant.on_demand).to eq true
|
||||
expect(imported_variant.on_hand).to eq 0
|
||||
end
|
||||
|
||||
context "linked to product group" do
|
||||
let(:imported_variant) { importer.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.FRUIT_JUICE }
|
||||
let!(:new_taxon) {
|
||||
create(
|
||||
:taxon,
|
||||
name: "Fruit Juice",
|
||||
dfc_id: "https://github.com/datafoodconsortium/taxonomies/releases/latest/download/productTypes.rdf#fruit-juice"
|
||||
)
|
||||
}
|
||||
|
||||
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_variant.primary_taxon).to eq(new_taxon)
|
||||
end
|
||||
|
||||
it "copies name and description from parent product" do
|
||||
supplied_product.isVariantOf << DfcProvider::SuppliedProduct.new(
|
||||
"some-id", name: "Our tomatoes", description: "Choose a variety."
|
||||
)
|
||||
imported_product = imported_variant.product
|
||||
expect(imported_product.name).to eq "Our tomatoes"
|
||||
expect(imported_product.description).to eq "Choose a variety."
|
||||
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",
|
||||
isVariantOf: [product_group],
|
||||
)
|
||||
end
|
||||
let(:product_group) do
|
||||
DataFoodConsortium::Connector::SuppliedProduct.new(
|
||||
"http://test.host/api/dfc/product_groups/6"
|
||||
)
|
||||
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")
|
||||
expect(imported_product.semantic_link.semantic_id)
|
||||
.to eq "http://test.host/api/dfc/product_groups/6"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe ".referenced_spree_product" do
|
||||
let(:result) { importer.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 semantic id" do
|
||||
variant.save!
|
||||
supplied_product.isVariantOf <<
|
||||
DataFoodConsortium::Connector::SuppliedProduct.new(
|
||||
"http://test.host/api/dfc/product_groups/6"
|
||||
)
|
||||
expect(result).to eq spree_product
|
||||
end
|
||||
|
||||
it "returns a product referenced by external URI" do
|
||||
variant.save!
|
||||
supplied_product.isVariantOf <<
|
||||
DataFoodConsortium::Connector::SuppliedProduct.new(
|
||||
"http://example.net/product_group"
|
||||
)
|
||||
SemanticLink.create!(
|
||||
subject: spree_product,
|
||||
semantic_id: "http://example.net/product_group",
|
||||
)
|
||||
expect(result).to eq spree_product
|
||||
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_id: create(:enterprise).id)
|
||||
|
||||
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
|
||||
@@ -177,29 +177,6 @@ RSpec.configure do |config|
|
||||
ActionController::Base.perform_caching = caching
|
||||
end
|
||||
|
||||
# Take example responses from Rswag specs for API documentation.
|
||||
# https://github.com/rswag/rswag#enable-auto-generation-examples-from-responses
|
||||
config.after(:each, :rswag_autodoc) do |example|
|
||||
# Categories and group operations of the same API endpoint.
|
||||
example.metadata[:operation][:tags] ||= [self.class.top_level_description]
|
||||
|
||||
next if response&.body.blank?
|
||||
|
||||
# Include response as example in the documentation.
|
||||
example.metadata[:response][:content] ||= {}
|
||||
example.metadata[:response][:content].deep_merge!(
|
||||
{
|
||||
"application/json" => {
|
||||
examples: {
|
||||
test_example: {
|
||||
value: JSON.parse(response.body, symbolize_names: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
# Show javascript errors in test output with `js_debug: true`
|
||||
config.after(:each, :js_debug) do
|
||||
errors = page.driver.browser.manage.logs.get(:browser)
|
||||
|
||||
@@ -33,8 +33,8 @@ RSpec.describe "Customers", type: :request, swagger_doc: "v1.yaml", feature: :ap
|
||||
produces "application/json"
|
||||
|
||||
response "200", "Customers list" do
|
||||
param(:enterprise_id) { enterprise1.id }
|
||||
param("extra_fields[customer]") { :balance }
|
||||
let(:enterprise_id) { enterprise1.id }
|
||||
let("extra_fields[customer]") { :balance }
|
||||
schema '$ref': "#/components/schemas/customers_collection"
|
||||
|
||||
run_test!
|
||||
@@ -176,7 +176,7 @@ RSpec.describe "Customers", type: :request, swagger_doc: "v1.yaml", feature: :ap
|
||||
}
|
||||
|
||||
response "201", "Minimal customer created" do
|
||||
param(:customer) do
|
||||
let(:customer) do
|
||||
{
|
||||
email: "test@example.com",
|
||||
enterprise_id: enterprise1.id.to_s
|
||||
@@ -196,7 +196,7 @@ RSpec.describe "Customers", type: :request, swagger_doc: "v1.yaml", feature: :ap
|
||||
end
|
||||
|
||||
response "201", "Example customer created" do
|
||||
param(:customer) do
|
||||
let(:customer) do
|
||||
CustomerSchema.writable_attributes.transform_values do |attribute|
|
||||
attribute[:example]
|
||||
end.merge(
|
||||
@@ -219,7 +219,7 @@ RSpec.describe "Customers", type: :request, swagger_doc: "v1.yaml", feature: :ap
|
||||
end
|
||||
|
||||
response "422", "Unpermitted parameter" do
|
||||
param(:customer) do
|
||||
let(:customer) do
|
||||
{
|
||||
email: "test@example.com",
|
||||
enterprise_id: enterprise1.id.to_s,
|
||||
@@ -234,7 +234,7 @@ RSpec.describe "Customers", type: :request, swagger_doc: "v1.yaml", feature: :ap
|
||||
end
|
||||
|
||||
response "422", "Unprocessable entity" do
|
||||
param(:customer) { {} }
|
||||
let(:customer) { {} }
|
||||
schema '$ref': "#/components/schemas/error_response"
|
||||
|
||||
run_test! do
|
||||
@@ -252,7 +252,7 @@ RSpec.describe "Customers", type: :request, swagger_doc: "v1.yaml", feature: :ap
|
||||
produces "application/json"
|
||||
|
||||
response "200", "Customer" do
|
||||
param(:id) { customer1.id }
|
||||
let(:id) { customer1.id }
|
||||
schema CustomerSchema.schema(
|
||||
require_all: true,
|
||||
extra_fields: { name: :balance, required: true }
|
||||
@@ -269,7 +269,7 @@ RSpec.describe "Customers", type: :request, swagger_doc: "v1.yaml", feature: :ap
|
||||
end
|
||||
|
||||
response "404", "Not found" do
|
||||
param(:id) { 0 }
|
||||
let(:id) { 0 }
|
||||
schema '$ref': "#/components/schemas/error_response"
|
||||
|
||||
run_test! do
|
||||
@@ -281,7 +281,7 @@ RSpec.describe "Customers", type: :request, swagger_doc: "v1.yaml", feature: :ap
|
||||
before { logout }
|
||||
|
||||
response "401", "Unauthorized" do
|
||||
param(:id) { customer1.id }
|
||||
let(:id) { customer1.id }
|
||||
schema '$ref': "#/components/schemas/error_response"
|
||||
|
||||
run_test! do
|
||||
@@ -350,8 +350,8 @@ RSpec.describe "Customers", type: :request, swagger_doc: "v1.yaml", feature: :ap
|
||||
}
|
||||
|
||||
response "200", "Customer updated" do
|
||||
param(:id) { customer1.id }
|
||||
param(:customer) do
|
||||
let(:id) { customer1.id }
|
||||
let(:customer) do
|
||||
{
|
||||
email: "test@example.com",
|
||||
enterprise_id: enterprise1.id.to_s
|
||||
@@ -421,8 +421,8 @@ RSpec.describe "Customers", type: :request, swagger_doc: "v1.yaml", feature: :ap
|
||||
end
|
||||
|
||||
response "422", "Unprocessable entity" do
|
||||
param(:id) { customer1.id }
|
||||
param(:customer) { {} }
|
||||
let(:id) { customer1.id }
|
||||
let(:customer) { {} }
|
||||
schema '$ref': "#/components/schemas/error_response"
|
||||
|
||||
run_test!
|
||||
@@ -435,7 +435,7 @@ RSpec.describe "Customers", type: :request, swagger_doc: "v1.yaml", feature: :ap
|
||||
produces "application/json"
|
||||
|
||||
response "200", "Customer deleted" do
|
||||
param(:id) { customer1.id }
|
||||
let(:id) { customer1.id }
|
||||
schema '$ref': "#/components/schemas/customer"
|
||||
|
||||
run_test!
|
||||
@@ -450,7 +450,7 @@ RSpec.describe "Customers", type: :request, swagger_doc: "v1.yaml", feature: :ap
|
||||
produces "application/json"
|
||||
|
||||
response "200", "Customers list" do
|
||||
param(:enterprise_id) { enterprise1.id }
|
||||
let(:enterprise_id) { enterprise1.id }
|
||||
schema '$ref': "#/components/schemas/customers_collection"
|
||||
|
||||
run_test!
|
||||
|
||||
@@ -71,11 +71,27 @@ RSpec.configure do |config|
|
||||
# the key, this may want to be changed to avoid putting yaml in json files.
|
||||
# Defaults to json. Accepts ':json' and ':yaml'.
|
||||
config.openapi_format = :yaml
|
||||
end
|
||||
|
||||
module RswagExtension
|
||||
def param(args, &)
|
||||
let(args) { instance_eval(&) }
|
||||
# Take example responses from Rswag specs for API documentation.
|
||||
# https://github.com/rswag/rswag#enable-auto-generation-examples-from-responses
|
||||
config.after(:each, swagger_doc: "dfc.yaml") do |example|
|
||||
# Categories and group operations of the same API endpoint.
|
||||
example.metadata[:operation][:tags] ||= [self.class.top_level_description]
|
||||
|
||||
next if response&.body.blank?
|
||||
|
||||
# Include response as example in the documentation.
|
||||
example.metadata[:response][:content] ||= {}
|
||||
example.metadata[:response][:content].deep_merge!(
|
||||
{
|
||||
"application/json" => {
|
||||
examples: {
|
||||
test_example: {
|
||||
value: JSON.parse(response.body, symbolize_names: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
Rswag::Specs::ExampleGroupHelpers.prepend RswagExtension
|
||||
|
||||
@@ -177,8 +177,13 @@ paths:
|
||||
"@type": dfc-b:QuantitativeValue
|
||||
dfc-b:hasUnit: dfc-m:Gram
|
||||
dfc-b:value: 1.0
|
||||
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
|
||||
- "@id": http://test.host/api/dfc/product_groups/90000
|
||||
"@type": dfc-b:SuppliedProduct
|
||||
dfc-b:name: Apple
|
||||
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:
|
||||
@@ -463,6 +468,7 @@ paths:
|
||||
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
|
||||
@@ -552,6 +558,35 @@ paths:
|
||||
"@type": dfc-b:Person
|
||||
'404':
|
||||
description: not found
|
||||
"/api/dfc/product_groups/{id}":
|
||||
parameters:
|
||||
- name: enterprise_id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
get:
|
||||
summary: Show ProductGroup
|
||||
tags:
|
||||
- ProductGroups
|
||||
responses:
|
||||
'200':
|
||||
description: success
|
||||
content:
|
||||
application/json:
|
||||
examples:
|
||||
test_example:
|
||||
value:
|
||||
"@context": https://www.datafoodconsortium.org
|
||||
"@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
|
||||
"/api/dfc/enterprises/{enterprise_id}/social_medias/{name}":
|
||||
get:
|
||||
summary: Show social media
|
||||
@@ -615,6 +650,7 @@ paths:
|
||||
"@type": dfc-b:QuantitativeValue
|
||||
dfc-b:hasUnit: dfc-m:Gram
|
||||
dfc-b:value: 6.0
|
||||
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
|
||||
dfc-b:image: http://test.host/rails/active_storage/url/logo-white.png
|
||||
@@ -675,6 +711,7 @@ paths:
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user