diff --git a/engines/dfc_provider/app/controllers/dfc_provider/addresses_controller.rb b/engines/dfc_provider/app/controllers/dfc_provider/addresses_controller.rb new file mode 100644 index 0000000000..f03acfb2f0 --- /dev/null +++ b/engines/dfc_provider/app/controllers/dfc_provider/addresses_controller.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +# Controller used to provide the CatalogItem API for the DFC application +module DfcProvider + class AddressesController < DfcProvider::ApplicationController + def show + address = Spree::Address.find(params.require(:id)) + + return not_found unless authorized(address) + + dfc_address = AddressBuilder.address(address) + render json: DfcIo.export(dfc_address) + end + + private + + # Does the current user have access to this address? + # + # It's possible to guess address ids and therefore we need to prevent the + # collection of sensitive customer data. + # + # We may want to extend the list of authorised addresses once the DFC API + # references them. To start with, we would only need enterprise addresses + # but I included a few more options to show how this will probably evolve. + # + # Currently not checked models: + # + # - Spree::Card + # - Spree::Order + # - Spree::Shipment + # - Subscription + def authorized(address) + current_user.ship_address_id == address.id || + current_user.bill_address_id == address.id || + [ + customer_address(address), + public_enterprise_group_address(address), + public_enterprise_address(address), + managed_enterprise_address(address), + ].any?(&:exists?) + end + + def customer_address(address) + current_user.customers.where(bill_address: address).or( + current_user.customers.where(ship_address: address) + ) + end + + def public_enterprise_group_address(address) + EnterpriseGroup.where(address:) + end + + def public_enterprise_address(address) + Enterprise.activated.visible.is_distributor.where(address:) + end + + def managed_enterprise_address(address) + current_user.enterprises.where(address:) + end + end +end diff --git a/engines/dfc_provider/app/services/address_builder.rb b/engines/dfc_provider/app/services/address_builder.rb index 331d742a7e..7b89d2a073 100644 --- a/engines/dfc_provider/app/services/address_builder.rb +++ b/engines/dfc_provider/app/services/address_builder.rb @@ -2,9 +2,8 @@ class AddressBuilder < DfcBuilder def self.address(address) - # TODO add url helper/address contoller so we can do urls.address_url(address.id) DataFoodConsortium::Connector::Address.new( - "http://test.host/api/dfc-v1.7/address/#{address.id}", + urls.address_url(address), street: address.address1, postalCode: address.zipcode, city: address.city, diff --git a/engines/dfc_provider/config/routes.rb b/engines/dfc_provider/config/routes.rb index a016a9f5b9..4064a83fca 100644 --- a/engines/dfc_provider/config/routes.rb +++ b/engines/dfc_provider/config/routes.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true DfcProvider::Engine.routes.draw do + resources :addresses, only: [:show] resources :enterprises, only: [:show] do resources :catalog_items, only: [:index, :show, :update] resources :supplied_products, only: [:create, :show, :update] diff --git a/engines/dfc_provider/spec/requests/addresses_spec.rb b/engines/dfc_provider/spec/requests/addresses_spec.rb new file mode 100644 index 0000000000..7798dcd282 --- /dev/null +++ b/engines/dfc_provider/spec/requests/addresses_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require DfcProvider::Engine.root.join("spec/swagger_helper") + +describe "Addresses", type: :request, swagger_doc: "dfc-v1.7/swagger.yaml", rswag_autodoc: true do + let(:user) { create(:oidc_user) } + let(:address) { create(:address, id: 40_000) } + let(:result) { json_response } + + before { login_as user } + + path "/api/dfc-v1.7/addresses/{id}" do + get "Show address" do + parameter name: :id, in: :path, type: :string + produces "application/json" + + response "200", "successful" do + let(:id) { address.id } + + before { create(:enterprise, owner: user, address:) } + + run_test! do + expect(result["@id"]).to eq "http://test.host/api/dfc-v1.7/addresses/40000" + expect(result["@type"]).to eq "dfc-b:Address" + end + end + + response "404", "not found" do + context "when the address doesn't exist" do + let(:id) { 0 } + run_test! + end + + context "when the address doesn't belong to you" do + let(:id) { address.id } + run_test! + end + end + end + end +end diff --git a/engines/dfc_provider/spec/services/address_builder_spec.rb b/engines/dfc_provider/spec/services/address_builder_spec.rb index 5beac2aba2..473f9c86fd 100644 --- a/engines/dfc_provider/spec/services/address_builder_spec.rb +++ b/engines/dfc_provider/spec/services/address_builder_spec.rb @@ -14,7 +14,7 @@ describe AddressBuilder do describe ".address" do it "assigns a semantic id" do expect(result.semanticId).to eq( - "http://test.host/api/dfc-v1.7/address/1" + "http://test.host/api/dfc-v1.7/addresses/1" ) end diff --git a/engines/dfc_provider/spec/services/enterprise_builder_spec.rb b/engines/dfc_provider/spec/services/enterprise_builder_spec.rb index 932e066212..ac4bfb1ade 100644 --- a/engines/dfc_provider/spec/services/enterprise_builder_spec.rb +++ b/engines/dfc_provider/spec/services/enterprise_builder_spec.rb @@ -9,7 +9,7 @@ describe EnterpriseBuilder do :enterprise, id: 10_000, name: "Fabi's Farm", description: "The place where stuff grows", abn: "123 456 789 0", - address: build(:address, city: "Melbourne"), + address: build(:address, id: 40_000, city: "Melbourne"), ) } let(:variant) { diff --git a/swagger/dfc-v1.7/swagger.yaml b/swagger/dfc-v1.7/swagger.yaml index 66299f0456..34b927363e 100644 --- a/swagger/dfc-v1.7/swagger.yaml +++ b/swagger/dfc-v1.7/swagger.yaml @@ -38,6 +38,32 @@ security: - ofn_api_token: [] - ofn_session: [] paths: + "/api/dfc-v1.7/addresses/{id}": + get: + summary: Show address + parameters: + - name: id + in: path + required: true + schema: + type: string + responses: + '200': + description: successful + content: + application/json: + examples: + test_example: + value: + "@context": https://www.datafoodconsortium.org + "@id": http://test.host/api/dfc-v1.7/addresses/40000 + "@type": dfc-b:Address + dfc-b:hasStreet: 10 Lovely Street + dfc-b:hasPostalCode: '20170' + dfc-b:hasCity: Herndon + dfc-b:hasCountry: Australia + '404': + description: not found "/api/dfc-v1.7/enterprises/{enterprise_id}/catalog_items": parameters: - name: enterprise_id @@ -64,7 +90,7 @@ paths: dfc-b:affiliates: http://test.host/api/dfc-v1.7/enterprises/10000 - "@id": http://test.host/api/dfc-v1.7/enterprises/10000 "@type": dfc-b:Enterprise - dfc-b:hasAddress: http://test.host/api/dfc-v1.7/address/40000 + dfc-b:hasAddress: http://test.host/api/dfc-v1.7/addresses/40000 dfc-b:hasName: Fred's Farm dfc-b:hasDescription: Beautiful dfc-b:supplies: http://test.host/api/dfc-v1.7/enterprises/10000/supplied_products/10001 @@ -221,7 +247,7 @@ paths: "@graph": - "@id": http://test.host/api/dfc-v1.7/enterprises/10000 "@type": dfc-b:Enterprise - dfc-b:hasAddress: http://test.host/api/dfc-v1.7/address/40000 + dfc-b:hasAddress: http://test.host/api/dfc-v1.7/addresses/40000 dfc-b:hasName: Fred's Farm dfc-b:hasDescription: This is an awesome enterprise dfc-b:VATnumber: 123 456