Skip to content

Commit

Permalink
Add code to assign an area to an address through postcode
Browse files Browse the repository at this point in the history
  • Loading branch information
jjromeo committed Apr 25, 2023
1 parent 4d1c5c5 commit a6e5226
Show file tree
Hide file tree
Showing 11 changed files with 288 additions and 4 deletions.
6 changes: 5 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ PATH
countries
defra_ruby_address
defra_ruby_alert (~> 2.1)
defra_ruby_area (~> 2.0)
defra_ruby_email
defra_ruby_validators (>= 2.5.0)
high_voltage (~> 3.1)
Expand Down Expand Up @@ -133,6 +134,9 @@ GEM
rest-client (~> 2.0)
defra_ruby_alert (2.1.1)
airbrake
defra_ruby_area (2.0.3)
nokogiri (>= 1.13.2)
rest-client (~> 2.0)
defra_ruby_email (1.2.0)
notifications-ruby-client
rails (~> 6.0)
Expand Down Expand Up @@ -268,7 +272,7 @@ GEM
faraday (>= 0.9)
sawyer (~> 0.8.0, >= 0.5.3)
orm_adapter (0.5.0)
os_map_ref (0.5.0)
os_map_ref (0.1.1)
parallel (1.22.1)
parser (3.2.1.1)
ast (~> 2.4.1)
Expand Down
1 change: 1 addition & 0 deletions app/models/waste_carriers_engine/address.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class Address
field :royalMailUpdateDate, as: :royal_mail_update_date, type: String
field :easting, type: Integer
field :northing, type: Integer
field :area, type: String
field :firstOrOnlyEasting, as: :first_or_only_easting, type: Integer
field :firstOrOnlyNorthing, as: :first_or_only_northing, type: Integer
field :firstName, as: :first_name, type: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
module WasteCarriersEngine
class AddressLookupService < BaseService
def run(postcode)
DefraRuby::Address::OsPlacesAddressLookupService.run(postcode)
DefraRuby::Address::EaAddressFacadeV11Service.run(postcode)
end
end
end
24 changes: 24 additions & 0 deletions app/services/waste_carriers_engine/assign_site_details_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module WasteCarriersEngine
class AssignSiteDetailsService < BaseService
attr_reader :address

delegate :postcode, :area, to: :address

def run(address:)
@address = address

assign_area_from_postcode
end

private

def assign_area_from_postcode
return if area.present?
return unless postcode.present?

x, y = DetermineEastingAndNorthingService.run(postcode: postcode).values

address.area = DetermineAreaService.run(easting: x, northing: y)
end
end
end
20 changes: 20 additions & 0 deletions app/services/waste_carriers_engine/determine_area_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module WasteCarriersEngine
class DetermineAreaService < BaseService
def run(easting:, northing:)
response = DefraRuby::Area::PublicFaceAreaService.run(easting, northing)

return response.areas.first.long_name if response.successful?
return "Outside England" if response.error.instance_of?(DefraRuby::Area::NoMatchError)

handle_error(response.error, easting, northing)
"Not found"
end

private

def handle_error(error, easting, northing)
Airbrake.notify(error, easting: easting, northing: northing) if defined? Airbrake
Rails.logger.error "Area lookup failed:\n #{error}"
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
module WasteCarriersEngine
class DetermineEastingAndNorthingService < BaseService
def run(postcode:)
@result = { easting: nil, northing: nil }

easting_and_northing_from_postcode(postcode)

@result
end

private

def easting_and_northing_from_postcode(postcode)
return if postcode.blank?

response = AddressLookupService.run(postcode)

if response.successful?
apply_result_coordinates(response.results.first)
elsif response.error.is_a?(DefraRuby::Address::NoMatchError)
no_match_from_postcode_lookup(postcode)
else
error_from_postcode_lookup(postcode, response.error)
end
end

def apply_result_coordinates(result)
@result[:easting] = result["x"].to_f
@result[:northing] = result["y"].to_f
end

def handle_error(error, message, metadata)
Airbrake.notify(error, metadata) if defined?(Airbrake)
Rails.logger.error(message)
end

def no_match_from_postcode_lookup(postcode)
default_do_not_fetch_again_coordinates

message = "Postcode to easting and northing returned no results"
handle_error(StandardError.new(message), message, postcode: postcode)
end

def error_from_postcode_lookup(postcode, error)
default_do_not_fetch_again_coordinates

message = "Postcode to easting and northing errored: #{error.message}"
handle_error(StandardError.new(message), message, postcode: postcode)
end

def default_do_not_fetch_again_coordinates
@result[:easting] = 0.00
@result[:northing] = 0.00
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ module WasteCarriersEngine
it "does send a request to os places using the defra ruby gem" do
postcode = "BS1 2AF"

allow(DefraRuby::Address::OsPlacesAddressLookupService).to receive(:run)
allow(DefraRuby::Address::EaAddressFacadeV11Service).to receive(:run)

described_class.run(postcode)

expect(DefraRuby::Address::OsPlacesAddressLookupService).to have_received(:run).with(postcode)
expect(DefraRuby::Address::EaAddressFacadeV11Service).to have_received(:run).with(postcode)
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
require 'rails_helper'

require 'rails_helper'

