From 4711ff3100187a42a87f266d83e5cd9de917f974 Mon Sep 17 00:00:00 2001 From: Rohan Likhite Date: Fri, 26 Jun 2026 10:41:29 -0400 Subject: [PATCH] Rename Gem for BatchlineSolutions The business has rebranded, from IntelligentFoods to BatchlineSolutions. Before we publish this gem, we need to replace all instances of the old branding with the new business name. This change addresses the need by: * Replacing IntelligentFoods with BLS * Introducing a Core namespace * Updating the folder structure to align with the new branding --- Gemfile.lock | 4 +- README.md | 27 ++-- bin/console | 2 +- ...ent-foods-ruby.gemspec => bls-core.gemspec | 21 +-- lib/bls/core.rb | 42 ++++++ lib/bls/core/api/v1.rb | 70 ++++++++++ lib/bls/core/api/v1/authenticator.rb | 54 ++++++++ lib/bls/core/api_client.rb | 91 ++++++++++++ lib/bls/core/api_operations/retrieve.rb | 31 +++++ lib/bls/core/authorization/base.rb | 15 ++ lib/bls/core/authorization/basic.rb | 18 +++ lib/bls/core/authorization/bearer.rb | 13 ++ lib/bls/core/authorization/blank.rb | 11 ++ lib/bls/core/errors.rb | 13 ++ lib/bls/core/resources/api_error.rb | 36 +++++ lib/bls/core/resources/inventory_level.rb | 26 ++++ lib/bls/core/resources/menu.rb | 25 ++++ lib/bls/core/resources/menu_item.rb | 11 ++ lib/bls/core/resources/object.rb | 29 ++++ lib/bls/core/resources/order.rb | 116 ++++++++++++++++ lib/bls/core/resources/order_item.rb | 11 ++ lib/bls/core/resources/recipient.rb | 11 ++ lib/bls/core/rspec.rb | 9 ++ .../core/serializers/menu_item_serializer.rb | 15 ++ .../core/serializers/order_item_serializer.rb | 15 ++ .../core/serializers/recipient_serializer.rb | 31 +++++ lib/bls/core/testing/fake.rb | 129 ++++++++++++++++++ lib/bls/core/testing/fake/callback.rb | 114 ++++++++++++++++ lib/bls/core/version.rb | 7 + lib/intelligent_foods.rb | 40 ------ lib/intelligent_foods/api/v1.rb | 68 --------- lib/intelligent_foods/api/v1/authenticator.rb | 52 ------- lib/intelligent_foods/api_client.rb | 89 ------------ .../api_operations/retrieve.rb | 29 ---- lib/intelligent_foods/authorization/base.rb | 13 -- lib/intelligent_foods/authorization/basic.rb | 16 --- lib/intelligent_foods/authorization/bearer.rb | 11 -- lib/intelligent_foods/authorization/blank.rb | 9 -- lib/intelligent_foods/errors.rb | 11 -- lib/intelligent_foods/resources/api_error.rb | 34 ----- .../resources/inventory_level.rb | 24 ---- lib/intelligent_foods/resources/menu.rb | 23 ---- lib/intelligent_foods/resources/menu_item.rb | 9 -- lib/intelligent_foods/resources/object.rb | 27 ---- lib/intelligent_foods/resources/order.rb | 114 ---------------- lib/intelligent_foods/resources/order_item.rb | 9 -- lib/intelligent_foods/resources/recipient.rb | 10 -- lib/intelligent_foods/rspec.rb | 9 -- .../serializers/menu_item_serializer.rb | 13 -- .../serializers/order_item_serializer.rb | 13 -- .../serializers/recipient_serializer.rb | 29 ---- lib/intelligent_foods/testing/fake.rb | 127 ----------------- .../testing/fake/callback.rb | 112 --------------- lib/intelligent_foods/version.rb | 5 - sig/bls-core.rbs | 5 + sig/intelligent_foods.rbs | 3 - .../api/v1/authenticator_spec.rb | 20 +-- .../api/v1_spec.rb | 38 +++--- .../api_client_spec.rb | 64 ++++----- .../resources/inventory_level_spec.rb | 4 +- .../resources/menu_spec.rb | 14 +- .../resources/order_spec.rb | 36 ++--- spec/core_spec.rb | 33 +++++ spec/factories.rb | 10 +- spec/intelligent_foods_spec.rb | 33 ----- spec/spec_helper.rb | 2 +- spec/support/helpers/api_helper.rb | 4 +- 67 files changed, 1101 insertions(+), 1058 deletions(-) rename intelligent-foods-ruby.gemspec => bls-core.gemspec (65%) create mode 100644 lib/bls/core.rb create mode 100644 lib/bls/core/api/v1.rb create mode 100644 lib/bls/core/api/v1/authenticator.rb create mode 100644 lib/bls/core/api_client.rb create mode 100644 lib/bls/core/api_operations/retrieve.rb create mode 100644 lib/bls/core/authorization/base.rb create mode 100644 lib/bls/core/authorization/basic.rb create mode 100644 lib/bls/core/authorization/bearer.rb create mode 100644 lib/bls/core/authorization/blank.rb create mode 100644 lib/bls/core/errors.rb create mode 100644 lib/bls/core/resources/api_error.rb create mode 100644 lib/bls/core/resources/inventory_level.rb create mode 100644 lib/bls/core/resources/menu.rb create mode 100644 lib/bls/core/resources/menu_item.rb create mode 100644 lib/bls/core/resources/object.rb create mode 100644 lib/bls/core/resources/order.rb create mode 100644 lib/bls/core/resources/order_item.rb create mode 100644 lib/bls/core/resources/recipient.rb create mode 100644 lib/bls/core/rspec.rb create mode 100644 lib/bls/core/serializers/menu_item_serializer.rb create mode 100644 lib/bls/core/serializers/order_item_serializer.rb create mode 100644 lib/bls/core/serializers/recipient_serializer.rb create mode 100644 lib/bls/core/testing/fake.rb create mode 100644 lib/bls/core/testing/fake/callback.rb create mode 100644 lib/bls/core/version.rb delete mode 100644 lib/intelligent_foods.rb delete mode 100644 lib/intelligent_foods/api/v1.rb delete mode 100644 lib/intelligent_foods/api/v1/authenticator.rb delete mode 100644 lib/intelligent_foods/api_client.rb delete mode 100644 lib/intelligent_foods/api_operations/retrieve.rb delete mode 100644 lib/intelligent_foods/authorization/base.rb delete mode 100644 lib/intelligent_foods/authorization/basic.rb delete mode 100644 lib/intelligent_foods/authorization/bearer.rb delete mode 100644 lib/intelligent_foods/authorization/blank.rb delete mode 100644 lib/intelligent_foods/errors.rb delete mode 100644 lib/intelligent_foods/resources/api_error.rb delete mode 100644 lib/intelligent_foods/resources/inventory_level.rb delete mode 100644 lib/intelligent_foods/resources/menu.rb delete mode 100644 lib/intelligent_foods/resources/menu_item.rb delete mode 100644 lib/intelligent_foods/resources/object.rb delete mode 100644 lib/intelligent_foods/resources/order.rb delete mode 100644 lib/intelligent_foods/resources/order_item.rb delete mode 100644 lib/intelligent_foods/resources/recipient.rb delete mode 100644 lib/intelligent_foods/rspec.rb delete mode 100644 lib/intelligent_foods/serializers/menu_item_serializer.rb delete mode 100644 lib/intelligent_foods/serializers/order_item_serializer.rb delete mode 100644 lib/intelligent_foods/serializers/recipient_serializer.rb delete mode 100644 lib/intelligent_foods/testing/fake.rb delete mode 100644 lib/intelligent_foods/testing/fake/callback.rb delete mode 100644 lib/intelligent_foods/version.rb create mode 100644 sig/bls-core.rbs delete mode 100644 sig/intelligent_foods.rbs rename spec/{intelligent_foods => core}/api/v1/authenticator_spec.rb (69%) rename spec/{intelligent_foods => core}/api/v1_spec.rb (68%) rename spec/{intelligent_foods => core}/api_client_spec.rb (76%) rename spec/{intelligent_foods => core}/resources/inventory_level_spec.rb (77%) rename spec/{intelligent_foods => core}/resources/menu_spec.rb (77%) rename spec/{intelligent_foods => core}/resources/order_spec.rb (83%) create mode 100644 spec/core_spec.rb delete mode 100644 spec/intelligent_foods_spec.rb diff --git a/Gemfile.lock b/Gemfile.lock index 37fd9fd..1793d91 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - intelligent_foods (0.0.0) + bls-core (0.0.0) GEM remote: https://rubygems.org/ @@ -76,8 +76,8 @@ PLATFORMS arm64-darwin-21 DEPENDENCIES + bls-core! factory_bot - intelligent_foods! json-schema pry (~> 0.14) rake (~> 13.0) diff --git a/README.md b/README.md index 27a8490..c5c7c21 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,30 @@ -# IntelligentFoods +# BatchlineSolutions ## Installation Install the gem and add to the application's Gemfile by executing: - $ bundle add intelligent-foods-ruby + $ bundle add bls-core If bundler is not being used to manage dependencies, install the gem by executing: - $ gem install intelligent-foods-ruby + $ gem install bls-core ## Getting Started ### Setup Work ``` -require "intelligent-foods-ruby" +require "bls/core" -IntelligentFoods.configure do |config| +Bls::Core.configure do |config| config.client_id = "XXXXXX" config.client_secret = "YYYYYY" config.environment = "preview" end ``` -### Authentication - -``` -@client = IntelligentFoods.client -@client.authenticate! -``` - ### Usage ## Development @@ -52,15 +45,15 @@ For `rspec`, add the following line to your `spec/rails_helper.rb` or `spec/spec_helper` if `rails_helper` does not exist: ``` -require "intelligent_foods/rspec" +require "bls/core/rspec" ``` ## Contributing Bug reports and pull requests are welcome on GitHub at -https://github.com/[USERNAME]/intelligent-foods-ruby. This project is intended +https://github.com/[USERNAME]/core-ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected -to adhere to the [code of conduct](https://github.com/[USERNAME]/intelligent-foods-ruby/blob/main/CODE_OF_CONDUCT.md). +to adhere to the [code of conduct](https://github.com/[USERNAME]/core-ruby/blob/main/CODE_OF_CONDUCT.md). ## License @@ -68,5 +61,5 @@ The gem is available as open source under the terms of the [MIT License](https:/ ## Code of Conduct -Everyone interacting in the IntelligentFoods project's codebases, issue -trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/intelligent-foods-ruby/blob/main/CODE_OF_CONDUCT.md). +Everyone interacting in the BatchlineSolutions project's codebases, issue +trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/core-ruby/blob/main/CODE_OF_CONDUCT.md). diff --git a/bin/console b/bin/console index 4eb347d..488780d 100755 --- a/bin/console +++ b/bin/console @@ -2,7 +2,7 @@ # frozen_string_literal: true require "bundler/setup" -require "intelligent_foods" +require "bls/core" # You can add fixtures and/or initialization code here to make experimenting # with your gem easier. You can also use a different console, if you like. diff --git a/intelligent-foods-ruby.gemspec b/bls-core.gemspec similarity index 65% rename from intelligent-foods-ruby.gemspec rename to bls-core.gemspec index 6181529..408691d 100644 --- a/intelligent-foods-ruby.gemspec +++ b/bls-core.gemspec @@ -1,23 +1,24 @@ # frozen_string_literal: true -require_relative "lib/intelligent_foods/version" +require_relative "lib/bls/core/version" Gem::Specification.new do |spec| - spec.name = "intelligent_foods" - spec.version = IntelligentFoods::VERSION - spec.authors = ["Chris Woodford"] - spec.email = ["chris@gobble.com"] - - spec.summary = "Ruby SDK for the Intelligent Foods API" - spec.homepage = "https://intelligentfoods.com" + spec.name = "bls-core" + spec.version = Bls::Core::VERSION + spec.authors = ["Chris Woodford", "Rohan Likhite"] + spec.email = ["chris.woodford@batchline.solutions", + "rohan.likhite@batchline.solutions"] + + spec.summary = "Ruby SDK for the Batchline Solutions Core API" + spec.homepage = "https://batchline.solutions.com" spec.license = "MIT" spec.required_ruby_version = ">= 3.1" spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'" spec.metadata["homepage_uri"] = spec.homepage - spec.metadata["source_code_uri"] = "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/Intelligent-Foods/intelligent-foods-ruby" - spec.metadata["changelog_uri"] = "/Intelligent-Foods/intelligent-foods-rub/CHANGELOG.mdy" + spec.metadata["source_code_uri"] = "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/batchlinesolutions/core-ruby" + spec.metadata["changelog_uri"] = "/batchlinesolutions/core-ruby//CHANGELOG.mdy" # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. diff --git a/lib/bls/core.rb b/lib/bls/core.rb new file mode 100644 index 0000000..f672ea4 --- /dev/null +++ b/lib/bls/core.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require "base64" +require "json" +require "net/http" +require "ostruct" + +require_relative "core/api_client" +require_relative "core/api/v1" +require_relative "core/api/v1/authenticator" +require_relative "core/api_operations/retrieve" +require_relative "core/authorization/base" +require_relative "core/authorization/basic" +require_relative "core/authorization/blank" +require_relative "core/resources/api_error" +require_relative "core/authorization/bearer" +require_relative "core/resources/object" +require_relative "core/resources/inventory_level" +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/recipient" +require_relative "core/serializers/recipient_serializer" +require_relative "core/version" +require_relative "core/errors" + +module Bls + module Core + class Error < StandardError; end + + class << self + attr_accessor :client_id, :client_secret, :environment + + def configure + yield self + end + end + end +end diff --git a/lib/bls/core/api/v1.rb b/lib/bls/core/api/v1.rb new file mode 100644 index 0000000..2ac36f2 --- /dev/null +++ b/lib/bls/core/api/v1.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +module Bls + module Core + class V1 + attr_accessor :authentication + attr_reader :environment, :username, :password + + PRODUCTION = "production" + STAGING = "staging" + + def initialize(username: nil, password: nil, authentication: nil, + environment: STAGING) + @authentication = authentication + @environment = environment + @username = username + @password = password + end + + def base_url + "https://api.sunbasket.#{tld}/partner/v1" + end + + def authenticate! + return if authenticated? + + authenticator = V1::Authenticator.build(self) + @authentication = authenticator.save! + end + + def client + build_api_client! + end + + def authenticated? + authentication.present? + end + + def reset_authentication + @authentication = nil + self + end + + def self.build(config: Bls::Core) + new(environment: config.environment, + username: config.client_id, + password: config.client_secret) + end + + protected + + def build_api_client! + authenticate! + Bls::Core::ApiClient.build(self) + end + + def tld + @tld ||= determine_tld + end + + def determine_tld + if environment == PRODUCTION + "com" + else + "dev" + end + end + end + end +end diff --git a/lib/bls/core/api/v1/authenticator.rb b/lib/bls/core/api/v1/authenticator.rb new file mode 100644 index 0000000..77c9e17 --- /dev/null +++ b/lib/bls/core/api/v1/authenticator.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module Bls + module Core + class V1::Authenticator + attr_accessor :base_url, :client_id, :client_secret + + def initialize(base_url:, client_id:, client_secret:) + @base_url = base_url + @client_id = client_id + @client_secret = client_secret + end + + def save! + path = "#{base_url}/token" + body = { client_id: client_id, client_secret: client_secret } + auth = Authorization::Basic. + factory(client_id: client_id, client_secret: client_secret) + client = Core::ApiClient.new(authentication: auth) + response = client.post(path: path, body: body) + handle_authentication_response!(response: response.data) + end + + def self.build(api) + new(base_url: api.base_url, + client_id: api.username, + client_secret: api.password) + end + + protected + + def handle_authentication_response!(response:) + if response_has_errors?(response) + handle_errors! + else + handle_successful_authentication_response(response) + end + end + + def response_has_errors?(response) + response.has_key?(:error) + end + + def handle_successful_authentication_response(response) + access_token = response[:access_token] + Core::Authorization::Bearer.new(token: access_token) + end + + def handle_errors! + raise AuthenticationError.new(status: 401, title: "Authentication Failed") + end + end + end +end diff --git a/lib/bls/core/api_client.rb b/lib/bls/core/api_client.rb new file mode 100644 index 0000000..6a7ad9b --- /dev/null +++ b/lib/bls/core/api_client.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +module Bls + module Core + class ApiClient + attr_reader :api, :authentication + + def initialize(api: nil, authentication: nil) + @api = api + @authentication = authentication || Authorization::Blank.new + end + + def post(path:, body:) + uri = URI(path) + request = build_request_with_body(uri: uri, body: body) + execute_request(request: request, uri: request.uri) + end + + def delete(path:, **) + uri = URI(path) + request = Net::HTTP::Delete.new(uri) + execute_request(request: request, uri: request.uri) + end + + def get(path:, **) + uri = URI(path) + request = Net::HTTP::Get.new(uri) + execute_request(request: request, uri: request.uri) + end + + def execute_request(request:, uri:) + Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http| + request["Authorization"] = authentication.header + response = http.request(request) + handle_response(response: response) + end + end + + def self.build(api) + new(api: api, authentication: api.authentication) + end + + protected + + def build_request_with_body(uri:, body:) + request = Net::HTTP::Post.new(uri) + request.body = body.to_json + request["content-type"] = "application/json" + request + end + + def handle_response(response:) + if authentication_failed?(response.code) + handle_authentication_error! + else + body = parse_response_body(response) + OpenStruct.new(data: body, success?: request_successful?(response.code)) + end + end + + def handle_authentication_error! + api.reset_authentication + raise AuthenticationError.new(status: 401, + title: "Authentication Failed") + end + + def parse_response_body(response) + return {} if empty_response?(response.code) + return {} if redirection?(response.code) + + JSON.parse(response.body, symbolize_names: true) + end + + def empty_response?(response_code) + response_code.to_i == 204 + end + + def redirection?(response_code) + response_code.to_i.between?(300, 399) + end + + def request_successful?(response_code) + response_code.to_i.between?(200, 299) + end + + def authentication_failed?(response_code) + response_code.to_i == 401 + end + end + end +end diff --git a/lib/bls/core/api_operations/retrieve.rb b/lib/bls/core/api_operations/retrieve.rb new file mode 100644 index 0000000..0862dac --- /dev/null +++ b/lib/bls/core/api_operations/retrieve.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Bls + module Core + module ApiOperations + module Retrieve + def retrieve(id) + resource = new(id: id) + api_client = resource.client + response = api_client.get(path: resource.resource_path) + if response.success? + build_from_response(response.data) + else + raise ResourceRetrievalError.build(response) + end + end + + def retrieve_all + resource = new + api_client = resource.client + response = api_client.get(path: resource.resources_path) + if response.success? + response.data.map { |id| new(id: id) } + else + raise ResourceRetrievalError.build(response) + end + end + end + end + end +end diff --git a/lib/bls/core/authorization/base.rb b/lib/bls/core/authorization/base.rb new file mode 100644 index 0000000..52630a6 --- /dev/null +++ b/lib/bls/core/authorization/base.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Bls + module Core + module Authorization + class Base + attr_reader :token + + def initialize(token: nil) + @token = token + end + end + end + end +end diff --git a/lib/bls/core/authorization/basic.rb b/lib/bls/core/authorization/basic.rb new file mode 100644 index 0000000..010c8ea --- /dev/null +++ b/lib/bls/core/authorization/basic.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Bls + module Core + module Authorization + class Basic < Base + def header + "Basic #{token}" + end + + def self.factory(client_id:, client_secret:) + encoded_token = Base64.strict_encode64("#{client_id}:#{client_secret}") + new(token: encoded_token) + end + end + end + end +end diff --git a/lib/bls/core/authorization/bearer.rb b/lib/bls/core/authorization/bearer.rb new file mode 100644 index 0000000..81f960d --- /dev/null +++ b/lib/bls/core/authorization/bearer.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Bls + module Core + module Authorization + class Bearer < Base + def header + "Bearer #{token}" + end + end + end + end +end diff --git a/lib/bls/core/authorization/blank.rb b/lib/bls/core/authorization/blank.rb new file mode 100644 index 0000000..951c81b --- /dev/null +++ b/lib/bls/core/authorization/blank.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Bls + module Core + module Authorization + class Blank < Base + def header; end + end + end + end +end diff --git a/lib/bls/core/errors.rb b/lib/bls/core/errors.rb new file mode 100644 index 0000000..eeb7f03 --- /dev/null +++ b/lib/bls/core/errors.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Bls + module Core + class ResourceRetrievalError < ApiError; end + + class OrderNotCancelledError < ApiError; end + + class OrderNotCreatedError < ApiError; end + + class AuthenticationError < ApiError; end + end +end diff --git a/lib/bls/core/resources/api_error.rb b/lib/bls/core/resources/api_error.rb new file mode 100644 index 0000000..1a7475f --- /dev/null +++ b/lib/bls/core/resources/api_error.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Bls + module Core + class ApiError < StandardError + attr_reader :status, :title, :details + + def initialize(status:, title:, details: nil) + @status = status + @title = title + @details = details + super + end + + def message + if details.blank? + "#{status} - #{title}" + else + "#{status} #{title} - #{description}" + end + end + + def self.build(response) + data = response.data + data => { status:, title:, details: } + new(status: status, title: title, details: details) + end + + protected + + def description + details[:description] + end + end + end +end diff --git a/lib/bls/core/resources/inventory_level.rb b/lib/bls/core/resources/inventory_level.rb new file mode 100644 index 0000000..29195f5 --- /dev/null +++ b/lib/bls/core/resources/inventory_level.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Bls + module Core + class InventoryLevel < Core::Object + def object_name + "inventory-level" + end + + def resources_path + "#{api.base_url}/products/inventory-levels" + end + + def self.retrieve_all + resource = new + api_client = resource.client + response = api_client.get(path: resource.resources_path) + if response.success? + response.data[:products].map { |product| build(product) } + else + raise ResourceRetrievalError.build(response) + end + end + end + end +end diff --git a/lib/bls/core/resources/menu.rb b/lib/bls/core/resources/menu.rb new file mode 100644 index 0000000..6aba288 --- /dev/null +++ b/lib/bls/core/resources/menu.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Bls + module Core + class Menu < Core::Object + def initialize(args = {}) + super + end + + def object_name + "menu" + end + + def resources_path + "#{api.base_url}/menus" + end + + def self.build_from_response(data) + menu = build(data) + menu.items = MenuItem.build(data[:items]) + menu + end + end + end +end diff --git a/lib/bls/core/resources/menu_item.rb b/lib/bls/core/resources/menu_item.rb new file mode 100644 index 0000000..c9894a8 --- /dev/null +++ b/lib/bls/core/resources/menu_item.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Bls + module Core + class MenuItem < Core::Object + def initialize(args = {}) + super + end + end + end +end diff --git a/lib/bls/core/resources/object.rb b/lib/bls/core/resources/object.rb new file mode 100644 index 0000000..edead66 --- /dev/null +++ b/lib/bls/core/resources/object.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Bls + module Core + class Object < OpenStruct + extend Bls::Core::ApiOperations::Retrieve + + def self.build(data) + JSON.parse(data.to_json, object_class: self) + end + + def resources_path + "#{api.base_url}/#{object_name.downcase}" + end + + def resource_path + "#{api.base_url}/#{object_name.downcase}/#{id}" + end + + def client + api.client + end + + def api + @api ||= Bls::Core::V1.build + end + end + end +end diff --git a/lib/bls/core/resources/order.rb b/lib/bls/core/resources/order.rb new file mode 100644 index 0000000..2d74ba1 --- /dev/null +++ b/lib/bls/core/resources/order.rb @@ -0,0 +1,116 @@ +# frozen_string_literal: true + +module Bls + module Core + class Order < Core::Object + ACCEPTED = "accepted" + CANCELLED = "cancelled" + ERROR = "error" + IN_PROCESS = "in_process" + INITIALIZED = "initialized" + PROCESSED = "processed" + + attr_reader :skip_temperature_check, :skip_address_check + + def initialize(skip_temperature_check: false, skip_address_check: false, **) + super + @skip_temperature_check = skip_temperature_check + @skip_address_check = skip_address_check + end + + def self.build_from_response(data) + order = build(data) + order[:items] = OrderItem.build(data[:items]) + order[:ship_to] = Recipient.build(data[:ship_to]) + order + end + + def object_name + "order" + end + + def create! + response = client.post(path: resources_path, body: request_body) + if response.success? + Order::build_from_response(response.data) + else + handle_order_not_created(response) + end + end + + def cancel! + response = client.delete(path: resource_path) + if response.success? + mark_as_cancelled + self + else + handle_order_not_cancelled(response) + end + end + + def request_body + @request_body ||= { + menu_id: menu.id, + reference_id: external_id.to_s, + ship_to: ship_to, + delivery_date: delivery_date, + items: items_json, + validation_options: validation_options, + callback_url: callback_url, + callback_headers: callback_headers, + } + end + + def cancelled? + status.downcase == CANCELLED + end + + def valid? + status.downcase != ERROR + end + + def accepted? + status.downcase == ACCEPTED + end + + protected + + def handle_order_not_created(response) + mark_as_invalid + raise OrderNotCreatedError.build(response) + end + + def handle_order_not_cancelled(response) + mark_as_invalid + raise OrderNotCancelledError.build(response) + end + + def mark_as_cancelled + self[:status] = CANCELLED + end + + def mark_as_invalid + self[:status] = ERROR + end + + def items_json + return if items.nil? + + items.map do |item| + OrderItemSerializer.new(item).to_json + end + end + + def ship_to + RecipientSerializer.new(recipient).to_json + end + + def validation_options + { + skip_temperature_check: skip_temperature_check, + skip_address_check: skip_address_check, + } + end + end + end +end diff --git a/lib/bls/core/resources/order_item.rb b/lib/bls/core/resources/order_item.rb new file mode 100644 index 0000000..5d147a2 --- /dev/null +++ b/lib/bls/core/resources/order_item.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Bls + module Core + class OrderItem < Core::Object + def initialize(args = {}) + super + end + end + end +end diff --git a/lib/bls/core/resources/recipient.rb b/lib/bls/core/resources/recipient.rb new file mode 100644 index 0000000..baeb935 --- /dev/null +++ b/lib/bls/core/resources/recipient.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Bls + module Core + class Recipient < Core::Object + def initialize(args = {}) + super + end + end + end +end diff --git a/lib/bls/core/rspec.rb b/lib/bls/core/rspec.rb new file mode 100644 index 0000000..7c624af --- /dev/null +++ b/lib/bls/core/rspec.rb @@ -0,0 +1,9 @@ +require "rspec/rails" +require "bls/core/testing/fake" +require "bls/core/testing/fake/callback" + +RSpec.configure do |config| + config.before(:each) do + stub_const("Bls::Core", Bls::Core::Testing::Fake) + end +end diff --git a/lib/bls/core/serializers/menu_item_serializer.rb b/lib/bls/core/serializers/menu_item_serializer.rb new file mode 100644 index 0000000..022d0ff --- /dev/null +++ b/lib/bls/core/serializers/menu_item_serializer.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Bls + module Core + class MenuItemSerializer < SimpleDelegator + def to_json + { + id: id, + sku: sku, + name: name, + } + end + end + end +end diff --git a/lib/bls/core/serializers/order_item_serializer.rb b/lib/bls/core/serializers/order_item_serializer.rb new file mode 100644 index 0000000..768f053 --- /dev/null +++ b/lib/bls/core/serializers/order_item_serializer.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Bls + module Core + class OrderItemSerializer < SimpleDelegator + def to_json + { + sku: sku, + protein_sku: protein_sku, + quantity: quantity, + } + end + end + end +end diff --git a/lib/bls/core/serializers/recipient_serializer.rb b/lib/bls/core/serializers/recipient_serializer.rb new file mode 100644 index 0000000..3a1a02f --- /dev/null +++ b/lib/bls/core/serializers/recipient_serializer.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Bls + module Core + class RecipientSerializer < SimpleDelegator + def to_json + recipient_object = { + name: name, + street1: street1, + street2: street2, + city: city, + state: state, + zip: zip, + zip4: zip4, + email: email, + phone: phone, + delivery_instructions: delivery_instructions, + } + remove_blank_values(recipient_object) + end + + protected + + def remove_blank_values(obj) + obj.delete_if do |_k, v| + v.blank? + end + end + end + end +end diff --git a/lib/bls/core/testing/fake.rb b/lib/bls/core/testing/fake.rb new file mode 100644 index 0000000..5c03dd3 --- /dev/null +++ b/lib/bls/core/testing/fake.rb @@ -0,0 +1,129 @@ +# frozen_string_literal: true + +module Bls + module Core + module Testing + class Fake + def self.configure(*); end + + class DistributionCenterInventory + def self.create(name: "WEST_COAST", quantity: 100) + OpenStruct.new(distribution_center: name, quantity: quantity) + end + end + + class InventoryLevel + def self.create(sku: "SKU1", date: Date.today.to_s, + dc_inventory: [DistributionCenterInventory.create]) + inventory_level = OpenStruct.new( + date: date, + inventory_by_distribution_center: dc_inventory + ) + OpenStruct.new(sku: sku, inventory_levels: [inventory_level]) + end + end + + class MenuItem + def self.create(id: SecureRandom.uuid, sku: "SKU1", + name: "FakeMenuItem") + OpenStruct.new(id: id, sku: sku, name: name) + end + end + + class Menu + def self.new(id: Date.today.to_s) + items = [Fake::MenuItem.create] + OpenStruct.new(id: id, deadline: Time.zone.now, + shipping_fee: 9.99, items: items) + end + + def self.retrieve(id) + items = [Fake::MenuItem.create] + OpenStruct.new(id: id, deadline: Time.zone.now, + shipping_fee: 9.99, items: items) + end + end + + class MenuItemSerializer + def initialize(menu_item) + OpenStruct.new(id: menu_item.id, sku: menu_item.sku, + name: menu_item.name) + 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, + delivery_instructions: nil) + OpenStruct.new(name: name, street1: street1, street2: street2, + city: city, state: state, zip: zip, zip4: zip4, + email: email, phone: phone, + delivery_instructions: delivery_instructions) + end + end + + class Order + def initialize(*) + self + end + + def id + @id ||= SecureRandom.uuid + end + + def create! + items = [Fake::MenuItem.create] + ship_to = Fake::Recipient.new + OpenStruct.new(id: SecureRandom.uuid, items: items, + ship_to: ship_to) + end + + def cancel! + OpenStruct.new(id: SecureRandom.uuid, + status: "CANCELLED") + end + end + + class OrderItem + def self.new(sku:, quantity:, protein_sku:) + OpenStruct.new(sku: sku, quantity: quantity, protein_sku: protein_sku) + end + end + + def self.client + Fake::Client.new + end + + class ApiClient + def self.new(*) + Fake::Client.new + end + end + + class ApiClientSerializer + def self.serialize(*) + OpenStruct.new(id: nil, secret: nil).to_h + end + end + + class Client + def initialize(*) + self + end + + def authenticated? + false + end + + def authenticate!; end + end + + class OrderNotCreatedError < StandardError; end + + class OrderNotCancelledError < StandardError; end + + class AuthenticationError < StandardError; end + end + end + end +end diff --git a/lib/bls/core/testing/fake/callback.rb b/lib/bls/core/testing/fake/callback.rb new file mode 100644 index 0000000..3b6361b --- /dev/null +++ b/lib/bls/core/testing/fake/callback.rb @@ -0,0 +1,114 @@ +module Bls + module Core + module Testing + class Fake::Callback + def self.shipment_created(order_id: SecureRandom.uuid, + shipment_id: SecureRandom.uuid, + delivery_date: Date.today, + carrier: "DEFAULT_CARRIER", + tracking_url: "https://domain.com/track/1234", + tracking_code: SecureRandom.uuid) + { + id: shipment_id, + order_id: order_id, + reference_id: SecureRandom.uuid, + event_type: "SHIPMENT_CREATED", + shipment_id: shipment_id, + data: { + id: shipment_id, + menu_id: delivery_date.to_date.beginning_of_week.to_s, + order_id: order_id, + reference_id: SecureRandom.uuid, + ship_to: { + name: "Full Name", + company: "Company Name", + street1: "123 Main Street", + street2: "", + city: "Gotham", + state: "NY", + zip: "12345", + zip4: "", + email: "example@domain.com", + phone: "5555555555", + delivery_instructions: "Default delivery instructions", + }, + delivery_date: delivery_date, + items: [ + { + id: "d89397e1bad49c3b855df4406e5bf0d", + sku: "MP0001", + protein_id: "cca8eedc9842ff9a80e06242fe5a68", + protein_sku: "MP0001", + quantity: 1, + }, + ], + status: "CREATED", + delivery_status: "CREATED", + carrier: carrier, + shipment_number: 1, + total_shipments: 1, + tracking_code: tracking_code, + tracking_url: tracking_url, + delivery_status_detail: "arrived_at_destination", + delivery_status_message: "Left on front porch", + est_delivery_date: delivery_date.to_s, + }, + } + end + + def self.shipment_cancelled(order_id: SecureRandom.uuid, + shipment_id: SecureRandom.uuid, + delivery_date: Date.today, + carrier: "DEFAULT_CARRIER", + tracking_url: "https://domain.com/track/1234", + tracking_code: SecureRandom.uuid) + { + id: shipment_id, + order_id: order_id, + reference_id: SecureRandom.uuid, + event_type: "SHIPMENT_CANCELLED", + shipment_id: shipment_id, + data: { + id: shipment_id, + menu_id: delivery_date.to_date.beginning_of_week.to_s, + order_id: order_id, + reference_id: SecureRandom.uuid, + ship_to: { + name: "Full Name", + company: "Company Name", + street1: "123 Main Street", + street2: "", + city: "Gotham", + state: "NY", + zip: "12345", + zip4: "", + email: "example@domain.com", + phone: "5555555555", + delivery_instructions: "Default delivery instructions", + }, + delivery_date: delivery_date, + items: [ + { + id: "d89397e1bad49c3b855df4406e5bf0d", + sku: "MP0001", + protein_id: "cca8eedc9842ff9a80e06242fe5a68", + protein_sku: "MP0001", + quantity: 1, + }, + ], + status: "CANCELLED", + delivery_status: "PRE_TRANSIT", + carrier: carrier, + shipment_number: 1, + total_shipments: 1, + tracking_code: tracking_code, + tracking_url: tracking_url, + delivery_status_detail: "STATUS_UPDATE", + delivery_status_message: "UPDATE_INFO", + }, + } + end + end + end + end +end diff --git a/lib/bls/core/version.rb b/lib/bls/core/version.rb new file mode 100644 index 0000000..deae573 --- /dev/null +++ b/lib/bls/core/version.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module Bls + module Core + VERSION = "0.0.0" + end +end diff --git a/lib/intelligent_foods.rb b/lib/intelligent_foods.rb deleted file mode 100644 index a2a9d78..0000000 --- a/lib/intelligent_foods.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -require "base64" -require "json" -require "net/http" -require "ostruct" - -require "intelligent_foods/api_client" -require "intelligent_foods/api/v1" -require "intelligent_foods/api/v1/authenticator" -require "intelligent_foods/api_operations/retrieve" -require "intelligent_foods/authorization/base" -require "intelligent_foods/authorization/basic" -require "intelligent_foods/authorization/blank" -require "intelligent_foods/resources/api_error" -require "intelligent_foods/authorization/bearer" -require "intelligent_foods/resources/object" -require "intelligent_foods/resources/inventory_level" -require "intelligent_foods/resources/order" -require "intelligent_foods/resources/order_item" -require "intelligent_foods/serializers/order_item_serializer" -require "intelligent_foods/resources/menu" -require "intelligent_foods/resources/menu_item" -require "intelligent_foods/serializers/menu_item_serializer" -require "intelligent_foods/resources/recipient" -require "intelligent_foods/serializers/recipient_serializer" -require "intelligent_foods/version" -require "intelligent_foods/errors" - -module IntelligentFoods - class Error < StandardError; end - - class << self - attr_accessor :client_id, :client_secret, :environment - - def configure - yield self - end - end -end diff --git a/lib/intelligent_foods/api/v1.rb b/lib/intelligent_foods/api/v1.rb deleted file mode 100644 index 095fe2e..0000000 --- a/lib/intelligent_foods/api/v1.rb +++ /dev/null @@ -1,68 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - class V1 - attr_accessor :authentication - attr_reader :environment, :username, :password - - PRODUCTION = "production" - STAGING = "staging" - - def initialize(username: nil, password: nil, authentication: nil, - environment: STAGING) - @authentication = authentication - @environment = environment - @username = username - @password = password - end - - def base_url - "https://api.sunbasket.#{tld}/partner/v1" - end - - def authenticate! - return if authenticated? - - authenticator = V1::Authenticator.build(self) - @authentication = authenticator.save! - end - - def client - build_api_client! - end - - def authenticated? - authentication.present? - end - - def reset_authentication - @authentication = nil - self - end - - def self.build(config: IntelligentFoods) - new(environment: config.environment, - username: config.client_id, - password: config.client_secret) - end - - protected - - def build_api_client! - authenticate! - IntelligentFoods::ApiClient.build(self) - end - - def tld - @tld ||= determine_tld - end - - def determine_tld - if environment == PRODUCTION - "com" - else - "dev" - end - end - end -end diff --git a/lib/intelligent_foods/api/v1/authenticator.rb b/lib/intelligent_foods/api/v1/authenticator.rb deleted file mode 100644 index d7e6bd1..0000000 --- a/lib/intelligent_foods/api/v1/authenticator.rb +++ /dev/null @@ -1,52 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - class V1::Authenticator - attr_accessor :base_url, :client_id, :client_secret - - def initialize(base_url:, client_id:, client_secret:) - @base_url = base_url - @client_id = client_id - @client_secret = client_secret - end - - def save! - path = "#{base_url}/token" - body = { client_id: client_id, client_secret: client_secret } - auth = Authorization::Basic. - factory(client_id: client_id, client_secret: client_secret) - client = IntelligentFoods::ApiClient.new(authentication: auth) - response = client.post(path: path, body: body) - handle_authentication_response!(response: response.data) - end - - def self.build(api) - new(base_url: api.base_url, - client_id: api.username, - client_secret: api.password) - end - - protected - - def handle_authentication_response!(response:) - if response_has_errors?(response) - handle_errors! - else - handle_successful_authentication_response(response) - end - end - - def response_has_errors?(response) - response.has_key?(:error) - end - - def handle_successful_authentication_response(response) - access_token = response[:access_token] - IntelligentFoods::Authorization::Bearer.new(token: access_token) - end - - def handle_errors! - raise AuthenticationError.new(status: 401, title: "Authentication Failed") - end - end -end diff --git a/lib/intelligent_foods/api_client.rb b/lib/intelligent_foods/api_client.rb deleted file mode 100644 index d18b094..0000000 --- a/lib/intelligent_foods/api_client.rb +++ /dev/null @@ -1,89 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - class ApiClient - attr_reader :api, :authentication - - def initialize(api: nil, authentication: nil) - @api = api - @authentication = authentication || Authorization::Blank.new - end - - def post(path:, body:) - uri = URI(path) - request = build_request_with_body(uri: uri, body: body) - execute_request(request: request, uri: request.uri) - end - - def delete(path:, **) - uri = URI(path) - request = Net::HTTP::Delete.new(uri) - execute_request(request: request, uri: request.uri) - end - - def get(path:, **) - uri = URI(path) - request = Net::HTTP::Get.new(uri) - execute_request(request: request, uri: request.uri) - end - - def execute_request(request:, uri:) - Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http| - request["Authorization"] = authentication.header - response = http.request(request) - handle_response(response: response) - end - end - - def self.build(api) - new(api: api, authentication: api.authentication) - end - - protected - - def build_request_with_body(uri:, body:) - request = Net::HTTP::Post.new(uri) - request.body = body.to_json - request["content-type"] = "application/json" - request - end - - def handle_response(response:) - if authentication_failed?(response.code) - handle_authentication_error! - else - body = parse_response_body(response) - OpenStruct.new(data: body, success?: request_successful?(response.code)) - end - end - - def handle_authentication_error! - api.reset_authentication - raise AuthenticationError.new(status: 401, - title: "Authentication Failed") - end - - def parse_response_body(response) - return {} if empty_response?(response.code) - return {} if redirection?(response.code) - - JSON.parse(response.body, symbolize_names: true) - end - - def empty_response?(response_code) - response_code.to_i == 204 - end - - def redirection?(response_code) - response_code.to_i.between?(300, 399) - end - - def request_successful?(response_code) - response_code.to_i.between?(200, 299) - end - - def authentication_failed?(response_code) - response_code.to_i == 401 - end - end -end diff --git a/lib/intelligent_foods/api_operations/retrieve.rb b/lib/intelligent_foods/api_operations/retrieve.rb deleted file mode 100644 index 0a8e7e3..0000000 --- a/lib/intelligent_foods/api_operations/retrieve.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - module ApiOperations - module Retrieve - def retrieve(id) - resource = new(id: id) - api_client = resource.client - response = api_client.get(path: resource.resource_path) - if response.success? - build_from_response(response.data) - else - raise ResourceRetrievalError.build(response) - end - end - - def retrieve_all - resource = new - api_client = resource.client - response = api_client.get(path: resource.resources_path) - if response.success? - response.data.map { |id| new(id: id) } - else - raise ResourceRetrievalError.build(response) - end - end - end - end -end diff --git a/lib/intelligent_foods/authorization/base.rb b/lib/intelligent_foods/authorization/base.rb deleted file mode 100644 index 6d45ba3..0000000 --- a/lib/intelligent_foods/authorization/base.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - module Authorization - class Base - attr_reader :token - - def initialize(token: nil) - @token = token - end - end - end -end diff --git a/lib/intelligent_foods/authorization/basic.rb b/lib/intelligent_foods/authorization/basic.rb deleted file mode 100644 index 443618d..0000000 --- a/lib/intelligent_foods/authorization/basic.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - module Authorization - class Basic < Base - def header - "Basic #{token}" - end - - def self.factory(client_id:, client_secret:) - encoded_token = Base64.strict_encode64("#{client_id}:#{client_secret}") - new(token: encoded_token) - end - end - end -end diff --git a/lib/intelligent_foods/authorization/bearer.rb b/lib/intelligent_foods/authorization/bearer.rb deleted file mode 100644 index 034ed3d..0000000 --- a/lib/intelligent_foods/authorization/bearer.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - module Authorization - class Bearer < Base - def header - "Bearer #{token}" - end - end - end -end diff --git a/lib/intelligent_foods/authorization/blank.rb b/lib/intelligent_foods/authorization/blank.rb deleted file mode 100644 index 65d1ce7..0000000 --- a/lib/intelligent_foods/authorization/blank.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - module Authorization - class Blank < Base - def header; end - end - end -end diff --git a/lib/intelligent_foods/errors.rb b/lib/intelligent_foods/errors.rb deleted file mode 100644 index 3e9f8ac..0000000 --- a/lib/intelligent_foods/errors.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - class ResourceRetrievalError < ApiError; end - - class OrderNotCancelledError < ApiError; end - - class OrderNotCreatedError < ApiError; end - - class AuthenticationError < ApiError; end -end diff --git a/lib/intelligent_foods/resources/api_error.rb b/lib/intelligent_foods/resources/api_error.rb deleted file mode 100644 index a45f679..0000000 --- a/lib/intelligent_foods/resources/api_error.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - class ApiError < StandardError - attr_reader :status, :title, :details - - def initialize(status:, title:, details: nil) - @status = status - @title = title - @details = details - super - end - - def message - if details.blank? - "#{status} - #{title}" - else - "#{status} #{title} - #{description}" - end - end - - def self.build(response) - data = response.data - data => { status:, title:, details: } - new(status: status, title: title, details: details) - end - - protected - - def description - details[:description] - end - end -end diff --git a/lib/intelligent_foods/resources/inventory_level.rb b/lib/intelligent_foods/resources/inventory_level.rb deleted file mode 100644 index f95b867..0000000 --- a/lib/intelligent_foods/resources/inventory_level.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - class InventoryLevel < IntelligentFoods::Object - def object_name - "inventory-level" - end - - def resources_path - "#{api.base_url}/products/inventory-levels" - end - - def self.retrieve_all - resource = new - api_client = resource.client - response = api_client.get(path: resource.resources_path) - if response.success? - response.data[:products].map { |product| build(product) } - else - raise ResourceRetrievalError.build(response) - end - end - end -end diff --git a/lib/intelligent_foods/resources/menu.rb b/lib/intelligent_foods/resources/menu.rb deleted file mode 100644 index 8f7657f..0000000 --- a/lib/intelligent_foods/resources/menu.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - class Menu < IntelligentFoods::Object - def initialize(args = {}) - super - end - - def object_name - "menu" - end - - def resources_path - "#{api.base_url}/menus" - end - - def self.build_from_response(data) - menu = build(data) - menu.items = MenuItem.build(data[:items]) - menu - end - end -end diff --git a/lib/intelligent_foods/resources/menu_item.rb b/lib/intelligent_foods/resources/menu_item.rb deleted file mode 100644 index 1a885fd..0000000 --- a/lib/intelligent_foods/resources/menu_item.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - class MenuItem < IntelligentFoods::Object - def initialize(args = {}) - super - end - end -end diff --git a/lib/intelligent_foods/resources/object.rb b/lib/intelligent_foods/resources/object.rb deleted file mode 100644 index 653944d..0000000 --- a/lib/intelligent_foods/resources/object.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - class Object < OpenStruct - extend IntelligentFoods::ApiOperations::Retrieve - - def self.build(data) - JSON.parse(data.to_json, object_class: self) - end - - def resources_path - "#{api.base_url}/#{object_name.downcase}" - end - - def resource_path - "#{api.base_url}/#{object_name.downcase}/#{id}" - end - - def client - api.client - end - - def api - @api ||= IntelligentFoods::V1.build - end - end -end diff --git a/lib/intelligent_foods/resources/order.rb b/lib/intelligent_foods/resources/order.rb deleted file mode 100644 index 25c837b..0000000 --- a/lib/intelligent_foods/resources/order.rb +++ /dev/null @@ -1,114 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - class Order < IntelligentFoods::Object - ACCEPTED = "accepted" - CANCELLED = "cancelled" - ERROR = "error" - IN_PROCESS = "in_process" - INITIALIZED = "initialized" - PROCESSED = "processed" - - attr_reader :skip_temperature_check, :skip_address_check - - def initialize(skip_temperature_check: false, skip_address_check: false, **) - super - @skip_temperature_check = skip_temperature_check - @skip_address_check = skip_address_check - end - - def self.build_from_response(data) - order = build(data) - order[:items] = OrderItem.build(data[:items]) - order[:ship_to] = Recipient.build(data[:ship_to]) - order - end - - def object_name - "order" - end - - def create! - response = client.post(path: resources_path, body: request_body) - if response.success? - Order::build_from_response(response.data) - else - handle_order_not_created(response) - end - end - - def cancel! - response = client.delete(path: resource_path) - if response.success? - mark_as_cancelled - self - else - handle_order_not_cancelled(response) - end - end - - def request_body - @request_body ||= { - menu_id: menu.id, - reference_id: external_id.to_s, - ship_to: ship_to, - delivery_date: delivery_date, - items: items_json, - validation_options: validation_options, - callback_url: callback_url, - callback_headers: callback_headers, - } - end - - def cancelled? - status.downcase == CANCELLED - end - - def valid? - status.downcase != ERROR - end - - def accepted? - status.downcase == ACCEPTED - end - - protected - - def handle_order_not_created(response) - mark_as_invalid - raise OrderNotCreatedError.build(response) - end - - def handle_order_not_cancelled(response) - mark_as_invalid - raise OrderNotCancelledError.build(response) - end - - def mark_as_cancelled - self[:status] = CANCELLED - end - - def mark_as_invalid - self[:status] = ERROR - end - - def items_json - return if items.nil? - - items.map do |item| - OrderItemSerializer.new(item).to_json - end - end - - def ship_to - RecipientSerializer.new(recipient).to_json - end - - def validation_options - { - skip_temperature_check: skip_temperature_check, - skip_address_check: skip_address_check, - } - end - end -end diff --git a/lib/intelligent_foods/resources/order_item.rb b/lib/intelligent_foods/resources/order_item.rb deleted file mode 100644 index f75de9c..0000000 --- a/lib/intelligent_foods/resources/order_item.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - class OrderItem < IntelligentFoods::Object - def initialize(args = {}) - super - end - end -end diff --git a/lib/intelligent_foods/resources/recipient.rb b/lib/intelligent_foods/resources/recipient.rb deleted file mode 100644 index e3d3a46..0000000 --- a/lib/intelligent_foods/resources/recipient.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - class Recipient < IntelligentFoods::Object - def initialize(args = {}) - super - end - end -end - diff --git a/lib/intelligent_foods/rspec.rb b/lib/intelligent_foods/rspec.rb deleted file mode 100644 index b95b28e..0000000 --- a/lib/intelligent_foods/rspec.rb +++ /dev/null @@ -1,9 +0,0 @@ -require "rspec/rails" -require "intelligent_foods/testing/fake" -require "intelligent_foods/testing/fake/callback" - -RSpec.configure do |config| - config.before(:each) do - stub_const("IntelligentFoods", IntelligentFoods::Testing::Fake) - end -end diff --git a/lib/intelligent_foods/serializers/menu_item_serializer.rb b/lib/intelligent_foods/serializers/menu_item_serializer.rb deleted file mode 100644 index f31c06b..0000000 --- a/lib/intelligent_foods/serializers/menu_item_serializer.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - class MenuItemSerializer < SimpleDelegator - def to_json - { - id: id, - sku: sku, - name: name, - } - end - end -end diff --git a/lib/intelligent_foods/serializers/order_item_serializer.rb b/lib/intelligent_foods/serializers/order_item_serializer.rb deleted file mode 100644 index f77dee8..0000000 --- a/lib/intelligent_foods/serializers/order_item_serializer.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - class OrderItemSerializer < SimpleDelegator - def to_json - { - sku: sku, - protein_sku: protein_sku, - quantity: quantity, - } - end - end -end diff --git a/lib/intelligent_foods/serializers/recipient_serializer.rb b/lib/intelligent_foods/serializers/recipient_serializer.rb deleted file mode 100644 index baa5e15..0000000 --- a/lib/intelligent_foods/serializers/recipient_serializer.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - class RecipientSerializer < SimpleDelegator - def to_json - recipient_object = { - name: name, - street1: street1, - street2: street2, - city: city, - state: state, - zip: zip, - zip4: zip4, - email: email, - phone: phone, - delivery_instructions: delivery_instructions, - } - remove_blank_values(recipient_object) - end - - protected - - def remove_blank_values(obj) - obj.delete_if do |_k, v| - v.blank? - end - end - end -end diff --git a/lib/intelligent_foods/testing/fake.rb b/lib/intelligent_foods/testing/fake.rb deleted file mode 100644 index 64dcda8..0000000 --- a/lib/intelligent_foods/testing/fake.rb +++ /dev/null @@ -1,127 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - module Testing - class Fake - def self.configure(*); end - - class DistributionCenterInventory - def self.create(name: "WEST_COAST", quantity: 100) - OpenStruct.new(distribution_center: name, quantity: quantity) - end - end - - class InventoryLevel - def self.create(sku: "SKU1", date: Date.today.to_s, - dc_inventory: [DistributionCenterInventory.create]) - inventory_level = OpenStruct.new( - date: date, - inventory_by_distribution_center: dc_inventory - ) - OpenStruct.new(sku: sku, inventory_levels: [inventory_level]) - end - end - - class MenuItem - def self.create(id: SecureRandom.uuid, sku: "SKU1", - name: "FakeMenuItem") - OpenStruct.new(id: id, sku: sku, name: name) - end - end - - class Menu - def self.new(id: Date.today.to_s) - items = [Fake::MenuItem.create] - OpenStruct.new(id: id, deadline: Time.zone.now, - shipping_fee: 9.99, items: items) - end - - def self.retrieve(id) - items = [Fake::MenuItem.create] - OpenStruct.new(id: id, deadline: Time.zone.now, - shipping_fee: 9.99, items: items) - end - end - - class MenuItemSerializer - def initialize(menu_item) - OpenStruct.new(id: menu_item.id, sku: menu_item.sku, - name: menu_item.name) - 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, - delivery_instructions: nil) - OpenStruct.new(name: name, street1: street1, street2: street2, - city: city, state: state, zip: zip, zip4: zip4, - email: email, phone: phone, - delivery_instructions: delivery_instructions) - end - end - - class Order - def initialize(*) - self - end - - def id - @id ||= SecureRandom.uuid - end - - def create! - items = [Fake::MenuItem.create] - ship_to = Fake::Recipient.new - OpenStruct.new(id: SecureRandom.uuid, items: items, - ship_to: ship_to) - end - - def cancel! - OpenStruct.new(id: SecureRandom.uuid, - status: "CANCELLED") - end - end - - class OrderItem - def self.new(sku:, quantity:, protein_sku:) - OpenStruct.new(sku: sku, quantity: quantity, protein_sku: protein_sku) - end - end - - def self.client - Fake::Client.new - end - - class ApiClient - def self.new(*) - Fake::Client.new - end - end - - class ApiClientSerializer - def self.serialize(*) - OpenStruct.new(id: nil, secret: nil).to_h - end - end - - class Client - def initialize(*) - self - end - - def authenticated? - false - end - - def authenticate!; end - end - - class OrderNotCreatedError < StandardError; end - - class OrderNotCancelledError < StandardError; end - - class AuthenticationError < StandardError; end - end - end -end diff --git a/lib/intelligent_foods/testing/fake/callback.rb b/lib/intelligent_foods/testing/fake/callback.rb deleted file mode 100644 index 0beafd1..0000000 --- a/lib/intelligent_foods/testing/fake/callback.rb +++ /dev/null @@ -1,112 +0,0 @@ -module IntelligentFoods - module Testing - class Fake::Callback - def self.shipment_created(order_id: SecureRandom.uuid, - shipment_id: SecureRandom.uuid, - delivery_date: Date.today, - carrier: "DEFAULT_CARRIER", - tracking_url: "https://domain.com/track/1234", - tracking_code: SecureRandom.uuid) - { - id: shipment_id, - order_id: order_id, - reference_id: SecureRandom.uuid, - event_type: "SHIPMENT_CREATED", - shipment_id: shipment_id, - data: { - id: shipment_id, - menu_id: delivery_date.to_date.beginning_of_week.to_s, - order_id: order_id, - reference_id: SecureRandom.uuid, - ship_to: { - name: "Full Name", - company: "Company Name", - street1: "123 Main Street", - street2: "", - city: "Gotham", - state: "NY", - zip: "12345", - zip4: "", - email: "example@domain.com", - phone: "5555555555", - delivery_instructions: "Default delivery instructions", - }, - delivery_date: delivery_date, - items: [ - { - id: "d89397e1bad49c3b855df4406e5bf0d", - sku: "MP0001", - protein_id: "cca8eedc9842ff9a80e06242fe5a68", - protein_sku: "MP0001", - quantity: 1, - }, - ], - status: "CREATED", - delivery_status: "CREATED", - carrier: carrier, - shipment_number: 1, - total_shipments: 1, - tracking_code: tracking_code, - tracking_url: tracking_url, - delivery_status_detail: "arrived_at_destination", - delivery_status_message: "Left on front porch", - est_delivery_date: delivery_date.to_s, - }, - } - end - - def self.shipment_cancelled(order_id: SecureRandom.uuid, - shipment_id: SecureRandom.uuid, - delivery_date: Date.today, - carrier: "DEFAULT_CARRIER", - tracking_url: "https://domain.com/track/1234", - tracking_code: SecureRandom.uuid) - { - id: shipment_id, - order_id: order_id, - reference_id: SecureRandom.uuid, - event_type: "SHIPMENT_CANCELLED", - shipment_id: shipment_id, - data: { - id: shipment_id, - menu_id: delivery_date.to_date.beginning_of_week.to_s, - order_id: order_id, - reference_id: SecureRandom.uuid, - ship_to: { - name: "Full Name", - company: "Company Name", - street1: "123 Main Street", - street2: "", - city: "Gotham", - state: "NY", - zip: "12345", - zip4: "", - email: "example@domain.com", - phone: "5555555555", - delivery_instructions: "Default delivery instructions", - }, - delivery_date: delivery_date, - items: [ - { - id: "d89397e1bad49c3b855df4406e5bf0d", - sku: "MP0001", - protein_id: "cca8eedc9842ff9a80e06242fe5a68", - protein_sku: "MP0001", - quantity: 1, - }, - ], - status: "CANCELLED", - delivery_status: "PRE_TRANSIT", - carrier: carrier, - shipment_number: 1, - total_shipments: 1, - tracking_code: tracking_code, - tracking_url: tracking_url, - delivery_status_detail: "STATUS_UPDATE", - delivery_status_message: "UPDATE_INFO", - }, - } - end - end - end -end diff --git a/lib/intelligent_foods/version.rb b/lib/intelligent_foods/version.rb deleted file mode 100644 index aa55aaa..0000000 --- a/lib/intelligent_foods/version.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -module IntelligentFoods - VERSION = "0.0.0" -end diff --git a/sig/bls-core.rbs b/sig/bls-core.rbs new file mode 100644 index 0000000..7e431a4 --- /dev/null +++ b/sig/bls-core.rbs @@ -0,0 +1,5 @@ +module Bls + module Core + VERSION: String + end +end diff --git a/sig/intelligent_foods.rbs b/sig/intelligent_foods.rbs deleted file mode 100644 index c7207bb..0000000 --- a/sig/intelligent_foods.rbs +++ /dev/null @@ -1,3 +0,0 @@ -module IntelligentFoods - VERSION: String -end diff --git a/spec/intelligent_foods/api/v1/authenticator_spec.rb b/spec/core/api/v1/authenticator_spec.rb similarity index 69% rename from spec/intelligent_foods/api/v1/authenticator_spec.rb rename to spec/core/api/v1/authenticator_spec.rb index 9356d11..ace0f4d 100644 --- a/spec/intelligent_foods/api/v1/authenticator_spec.rb +++ b/spec/core/api/v1/authenticator_spec.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true -RSpec.describe IntelligentFoods::V1::Authenticator do +RSpec.describe Bls::Core::V1::Authenticator do describe "#save!" do it "returns a authorization scheme" do - auth = IntelligentFoods::V1::Authenticator. + auth = Bls::Core::V1::Authenticator. new(base_url: "http://test.com", client_id: "id123", client_secret: "secret12") stub_authentication @@ -15,7 +15,7 @@ context "when authentication fails" do it "raises a AuthenticationError" do - auth = IntelligentFoods::V1::Authenticator. + auth = Bls::Core::V1::Authenticator. new(base_url: "http://test.com", client_id: "id123", client_secret: "secret12") response_body = { error: "Authentication Failed" } @@ -24,32 +24,32 @@ expect { auth.save! - }.to raise_error(IntelligentFoods::AuthenticationError) + }.to raise_error(Bls::Core::AuthenticationError) end end end describe ".build" do it "sets the client_id" do - api = IntelligentFoods::V1.new(username: "id123") + api = Bls::Core::V1.new(username: "id123") - result = IntelligentFoods::V1::Authenticator.build(api) + result = Bls::Core::V1::Authenticator.build(api) expect(result.client_id).to eq("id123") end it "sets the client_secret" do - api = IntelligentFoods::V1.new(password: "secret123") + api = Bls::Core::V1.new(password: "secret123") - result = IntelligentFoods::V1::Authenticator.build(api) + result = Bls::Core::V1::Authenticator.build(api) expect(result.client_secret).to eq("secret123") end it "sets the base_url" do - api = IntelligentFoods::V1.new + api = Bls::Core::V1.new - result = IntelligentFoods::V1::Authenticator.build(api) + result = Bls::Core::V1::Authenticator.build(api) expect(result.base_url).to eq(api.base_url) end diff --git a/spec/intelligent_foods/api/v1_spec.rb b/spec/core/api/v1_spec.rb similarity index 68% rename from spec/intelligent_foods/api/v1_spec.rb rename to spec/core/api/v1_spec.rb index dbbc41a..6afb42a 100644 --- a/spec/intelligent_foods/api/v1_spec.rb +++ b/spec/core/api/v1_spec.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true -RSpec.describe IntelligentFoods::V1 do +RSpec.describe Bls::Core::V1 do describe "#base_url" do context "when the environment is production" do it "returns the correct url" do - api = IntelligentFoods::V1.new(environment: "production") + api = Bls::Core::V1.new(environment: "production") result = api.base_url @@ -14,7 +14,7 @@ context "when the environment is not production" do it "returns the correct url" do - api = IntelligentFoods::V1.new(environment: "staging") + api = Bls::Core::V1.new(environment: "staging") result = api.base_url @@ -25,7 +25,7 @@ describe "#client" do it "returns a api client" do - api = IntelligentFoods::V1.new + api = Bls::Core::V1.new build_stubbed_authenticator(api) result = api.client @@ -36,16 +36,16 @@ describe "#authenticate" do it "builds a authenticator" do - api = IntelligentFoods::V1.new + api = Bls::Core::V1.new build_stubbed_authenticator(api) api.authenticate! - expect(IntelligentFoods::V1::Authenticator).to have_received(:build).once + expect(Bls::Core::V1::Authenticator).to have_received(:build).once end it "authenticates" do - api = IntelligentFoods::V1.new + api = Bls::Core::V1.new authenticator = build_stubbed_authenticator(api) api.authenticate! @@ -55,7 +55,7 @@ context "when the authentication is present" do it "is authenticated" do - api = IntelligentFoods::V1.new + api = Bls::Core::V1.new build_stubbed_authenticator(api) api.authenticate! @@ -67,8 +67,8 @@ describe "reset authentication" do it "removes the present authentication" do - auth = IntelligentFoods::Authorization::Bearer.new(token: "1234") - api = IntelligentFoods::V1.new(authentication: auth) + auth = Bls::Core::Authorization::Bearer.new(token: "1234") + api = Bls::Core::V1.new(authentication: auth) result = api.reset_authentication @@ -76,8 +76,8 @@ end it "is not authenticated" do - auth = IntelligentFoods::Authorization::Bearer.new(token: "1234") - api = IntelligentFoods::V1.new(authentication: auth) + auth = Bls::Core::Authorization::Bearer.new(token: "1234") + api = Bls::Core::V1.new(authentication: auth) result = api.reset_authentication @@ -89,7 +89,7 @@ it "sets the environment" do config = build_config(environment: "preview") - result = IntelligentFoods::V1.build(config: config) + result = Bls::Core::V1.build(config: config) expect(result.environment).to eq("preview") end @@ -97,7 +97,7 @@ it "sets the username" do config = build_config(client_id: "secretUsername") - result = IntelligentFoods::V1.build(config: config) + result = Bls::Core::V1.build(config: config) expect(result.username).to eq("secretUsername") end @@ -105,7 +105,7 @@ it "sets the password" do config = build_config(client_secret: "secretPassword") - result = IntelligentFoods::V1.build(config: config) + result = Bls::Core::V1.build(config: config) expect(result.password).to eq("secretPassword") end @@ -118,10 +118,10 @@ def build_config(environment: "staging", client_id: "cid_123", end def build_stubbed_authenticator(api) - authenticator = IntelligentFoods::V1::Authenticator.build(api) - authentication = IntelligentFoods::Authorization::Bearer.new(token: "123") - client = IntelligentFoods::ApiClient.new(authentication: authentication) - allow(IntelligentFoods::V1::Authenticator).to receive(:build). + authenticator = Bls::Core::V1::Authenticator.build(api) + authentication = Bls::Core::Authorization::Bearer.new(token: "123") + client = Bls::Core::ApiClient.new(authentication: authentication) + allow(Bls::Core::V1::Authenticator).to receive(:build). and_return(authenticator) allow(authenticator).to receive(:save!).and_return(client) authenticator diff --git a/spec/intelligent_foods/api_client_spec.rb b/spec/core/api_client_spec.rb similarity index 76% rename from spec/intelligent_foods/api_client_spec.rb rename to spec/core/api_client_spec.rb index fdcf0ec..5a4c754 100644 --- a/spec/intelligent_foods/api_client_spec.rb +++ b/spec/core/api_client_spec.rb @@ -1,21 +1,21 @@ # frozen_string_literal: true -RSpec.describe IntelligentFoods::ApiClient do +RSpec.describe Bls::Core::ApiClient do describe ".build" do it "sets the api" do - api = IntelligentFoods::V1.new + api = Bls::Core::V1.new - result = IntelligentFoods::ApiClient.build(api) + result = Bls::Core::ApiClient.build(api) expect(result.api).to eq(api) end it "sets the authentication" do - api = IntelligentFoods::V1.new - auth = IntelligentFoods::Authorization::Bearer.new(token: "1234") + api = Bls::Core::V1.new + auth = Bls::Core::Authorization::Bearer.new(token: "1234") allow(api).to receive(:authentication).and_return(auth) - result = IntelligentFoods::ApiClient.build(api) + result = Bls::Core::ApiClient.build(api) expect(result.authentication).to eq(auth) end @@ -25,11 +25,11 @@ it "sets the authorization bearer header" do stub_api_response request = build_stubbed_post - api = IntelligentFoods::V1.new - auth = IntelligentFoods::Authorization::Bearer.new(token: "1234") - client = IntelligentFoods::ApiClient.new(api: api, + api = Bls::Core::V1.new + auth = Bls::Core::Authorization::Bearer.new(token: "1234") + client = Bls::Core::ApiClient.new(api: api, authentication: auth) - allow(IntelligentFoods::Authorization::Bearer).to receive(:new). + allow(Bls::Core::Authorization::Bearer).to receive(:new). and_return(auth) header = "Bearer 1234" @@ -43,7 +43,7 @@ http_client = double allow(http_client).to receive(:request) stub_api_response http: http_client - client = IntelligentFoods::ApiClient.new + client = Bls::Core::ApiClient.new client.execute_request(request: request, uri: request.uri) @@ -55,7 +55,7 @@ http_client = double response = OpenStruct.new(code: 200) stub_api_response response: response, http: http_client - client = IntelligentFoods::ApiClient.new + client = Bls::Core::ApiClient.new allow(JSON).to receive(:parse) client.execute_request(request: request, uri: request.uri) @@ -69,7 +69,7 @@ http_client = double response = OpenStruct.new(code: 204) stub_api_response response: response, http: http_client - client = IntelligentFoods::ApiClient.new + client = Bls::Core::ApiClient.new allow(JSON).to receive(:parse) client.execute_request(request: request, uri: request.uri) @@ -84,7 +84,7 @@ http_client = double response = OpenStruct.new(code: 301) stub_api_response response: response, http: http_client - client = IntelligentFoods::ApiClient.new + client = Bls::Core::ApiClient.new allow(JSON).to receive(:parse) client.execute_request(request: request, uri: request.uri) @@ -94,17 +94,17 @@ end context "the response code is 401" do - it "raises a IntelligentFoods::AuthenticationError" do + it "raises a Bls::Core::AuthenticationError" do request = build_stubbed_post http_client = double response = OpenStruct.new(code: 401) stub_api_response response: response, http: http_client - api = IntelligentFoods::V1.new - client = IntelligentFoods::ApiClient.new(api: api) + api = Bls::Core::V1.new + client = Bls::Core::ApiClient.new(api: api) expect { client.execute_request(request: request, uri: request.uri) - }.to raise_error(IntelligentFoods::AuthenticationError) + }.to raise_error(Bls::Core::AuthenticationError) end it "resets the apis authentication" do @@ -112,13 +112,13 @@ http_client = double response = OpenStruct.new(code: 401) stub_api_response response: response, http: http_client - api = IntelligentFoods::V1.new - client = IntelligentFoods::ApiClient.new(api: api) + api = Bls::Core::V1.new + client = Bls::Core::ApiClient.new(api: api) allow(api).to receive(:reset_authentication) begin client.execute_request(request: request, uri: request.uri) - rescue IntelligentFoods::AuthenticationError + rescue Bls::Core::AuthenticationError end expect(api).to have_received(:reset_authentication) @@ -130,7 +130,7 @@ it "performs a post request" do stub_api_response body = { test: "yes" } - client = IntelligentFoods::ApiClient.new + client = Bls::Core::ApiClient.new allow(Net::HTTP::Post).to receive(:new).and_call_original client.post(path: "http://test.com/orders", body: body) @@ -141,7 +141,7 @@ it "assigns a body to the request" do stub_api_response body = { test: "yes" } - client = IntelligentFoods::ApiClient.new + client = Bls::Core::ApiClient.new path = "http://test.com/orders" request = Net::HTTP::Post.new(URI(path)) allow(Net::HTTP::Post).to receive(:new).and_return(request) @@ -154,7 +154,7 @@ it "executes the request" do stub_api_response body = { test: "yes" } - client = IntelligentFoods::ApiClient.new + client = Bls::Core::ApiClient.new allow(client).to receive(:execute_request).and_call_original client.post(path: "http://test.com/orders", body: body) @@ -166,8 +166,8 @@ it "executes the request with authorization" do stub_api_response body = { test: "yes" } - auth = IntelligentFoods::Authorization::Bearer.new(token: "1234") - client = IntelligentFoods::ApiClient.new(authentication: auth) + auth = Bls::Core::Authorization::Bearer.new(token: "1234") + client = Bls::Core::ApiClient.new(authentication: auth) request = Net::HTTP::Post.new(URI("http://test.com/orders")) allow(Net::HTTP::Post).to receive(:new).and_return(request) @@ -181,7 +181,7 @@ describe "#delete" do it "performs a delete request" do stub_api_response - client = IntelligentFoods::ApiClient.new + client = Bls::Core::ApiClient.new allow(Net::HTTP::Delete).to receive(:new).and_call_original client.delete(path: "http://test.com/orders/1") @@ -192,8 +192,8 @@ context "when authorization is provided" do it "executes the request with authorization" do stub_api_response - auth = IntelligentFoods::Authorization::Bearer.new(token: "1234") - client = IntelligentFoods::ApiClient.new(authentication: auth) + auth = Bls::Core::Authorization::Bearer.new(token: "1234") + client = Bls::Core::ApiClient.new(authentication: auth) request = Net::HTTP::Delete.new(URI("http://test.com/orders/1")) allow(Net::HTTP::Delete).to receive(:new).and_return(request) @@ -207,7 +207,7 @@ describe "#get" do it "performs a get request" do stub_api_response - client = IntelligentFoods::ApiClient.new + client = Bls::Core::ApiClient.new allow(Net::HTTP::Get).to receive(:new).and_call_original client.get(path: "http://test.com/orders/1") @@ -218,8 +218,8 @@ context "when authorization is provided" do it "executes the request with authorization" do stub_api_response - auth = IntelligentFoods::Authorization::Bearer.new(token: "1234") - client = IntelligentFoods::ApiClient.new(authentication: auth) + auth = Bls::Core::Authorization::Bearer.new(token: "1234") + client = Bls::Core::ApiClient.new(authentication: auth) request = Net::HTTP::Get.new(URI("http://test.com/orders/1")) allow(Net::HTTP::Get).to receive(:new).and_return(request) diff --git a/spec/intelligent_foods/resources/inventory_level_spec.rb b/spec/core/resources/inventory_level_spec.rb similarity index 77% rename from spec/intelligent_foods/resources/inventory_level_spec.rb rename to spec/core/resources/inventory_level_spec.rb index 4be4a93..9bd56e5 100644 --- a/spec/intelligent_foods/resources/inventory_level_spec.rb +++ b/spec/core/resources/inventory_level_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe IntelligentFoods::InventoryLevel do +RSpec.describe Bls::Core::InventoryLevel do describe ".retrieve_all" do it "returns the product inventory levels" do body = build_inventory_levels_response @@ -9,7 +9,7 @@ stub_api_response response: response product_id = "MP0527" - result = IntelligentFoods::InventoryLevel.retrieve_all + result = Bls::Core::InventoryLevel.retrieve_all expect(result.map(&:product_code)).to match_array(product_id) end diff --git a/spec/intelligent_foods/resources/menu_spec.rb b/spec/core/resources/menu_spec.rb similarity index 77% rename from spec/intelligent_foods/resources/menu_spec.rb rename to spec/core/resources/menu_spec.rb index ca8a82a..54b4196 100644 --- a/spec/intelligent_foods/resources/menu_spec.rb +++ b/spec/core/resources/menu_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe IntelligentFoods::Menu do +RSpec.describe Bls::Core::Menu do describe ".retrieve_all" do it "returns the menus" do menu_ids = ["2023-03-12"] @@ -8,7 +8,7 @@ stub_api_v1_authentication stub_api_response response: response - result = IntelligentFoods::Menu.retrieve_all + result = Bls::Core::Menu.retrieve_all expect(result.map(&:id)).to eq(menu_ids) end @@ -22,7 +22,7 @@ stub_api_v1_authentication stub_api_response response: response - result = IntelligentFoods::Menu.retrieve(menu_id) + result = Bls::Core::Menu.retrieve(menu_id) expect(result.id).to eq(menu_id) end @@ -35,7 +35,7 @@ response = build_response(body: body) stub_api_v1_authentication stub_api_response response: response - menu = IntelligentFoods::Menu.retrieve(menu_id) + menu = Bls::Core::Menu.retrieve(menu_id) result = menu.items.size @@ -43,15 +43,15 @@ end context "the id does not match a menu" do - it "raises a IntelligentFoods::ResourceRetrievalError error" do + it "raises a Bls::Core::ResourceRetrievalError error" do menu_id = "2023-01-01" response = error_response(message: "Menu not found", http_status_code: 400) stub_api_response response: response expect { - IntelligentFoods::Menu.retrieve(menu_id) - }.to raise_error(IntelligentFoods::ResourceRetrievalError) + Bls::Core::Menu.retrieve(menu_id) + }.to raise_error(Bls::Core::ResourceRetrievalError) end end end diff --git a/spec/intelligent_foods/resources/order_spec.rb b/spec/core/resources/order_spec.rb similarity index 83% rename from spec/intelligent_foods/resources/order_spec.rb rename to spec/core/resources/order_spec.rb index d91ae51..2487132 100644 --- a/spec/intelligent_foods/resources/order_spec.rb +++ b/spec/core/resources/order_spec.rb @@ -1,12 +1,12 @@ # frozen_string_literal: true -RSpec.describe IntelligentFoods::Order do +RSpec.describe Bls::Core::Order do describe "#create!" do it "creates the order" do recipient = build(:recipient) menu = build(:menu, id: "2023-01-01") order_item = build(:order_item) - order = IntelligentFoods::Order.new(menu: menu, + order = Bls::Core::Order.new(menu: menu, recipient: recipient, delivery_date: "2023-01-07", items: [order_item], @@ -36,9 +36,9 @@ it "assigns the recipient in the request body" do recipient = build(:recipient) - ship_to = IntelligentFoods::RecipientSerializer.new(recipient).to_json + ship_to = Bls::Core::RecipientSerializer.new(recipient).to_json menu = build(:menu, id: "2023-01-01") - order = IntelligentFoods::Order.new(recipient: recipient, menu: menu) + order = Bls::Core::Order.new(recipient: recipient, menu: menu) body = build_order_response response = build_response(body: body) stub_api_response response: response @@ -52,7 +52,7 @@ it "excludes it from the request body" do recipient = build(:recipient, street2: "") menu = build(:menu, id: "2023-01-01") - order = IntelligentFoods::Order.new(recipient: recipient, menu: menu) + order = Bls::Core::Order.new(recipient: recipient, menu: menu) body = build_order_response response = build_response(body: body) stub_api_response response: response @@ -67,10 +67,10 @@ recipient = build(:recipient) menu = build(:menu, id: "2023-01-01") order_item = build(:order_item, quantity: 2) - order_item_serialized = IntelligentFoods::OrderItemSerializer. + order_item_serialized = Bls::Core::OrderItemSerializer. new(order_item). to_json - order = IntelligentFoods::Order.new(recipient: recipient, menu: menu, + order = Bls::Core::Order.new(recipient: recipient, menu: menu, items: [order_item]) body = build_order_response response = build_response(body: body) @@ -86,7 +86,7 @@ recipient = build(:recipient) menu = build(:menu, id: "2023-01-01") order_item = build(:order_item, quantity: 2) - order = IntelligentFoods::Order.new(menu: menu, + order = Bls::Core::Order.new(menu: menu, recipient: recipient, items: [order_item], external_id: 1234) @@ -103,27 +103,27 @@ it "raises a OrderNotCreatedError" do recipient = build(:recipient) menu = build(:menu, id: "2023-01-01") - order = IntelligentFoods::Order.new(recipient: recipient, menu: menu) + order = Bls::Core::Order.new(recipient: recipient, menu: menu) response = error_response(message: "Order Not Created", http_status_code: 400) stub_api_response response: response expect { order.create! - }.to raise_error(IntelligentFoods::OrderNotCreatedError) + }.to raise_error(Bls::Core::OrderNotCreatedError) end it "marks the order as invalid" do recipient = build(:recipient) menu = build(:menu, id: "2023-01-01") - order = IntelligentFoods::Order.new(recipient: recipient, menu: menu) + order = Bls::Core::Order.new(recipient: recipient, menu: menu) response = error_response(message: "Order Not Created", http_status_code: 400) stub_api_response response: response begin order.create! - rescue IntelligentFoods::OrderNotCreatedError + rescue Bls::Core::OrderNotCreatedError # noop end @@ -136,7 +136,7 @@ recipient = build(:recipient) menu = build(:menu, id: "2023-01-01") order_item = build(:order_item, quantity: 2) - order = IntelligentFoods::Order.new(recipient: recipient, menu: menu, + order = Bls::Core::Order.new(recipient: recipient, menu: menu, items: [order_item], skip_temperature_check: true) body = build_order_response @@ -155,7 +155,7 @@ describe "#cancel!" do it "cancels the order" do - order = IntelligentFoods::Order.new(id: 1) + order = Bls::Core::Order.new(id: 1) response = build_response(body: nil, http_status_code: 204) stub_api_response response: response @@ -166,25 +166,25 @@ context "the response code is not 204" do it "raises a OrderNotCancelledError" do - order = IntelligentFoods::Order.new(id: 1) + order = Bls::Core::Order.new(id: 1) response = error_response(message: "Order Not Found", http_status_code: 400) stub_api_response response: response expect { order.cancel! - }.to raise_error(IntelligentFoods::OrderNotCancelledError) + }.to raise_error(Bls::Core::OrderNotCancelledError) end it "marks the order as not valid" do - order = IntelligentFoods::Order.new(id: 1) + order = Bls::Core::Order.new(id: 1) response = error_response(message: "Order Not Found", http_status_code: 400) stub_api_response response: response begin order.cancel! - rescue IntelligentFoods::OrderNotCancelledError + rescue Bls::Core::OrderNotCancelledError # noop end diff --git a/spec/core_spec.rb b/spec/core_spec.rb new file mode 100644 index 0000000..ee0fdf8 --- /dev/null +++ b/spec/core_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +RSpec.describe Bls::Core do + it "has a version number" do + expect(Bls::Core::VERSION).not_to be_nil + end + + describe "#configure" do + it "sets the client ID" do + Bls::Core.configure do |config| + config.client_id = "abc" + end + + expect(Bls::Core.client_id).to eq("abc") + end + + it "sets the client secret" do + Bls::Core.configure do |config| + config.client_secret = "abc" + end + + expect(Bls::Core.client_secret).to eq("abc") + end + + it "sets the environment" do + Bls::Core.configure do |config| + config.environment = "preview" + end + + expect(Bls::Core.environment).to eq("preview") + end + end +end diff --git a/spec/factories.rb b/spec/factories.rb index 12a5910..57bc36d 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -1,5 +1,5 @@ FactoryBot.define do - factory :order, class: "IntelligentFoods::Order" do + factory :order, class: "Bls::Core::Order" do menu recipient delivery_date { Date.today.to_s } @@ -14,7 +14,7 @@ end end - factory :recipient, class: "IntelligentFoods::Recipient" do + factory :recipient, class: "Bls::Core::Recipient" do name { "First Name" } street1 { "123 Main Street" } street2 { "Apt 2B" } @@ -27,14 +27,14 @@ delivery_instructions { "Door code 1234" } end - factory :menu, class: "IntelligentFoods::Menu" do + factory :menu, class: "Bls::Core::Menu" do id { Date.today.to_s } deadline { Time.now } shipping_fee { 9.99 } items { create_list :menu_item, 3 } end - factory :menu_item, class: "IntelligentFoods::MenuItem" do + factory :menu_item, class: "Bls::Core::MenuItem" do sequence :id do |n| "d89397e1bad49c3b855df4406e5bf0#{n}" end @@ -47,7 +47,7 @@ end end - factory :order_item, class: "IntelligentFoods::OrderItem" do + factory :order_item, class: "Bls::Core::OrderItem" do sequence :sku do |n| "IF233#{n}" end diff --git a/spec/intelligent_foods_spec.rb b/spec/intelligent_foods_spec.rb deleted file mode 100644 index eb5f719..0000000 --- a/spec/intelligent_foods_spec.rb +++ /dev/null @@ -1,33 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe IntelligentFoods do - it "has a version number" do - expect(IntelligentFoods::VERSION).not_to be_nil - end - - describe "#configure" do - it "sets the client ID" do - IntelligentFoods.configure do |config| - config.client_id = "abc" - end - - expect(IntelligentFoods.client_id).to eq("abc") - end - - it "sets the client secret" do - IntelligentFoods.configure do |config| - config.client_secret = "abc" - end - - expect(IntelligentFoods.client_secret).to eq("abc") - end - - it "sets the environment" do - IntelligentFoods.configure do |config| - config.environment = "preview" - end - - expect(IntelligentFoods.environment).to eq("preview") - end - end -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 31ff96c..8815977 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -3,7 +3,7 @@ require "factory_bot" require "json-schema" require "pry" -require "intelligent_foods" +require "bls/core" require "webmock" Dir["./spec/support/**/*.rb"].sort.each { |f| require f } diff --git a/spec/support/helpers/api_helper.rb b/spec/support/helpers/api_helper.rb index 67422ac..e64195b 100644 --- a/spec/support/helpers/api_helper.rb +++ b/spec/support/helpers/api_helper.rb @@ -23,8 +23,8 @@ def stub_authentication(access_token: "indifferenttoken") end def stub_api_v1_authentication(authenticated: true) - api = IntelligentFoods::V1.build - allow(IntelligentFoods::V1).to receive(:new).and_return(api) + api = Bls::Core::V1.build + allow(Bls::Core::V1).to receive(:new).and_return(api) allow(api).to receive(:authenticated?).and_return(authenticated) end