diff --git a/lib/intelligent_foods.rb b/lib/intelligent_foods.rb index 7643689..55806a8 100644 --- a/lib/intelligent_foods.rb +++ b/lib/intelligent_foods.rb @@ -6,11 +6,15 @@ require "ostruct" require "intelligent_foods/api_client" +require "intelligent_foods/api_adapter_factory" +require "intelligent_foods/api/v1" +require "intelligent_foods/api/v2" require "intelligent_foods/authorization" require "intelligent_foods/authorization/basic" require "intelligent_foods/resources/api_error" require "intelligent_foods/authorization/bearer" require "intelligent_foods/resources/object" +require "intelligent_foods/resources/address" require "intelligent_foods/resources/order" require "intelligent_foods/resources/order_item" require "intelligent_foods/serializers/order_item_serializer" @@ -34,10 +38,6 @@ def configure configure_environment end - def base_url - @base_url = "https://api.sunbasket.#{tld}/partner/v1" - end - def client @client ||= IntelligentFoods::ApiClient.new(id: client_id, secret: client_secret) @@ -46,18 +46,5 @@ def client def refresh_client @client = nil end - - protected - - attr_reader :tld - - def configure_environment - if environment == "production" - @tld = "com" - else - @tld = "dev" - end - end - end end diff --git a/lib/intelligent_foods/api/v1.rb b/lib/intelligent_foods/api/v1.rb new file mode 100644 index 0000000..59d2605 --- /dev/null +++ b/lib/intelligent_foods/api/v1.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module IntelligentFoods + class V1 + attr_accessor :tld + + def initialize(tld:) + @tld = tld + end + + def base_url + "https://api.sunbasket.#{tld}/partner/v1" + end + end +end diff --git a/lib/intelligent_foods/api/v2.rb b/lib/intelligent_foods/api/v2.rb new file mode 100644 index 0000000..b85588c --- /dev/null +++ b/lib/intelligent_foods/api/v2.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module IntelligentFoods + class V2 + attr_accessor :tld + + def initialize(tld:) + @tld = tld + end + + def base_url + "https://api.intelligentfoods.#{tld}" + end + end +end diff --git a/lib/intelligent_foods/api_adapter_factory.rb b/lib/intelligent_foods/api_adapter_factory.rb new file mode 100644 index 0000000..6c1a8ca --- /dev/null +++ b/lib/intelligent_foods/api_adapter_factory.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module IntelligentFoods + class ApiAdapterFactory + attr_accessor :tld, :api_version + + def initialize(tld:, api_version: nil) + @tld = tld + @api_version = api_version + end + + def create + return IntelligentFoods::V1.new(tld: tld) unless api_version.present? + + api_version.new(tld: tld) + end + + def self.build(resource, environment: IntelligentFoods.environment) + tld = if environment == "production" + "com" + else + "dev" + end + new(tld: tld, api_version: resource.api_version).create + end + end +end diff --git a/lib/intelligent_foods/api_client.rb b/lib/intelligent_foods/api_client.rb index eaacaa4..53e7e35 100644 --- a/lib/intelligent_foods/api_client.rb +++ b/lib/intelligent_foods/api_client.rb @@ -3,12 +3,16 @@ module IntelligentFoods class ApiClient attr_accessor :access_token - attr_reader :id, :secret + attr_reader :id, :secret, :basic_auth_token - def initialize(id: nil, secret: nil, client: nil) + def initialize(id: nil, secret: nil, client: nil, + username: nil, password: nil) @id = id @secret = secret @client = client || Net::HTTP + @basic_auth_token = IntelligentFoods::Authorization::Basic. + factory(client_id: username, + client_secret: password) end def authenticate! diff --git a/lib/intelligent_foods/authorization/basic.rb b/lib/intelligent_foods/authorization/basic.rb index 3b8dde3..76b1094 100644 --- a/lib/intelligent_foods/authorization/basic.rb +++ b/lib/intelligent_foods/authorization/basic.rb @@ -6,7 +6,7 @@ def header "Basic #{token}" end - def self.factory(client_id:, client_secret:) + def self.factory(client_id: nil, client_secret: nil) encoded_token = Base64.strict_encode64("#{client_id}:#{client_secret}") new(token: encoded_token) end diff --git a/lib/intelligent_foods/resources/address.rb b/lib/intelligent_foods/resources/address.rb new file mode 100644 index 0000000..ab0b91d --- /dev/null +++ b/lib/intelligent_foods/resources/address.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module IntelligentFoods + class Address < IntelligentFoods::Object + def api_version + IntelligentFoods::V2 + end + + def verify + uri = URI("#{api.base_url}/address/validate") + basic_auth_token = client.basic_auth_token + request = client.build_request_with_body(uri: uri, body: request_body) + response = client.execute_request(request: request, uri: uri, + authorization: basic_auth_token) + Address::build(response.data) + end + + def valid? + valid + end + + protected + + def request_body + @request_body ||= { + address1: address1, + address2: address2, + city: city, + state: state, + zip: zip, + } + end + end +end diff --git a/lib/intelligent_foods/resources/menu.rb b/lib/intelligent_foods/resources/menu.rb index a2f43ab..25720b1 100644 --- a/lib/intelligent_foods/resources/menu.rb +++ b/lib/intelligent_foods/resources/menu.rb @@ -13,7 +13,8 @@ def self.build_from_response(data) end def self.all - uri = URI("#{IntelligentFoods.base_url}/menus") + api = IntelligentFoods::ApiAdapterFactory.build(new) + uri = URI("#{api.base_url}/menus") request = Net::HTTP::Get.new(uri) client = IntelligentFoods.client response = client.execute_request(request: request, uri: uri) @@ -25,7 +26,8 @@ def self.all end def self.find(menu_id) - uri = URI("#{IntelligentFoods.base_url}/menu/#{menu_id}") + api = IntelligentFoods::ApiAdapterFactory.build(new) + uri = URI("#{api.base_url}/menu/#{menu_id}") request = Net::HTTP::Get.new(uri) client = IntelligentFoods.client response = client.execute_request(request: request, uri: uri) diff --git a/lib/intelligent_foods/resources/object.rb b/lib/intelligent_foods/resources/object.rb index c999378..fe0b710 100644 --- a/lib/intelligent_foods/resources/object.rb +++ b/lib/intelligent_foods/resources/object.rb @@ -11,5 +11,9 @@ def self.build(data) def client @client ||= IntelligentFoods.client end + + def api + @api ||= IntelligentFoods::ApiAdapterFactory.build(self) + end end end diff --git a/lib/intelligent_foods/resources/order.rb b/lib/intelligent_foods/resources/order.rb index 69c2c11..137e7cf 100644 --- a/lib/intelligent_foods/resources/order.rb +++ b/lib/intelligent_foods/resources/order.rb @@ -25,7 +25,7 @@ def self.build_from_response(data) end def create! - uri = URI("#{IntelligentFoods.base_url}/order") + uri = URI("#{api.base_url}/order") request = client.build_request_with_body(uri: uri, body: request_body) response = client.execute_request(request: request, uri: uri) if response.success? @@ -37,7 +37,7 @@ def create! end def cancel! - uri = URI("#{IntelligentFoods.base_url}/order/#{id}") + uri = URI("#{api.base_url}/order/#{id}") request = Net::HTTP::Delete.new(uri) response = client.execute_request(request: request, uri: uri) if response.success? diff --git a/spec/intelligent_foods/resources/address_spec.rb b/spec/intelligent_foods/resources/address_spec.rb new file mode 100644 index 0000000..3159319 --- /dev/null +++ b/spec/intelligent_foods/resources/address_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +RSpec.describe IntelligentFoods::Address do + describe "#verify" do + it "uses the api v2" do + address = IntelligentFoods::Address.new(address1: "123 Main Street", + city: "San Francisco", + state: "CA", + zip: "12345") + client = IntelligentFoods.client + allow(IntelligentFoods).to receive(:client).and_return(client) + allow(client).to receive(:build_request_with_body).and_call_original + expected_uri = URI("https://api.intelligentfoods.dev/address/validate") + stub_api_response + + address.verify + + expect(client).to have_received(:build_request_with_body). + with(hash_including(uri: expected_uri)) + end + + context "when the response is valid" do + it "is a valid address" do + address = IntelligentFoods::Address.new(address1: "123 Main Street", + city: "San Francisco", + state: "CA", + zip: "12345") + body = build_address_response + response = build_response(body: body) + stub_api_response response: response + + result = address.verify + + expect(result).to be_valid + end + end + + context "when the response is invalid" do + it "is not a valid address" do + address = IntelligentFoods::Address.new(address1: "123 Main Street", + city: "San Francisco", + state: "CA", + zip: "12345") + body = build_address_response(valid: false) + response = build_response(body: body) + stub_api_response response: response + + result = address.verify + + expect(result).not_to be_valid + end + end + end +end diff --git a/spec/intelligent_foods_spec.rb b/spec/intelligent_foods_spec.rb index a77c6f1..f7ade5f 100644 --- a/spec/intelligent_foods_spec.rb +++ b/spec/intelligent_foods_spec.rb @@ -52,6 +52,32 @@ expect(IntelligentFoods.base_url).to eq(staging_url) end + + context "the api version is v1" do + it "returns the correct url" do + IntelligentFoods.configure do |config| + config.environment = "staging" + end + staging_url = "https://api.sunbasket.dev/partner/v1" + + result = IntelligentFoods.base_url(api_version: "v1") + + expect(result).to eq(staging_url) + end + end + + context "the api version is v2" do + it "returns the correct url" do + IntelligentFoods.configure do |config| + config.environment = "staging" + end + staging_url = "https://api.intelligentfoods.dev" + + result = IntelligentFoods.base_url(api_version: "v2") + + expect(result).to eq(staging_url) + end + end end context "environment is production" do @@ -63,6 +89,32 @@ expect(IntelligentFoods.base_url).to eq(production_url) end + + context "the api version is v1" do + it "returns the correct url" do + IntelligentFoods.configure do |config| + config.environment = "production" + end + production_url = "https://api.sunbasket.com/partner/v1" + + result = IntelligentFoods.base_url(api_version: "v1") + + expect(result).to eq(production_url) + end + end + + context "the api version is v2" do + it "returns the correct url" do + IntelligentFoods.configure do |config| + config.environment = "production" + end + production_url = "https://api.intelligentfoods.com" + + result = IntelligentFoods.base_url(api_version: "v2") + + expect(result).to eq(production_url) + end + end end context "environment is not any of the above" do diff --git a/spec/support/fixtures/address_response.json b/spec/support/fixtures/address_response.json new file mode 100644 index 0000000..540dab9 --- /dev/null +++ b/spec/support/fixtures/address_response.json @@ -0,0 +1,23 @@ +{ + "valid": true, + "residential": true, + "zip4": "string", + "validationState": "VERIFIED", + "errorMessage": "string", + "suggestions": [ + { + "address": { + "address1": "string", + "address2": "string", + "city": "string", + "state": "AL", + "zip": "string", + "zip4": "string" + }, + "residential": true, + "confirmZip4": true, + "addressDpvMatchCode": "string", + "deliveryPointBarcode": "string" + } + ] +} diff --git a/spec/support/helpers/api_helper.rb b/spec/support/helpers/api_helper.rb index 5098819..c16eedf 100644 --- a/spec/support/helpers/api_helper.rb +++ b/spec/support/helpers/api_helper.rb @@ -1,4 +1,5 @@ module ApiHelper + ADDRESS_API_RESPONSE = "spec/support/fixtures/address_response.json".freeze MENU_API_RESPONSE = "spec/support/fixtures/menu_response.json".freeze ORDER_API_RESPONSE = "spec/support/fixtures/order_response.json".freeze ERROR_API_RESPONSE = "spec/support/fixtures/error_response.json".freeze @@ -63,6 +64,12 @@ def build_order_response read_order_api_response end + def build_address_response(valid: true) + stubbed_response = parse_json_file(ADDRESS_API_RESPONSE) + stubbed_response[:valid] = valid + stubbed_response + end + def stubbed_menu_items response = read_menu_api_response response[:items]