module WasteCarriersEngine
RSpec.describe AssignSiteDetailsService, type: :service do
describe '#run' do
let(:x) { 123456 }
let(:y) { 654321 }
let(:area) { 'Area Name' }

context 'when address has a postcode and area is not present' do
let(:address) { build(:address, :has_required_data) }

before do
allow(DetermineEastingAndNorthingService).to receive(:run).and_return(easting: x, northing: y)
allow(DetermineAreaService).to receive(:run).and_return(area)
end

it 'assigns area' do
described_class.run(address: address)
expect(address.area).to eq(area)
end
end

context 'when address has an area' do
let(:address) { build(:address, :has_required_data, area: area) }

it 'does not change the area' do
expect(DetermineEastingAndNorthingService).not_to receive(:run)
expect(DetermineAreaService).not_to receive(:run)

described_class.run(address: address)

expect(address.area).to eq(area)
end
end

context 'when address does not have a postcode' do
let(:address) { build(:address, :has_required_data, postcode: nil) }

it 'does not assign area' do
expect(DetermineEastingAndNorthingService).not_to receive(:run)
expect(DetermineAreaService).not_to receive(:run)

described_class.run(address: address)

expect(address.area).to be_nil
end
end
end
end
end
63 changes: 63 additions & 0 deletions spec/services/waste_carriers_engine/determine_area_service_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# frozen_string_literal: true

require "rails_helper"
require "defra_ruby/area"

module WasteCarriersEngine
RSpec.describe DetermineAreaService do
describe ".run" do
let(:coordinates) { { easting: 358_205.03, northing: 172_708.07 } }

before do
allow(DefraRuby::Area::PublicFaceAreaService)
.to receive(:run)
.with(coordinates[:easting], coordinates[:northing])
.and_return(response)
end

context "when the lookup is successful" do
let(:response) do
instance_double(DefraRuby::Area::Response, successful?: true, areas: [instance_double(DefraRuby::Area::Area, long_name: "Wessex")])
end

it "returns the matching area" do
expect(described_class.run(coordinates)).to eq "Wessex"
end

it "does not notify Airbrake of the error" do
allow(Airbrake).to receive(:notify)

described_class.run(coordinates)

expect(Airbrake).not_to have_received(:notify)
end
end

context "when the lookup is unsuccessful" do
context "with no match found" do
let(:response) { instance_double(DefraRuby::Area::Response, successful?: false, error: DefraRuby::Area::NoMatchError.new) }

it "returns 'Outside England'" do
expect(described_class.run(coordinates)).to eq "Outside England"
end
end

context "with a failure" do
let(:response) { instance_double(DefraRuby::Area::Response, successful?: false, error: StandardError.new) }

it "returns 'Not found'" do
expect(described_class.run(coordinates)).to eq "Not found"
end

it "uses Airbrake to notify Errbit of the error" do
allow(Airbrake).to receive(:notify)

described_class.run(coordinates)

expect(Airbrake).to have_received(:notify)
end
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# spec/services/waste_carriers_engine/determine_easting_and_northing_service_spec.rb

require 'rails_helper'

module WasteCarriersEngine
RSpec.describe DetermineEastingAndNorthingService, type: :service do
let(:service) { DetermineEastingAndNorthingService.new }
let(:valid_postcode) { 'SW1A 1AA' }
let(:invalid_postcode) { 'INVALID' }

describe '#run' do
context 'when given a valid postcode' do
before do
# Stub AddressLookupService to return a valid response
allow(AddressLookupService).to receive(:run).with(valid_postcode).and_return(
instance_double(DefraRuby::Address::Response, successful?: true, results: [{ "x" => 529_090, "y" => 179_645 }])
)
end

it 'returns the correct easting and northing values' do
result = service.run(postcode: valid_postcode)

expect(result[:easting]).to eq(529_090.0)
expect(result[:northing]).to eq(179_645.0)
end
end

context 'when given an invalid postcode' do
before do
# Stub AddressLookupService to return a NoMatchError
allow(AddressLookupService).to receive(:run).with(invalid_postcode).and_return(
instance_double(DefraRuby::Address::Response, successful?: false, error: DefraRuby::Address::NoMatchError.new)
)
end

it 'returns the default easting and northing values' do
result = service.run(postcode: invalid_postcode)

expect(result[:easting]).to eq(0.0)
expect(result[:northing]).to eq(0.0)
end
end

context 'when the postcode lookup service returns an error' do
before do
# Stub AddressLookupService to return a generic error
allow(AddressLookupService).to receive(:run).with(invalid_postcode).and_return(
instance_double(DefraRuby::Address::Response, successful?: false, error: StandardError.new('An error occurred'))
)
end

it 'returns the default easting and northing values' do
result = service.run(postcode: invalid_postcode)

expect(result[:easting]).to eq(0.0)
expect(result[:northing]).to eq(0.0)
end
end
end
end
end
2 changes: 2 additions & 0 deletions waste_carriers_engine.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,6 @@ Gem::Specification.new do |s|

# Used to generate a PDF from HTML, in our case, the users certificate
s.add_dependency "wicked_pdf"

s.add_dependency "defra_ruby_area", "~> 2.0"
end

0 comments on commit a6e5226

Please sign in to comment.