diff --git a/lib/bls/core.rb b/lib/bls/core.rb index 52c5c75..453583a 100644 --- a/lib/bls/core.rb +++ b/lib/bls/core.rb @@ -17,13 +17,20 @@ require_relative "core/resources/api_error" require_relative "core/authorization/bearer" require_relative "core/resources/object" +require_relative "core/resources/object_v2" +require_relative "core/resources/allergen" +require_relative "core/resources/dietary_tag" +require_relative "core/resources/ingredient" require_relative "core/resources/inventory_level" +require_relative "core/resources/nutrition_fact" require_relative "core/resources/order" require_relative "core/resources/order_item" require_relative "core/serializers/order_item_serializer" require_relative "core/resources/menu" require_relative "core/resources/menu_item" require_relative "core/serializers/menu_item_serializer" +require_relative "core/resources/product" +require_relative "core/resources/product_factory" require_relative "core/resources/recipient" require_relative "core/serializers/recipient_serializer" require_relative "core/version" diff --git a/lib/bls/core/resources/allergen.rb b/lib/bls/core/resources/allergen.rb new file mode 100644 index 0000000..6601376 --- /dev/null +++ b/lib/bls/core/resources/allergen.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module Bls + module Core + class Allergen < Bls::Core::ObjectV2; end + end +end diff --git a/lib/bls/core/resources/dietary_tag.rb b/lib/bls/core/resources/dietary_tag.rb new file mode 100644 index 0000000..db080fd --- /dev/null +++ b/lib/bls/core/resources/dietary_tag.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module Bls + module Core + class DietaryTag < Bls::Core::ObjectV2; end + end +end diff --git a/lib/bls/core/resources/ingredient.rb b/lib/bls/core/resources/ingredient.rb new file mode 100644 index 0000000..c82fbc5 --- /dev/null +++ b/lib/bls/core/resources/ingredient.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module Bls + module Core + class Ingredient < Bls::Core::ObjectV2; end + end +end diff --git a/lib/bls/core/resources/nutrition_fact.rb b/lib/bls/core/resources/nutrition_fact.rb new file mode 100644 index 0000000..77e3de7 --- /dev/null +++ b/lib/bls/core/resources/nutrition_fact.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module Bls + module Core + class NutritionFact < Bls::Core::ObjectV2; end + end +end diff --git a/lib/bls/core/resources/object_v2.rb b/lib/bls/core/resources/object_v2.rb new file mode 100644 index 0000000..9e3c447 --- /dev/null +++ b/lib/bls/core/resources/object_v2.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Bls + module Core + class ObjectV2 < Bls::Core::Object + def self.build_from_array(array) + array.map { |object| build(object) } + end + + def resources_path + "#{api.base_url}/api/#{object_name.downcase}" + end + + def resource_path + "#{api.base_url}/api/#{object_name.downcase}/#{id}" + end + + def api + @api ||= Bls::Core::V2.build + end + end + end +end diff --git a/lib/bls/core/resources/product.rb b/lib/bls/core/resources/product.rb new file mode 100644 index 0000000..43a3200 --- /dev/null +++ b/lib/bls/core/resources/product.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Bls + module Core + class Product < Bls::Core::ObjectV2 + def object_name + "products" + end + + def self.build_from_response(data) + factory = ProductFactory.new(data) + factory.create + end + end + end +end diff --git a/lib/bls/core/resources/product_factory.rb b/lib/bls/core/resources/product_factory.rb new file mode 100644 index 0000000..7e01563 --- /dev/null +++ b/lib/bls/core/resources/product_factory.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module Bls + module Core + class ProductFactory + attr_accessor :response + + def initialize(response) + @response = response + end + + def create + product = Product.build(response) + product.ingredients = ingredients + product.allergens = allergens + product.dietary_tags = dietary_tags + product.nutrition_facts = nutrition_facts + product + end + + protected + + def ingredients + Ingredient.build_from_array(response[:ingredients]) + end + + def allergens + Allergen.build_from_array(response[:allergens]) + end + + def dietary_tags + DietaryTag.build_from_array(response[:dietary_tags]) + end + + def nutrition_facts + as_packaged = NutritionFact. + build_from_array(response[:nutrition_facts][:as_packaged]) + as_cooked = NutritionFact. + build_from_array(response[:nutrition_facts]. + fetch(:as_cooked, [])) + { as_packaged: as_packaged, as_cooked: as_cooked } + end + end + end +end diff --git a/lib/bls/core/testing/fake.rb b/lib/bls/core/testing/fake.rb index 009adff..fa65b30 100644 --- a/lib/bls/core/testing/fake.rb +++ b/lib/bls/core/testing/fake.rb @@ -6,6 +6,26 @@ module Testing class Fake def self.configure(*); end + class Allergen + def self.create(code: "MILK", source: "ANCHOVY") + OpenStruct.new(code: code, source: source) + end + end + + class DietaryTag + def self.create(tag: "PESCATARIAN") + OpenStruct.new(tag: tag) + end + end + + class Ingredient + def self.create(code: "MILK", name: "ANCHOVY", quantity: 1, + unit: "serving") + OpenStruct.new(code: code, name: name, quantity: quantity, + unit: unit) + end + end + class InventoryLevel def self.create(sku: "SKU1", date: Date.today.to_s, distribution_center_name: "WEST_COAST", @@ -56,6 +76,12 @@ def initialize(menu_item) end end + class NutritionFact + def self.create(code: "CALORIES", amount: 0, unit: "serving") + OpenStruct.new(code: code, amount: amount, unit: unit) + end + end + class Recipient def self.new(name: nil, street1: nil, street2: nil, city: nil, state: nil, zip: nil, zip4: nil, email: nil, phone: nil, @@ -95,6 +121,15 @@ def self.new(sku:, quantity:, protein_sku:) end end + class Product + def self.new(code:, name:, status: "DRAFT", nutrition_facts: []) + OpenStruct.new(code: code, name: name, status: status, + nutrition_facts: nutrition_facts, + allergens: [Fake::Allergen.create], + dietary_tags: [Fake::DietaryTag.create]) + end + end + def self.client Fake::Client.new end diff --git a/spec/core/resources/product_factory_spec.rb b/spec/core/resources/product_factory_spec.rb new file mode 100644 index 0000000..379beb3 --- /dev/null +++ b/spec/core/resources/product_factory_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +RSpec.describe Bls::Core::ProductFactory do + describe "#create" do + it "assigns the ingredients to the Product" do + response = build_product_response + ingredient = Bls::Core::Ingredient.build(response[:ingredients]) + factory = Bls::Core::ProductFactory.new(response) + + result = factory.create + + expect(result.ingredients).to match_array(ingredient) + end + + it "assigns the allergens to the Product" do + response = build_product_response + allergen = Bls::Core::Allergen.build_from_array(response[:allergens]) + factory = Bls::Core::ProductFactory.new(response) + + result = factory.create + + expect(result.allergens).to match_array(allergen) + end + + it "assigns the dietary tags to the Product" do + response = build_product_response + dietary_tags = Bls::Core::Allergen. + build_from_array(response[:dietary_tags]) + factory = Bls::Core::ProductFactory.new(response) + + result = factory.create + + expect(result.dietary_tags).to match_array(dietary_tags) + end + + it "assigns the nutrition facts to the Product" do + response = build_product_response + factory = Bls::Core::ProductFactory.new(response) + product = factory.create + + result = product.nutrition_facts + + expect(result.keys).to match_array([:as_packaged, :as_cooked]) + end + end +end diff --git a/spec/core/resources/product_spec.rb b/spec/core/resources/product_spec.rb new file mode 100644 index 0000000..30590ab --- /dev/null +++ b/spec/core/resources/product_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +RSpec.describe Bls::Core::Product do + describe ".retrieve" do + it "returns the product" do + product_id = "MP0527" + body = build_product_response(product_id: product_id) + response = build_response(body: body) + stub_api_v2_authentication + stub_api_response response: response + + result = Bls::Core::Product.retrieve(product_id) + + expect(result.code).to eq(product_id) + end + end + + describe ".build_from_response" do + it "creates a product using the product factory" do + response = build_product_response + factory = Bls::Core::ProductFactory.new(response) + allow(Bls::Core::ProductFactory).to receive(:new).and_return(factory) + allow(factory).to receive(:create) + + Bls::Core::Product.build_from_response(response) + + expect(factory).to have_received(:create).once + end + end +end diff --git a/spec/support/fixtures/product_response.json b/spec/support/fixtures/product_response.json new file mode 100644 index 0000000..6a8e02e --- /dev/null +++ b/spec/support/fixtures/product_response.json @@ -0,0 +1,47 @@ +{ + "code": "string", + "name": "string", + "status": "DRAFT", + "refrigeration_type": "NONE", + "refrigeration_level": "STANDARD", + "unit_volume": { + "value": 0.1 + }, + "unit_slots": { + "value": 0.1 + }, + "ingredients": [ + { + "code": "string", + "name": "string", + "quantity": 0.1, + "unit": "string" + } + ], + "nutrition_facts": { + "as_packaged": [ + { + "code": "CALORIES", + "amount": 0, + "unit": "string" + } + ], + "as_cooked": [ + { + "code": "CALORIES", + "amount": 0, + "unit": "string" + } + ] + }, + "allergens": [ + { + "code": "MILK", + "source": "NONE" + } + ], + "dietary_tags": [ + "PESCATARIAN" + ] +} + diff --git a/spec/support/helpers/api_helper.rb b/spec/support/helpers/api_helper.rb index f7bb825..39e0f0d 100644 --- a/spec/support/helpers/api_helper.rb +++ b/spec/support/helpers/api_helper.rb @@ -4,6 +4,8 @@ module ApiHelper ERROR_API_RESPONSE = "spec/support/fixtures/error_response.json".freeze INVENTORY_LEVELS_API_RESPONSE = "spec/support/fixtures/inventory_levels_response.json".freeze + PRODUCT_API_RESPONSE = + "spec/support/fixtures/product_response.json".freeze def authentication_response(access_token:) build_response body: { access_token: access_token } @@ -70,6 +72,10 @@ def read_inventory_levels_api_response parse_json_file(INVENTORY_LEVELS_API_RESPONSE) end + def read_product_api_response + parse_json_file(PRODUCT_API_RESPONSE) + end + def build_menu_response(menu_id: "2023-01-01", menu_items: []) stubbed_response = read_menu_api_response stubbed_response[:id] = menu_id @@ -85,6 +91,12 @@ def build_inventory_levels_response read_inventory_levels_api_response end + def build_product_response(product_id: "string") + stubbed_response = read_product_api_response + stubbed_response[:code] = product_id + stubbed_response + end + def stubbed_menu_items response = read_menu_api_response response[:items]