Skip to content
This repository has been archived by the owner on Apr 22, 2021. It is now read-only.

Implement ProofAddress lambda (LG-3579) #3

Merged
merged 8 commits into from Oct 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 9 additions & 1 deletion Gemfile
Expand Up @@ -3,4 +3,12 @@ source 'https://rubygems.org'
gemspec

gem "aws-sdk", '~> 3'
gem "rspec"

gem 'lexisnexis', github: '18F/identity-lexisnexis-api-client-gem', tag: 'v2.4.1'
gem 'proofer', github: '18F/identity-proofer-gem', tag: 'v2.7.0'
gem 'retries'

group :test do
gem 'rspec'
gem 'webmock'
end
5 changes: 4 additions & 1 deletion README.md
Expand Up @@ -12,7 +12,9 @@ gem 'identity-idp-functions', github: '18f/identity-idp-functions'
Calling a handler directly

```ruby
IdentityIdpFunctions::DemoFunction::Handler.handle(
require 'identity-idp-functions'

IdentityIdpFunctions::DemoFunction.handle(
event: event,
context: context
)
Expand All @@ -21,6 +23,7 @@ IdentityIdpFunctions::DemoFunction::Handler.handle(
## Adding a new Lambda

- Lambdas should have an entry point at `source/$function_name/lib/$function_name.rb`
- Update `source/template.yaml` to add the required metadata about the lambda. Copy `DemoFunction` and replace all the various cases of its name (`demo_function`, `Demo Function`, `DemoFunction`)

## Running tests

Expand Down
2 changes: 1 addition & 1 deletion lib/identity-idp-functions/version.rb
@@ -1,3 +1,3 @@
module IdentityIdpFunctions
VERSION = "0.1.0"
VERSION = "0.2.0"
end
7 changes: 5 additions & 2 deletions source/demo_function/spec/demo_function_spec.rb
@@ -1,7 +1,10 @@
RSpec.describe IdentityIdpFunctions::DemoFunction do
before do
ENV["S3_BUCKET_NAME"] = 'test-bucket'
ENV["AWS_REGION"] = 'test-region'
stub_const(
'ENV',
'S3_BUCKET_NAME' => 'test-bucket',
'AWS_REGION' => 'test-region',
)

Aws.config[:s3] = {
stub_responses: {
Expand Down
8 changes: 8 additions & 0 deletions source/proof_address/lib/Gemfile
@@ -0,0 +1,8 @@
source "https://rubygems.org"

ruby '~> 2.7.0'

gem 'faraday'
gem 'lexisnexis', github: '18F/identity-lexisnexis-api-client-gem', tag: 'v2.4.1'
gem 'proofer', github: '18F/identity-proofer-gem', tag: 'v2.7.0'
gem 'retries'
57 changes: 57 additions & 0 deletions source/proof_address/lib/proof_address.rb
@@ -0,0 +1,57 @@
require 'proofer'
require 'lexisnexis'
require 'faraday'
require 'retries'

module IdentityIdpFunctions
class ProofAddress
def self.handle(event:, context:)
params = JSON.parse(event['body'], symbolize_names: true)
new(
idp_api_auth_token: ENV.fetch("IDP_API_AUTH_TOKEN"),
**params
).proof
end

attr_reader :applicant_pii, :callback_url, :idp_api_auth_token

def initialize(applicant_pii:, callback_url:, idp_api_auth_token:)
@applicant_pii = applicant_pii
@callback_url = callback_url
@idp_api_auth_token = idp_api_auth_token
end

def proof
proofer_result = with_retries(**retry_options) do
lexisnexis_proofer.proof(applicant_pii)
end

post_callback(callback_body: {
address_result: proofer_result.to_h,
})
end

def post_callback(callback_body:)
with_retries(**retry_options) do
Faraday.post(
callback_url,
callback_body.to_json,
"X-API-AUTH-TOKEN" => idp_api_auth_token,
"Content-Type" => 'application/json',
"Accept" => 'application/json'
)
end
end

def lexisnexis_proofer
LexisNexis::PhoneFinder::Proofer.new
end

def retry_options
{
max_tries: 3,
rescue: [Faraday::TimeoutError, Faraday::ConnectionFailed],
}
end
end
end
148 changes: 148 additions & 0 deletions source/proof_address/spec/proof_address_spec.rb
@@ -0,0 +1,148 @@
require 'securerandom'

RSpec.describe IdentityIdpFunctions::ProofAddress do
let(:idp_api_auth_token) { SecureRandom.hex }
let(:callback_url) { 'https://example.login.gov/api/callbacks/proof-address/:token' }
let(:applicant_pii) do
{
first_name: 'Johnny',
last_name: 'Appleseed',
uuid: SecureRandom.hex,
dob: '01/01/1970',
ssn: '123456789',
phone: '18888675309',
}
end

describe '.handle' do
before do
stub_const(
'ENV',
'IDP_API_AUTH_TOKEN' => idp_api_auth_token,
'lexisnexis_account_id' => 'abc123',
'lexisnexis_request_mode' => 'aaa',
'lexisnexis_username' => 'aaa',
'lexisnexis_password' => 'aaa',
'lexisnexis_base_url' => 'https://lexisnexis.example.com/',
'lexisnexis_phone_finder_workflow' => 'aaa',
Comment on lines +22 to +27
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

definitely looking forward to a day when we can pass these all in to the gems as explicit params instead of relying on ENV vars

)

stub_request(
:post,
'https://lexisnexis.example.com/restws/identity/v2/abc123/aaa/conversation'
).to_return(
body: {
"Status" => {
"TransactionStatus" => "passed"
}
}.to_json
)

stub_request(:post, callback_url).
with(
headers: {
'Content-Type' => 'application/json',
'X-API-AUTH-TOKEN' => idp_api_auth_token,
},
) do |request|
expect(JSON.parse(request.body, symbolize_names: true)).to eq(
address_result: {
exception: nil,
errors: {},
messages: [],
success: true,
}
)
end
end

let(:body) do
{
callback_url: callback_url,
applicant_pii: applicant_pii,
}
end

it 'runs' do
IdentityIdpFunctions::ProofAddress.handle(event: { 'body' => body.to_json }, context: nil)
end
end

describe '#proof' do
subject(:function) do
IdentityIdpFunctions::ProofAddress.new(
callback_url: callback_url,
applicant_pii: applicant_pii,
idp_api_auth_token: idp_api_auth_token,
)
end

let(:lexisnexis_proofer) { instance_double(LexisNexis::PhoneFinder::Proofer) }

before do
allow(function).to receive(:lexisnexis_proofer).and_return(lexisnexis_proofer)

stub_request(:post, callback_url).
with(headers: { 'X-API-AUTH-TOKEN' => idp_api_auth_token })
end

context 'with a successful response from the proofer' do
before do
expect(lexisnexis_proofer).to receive(:proof).
and_return(Proofer::Result.new)
end

it 'posts back to the callback url' do
function.proof

expect(WebMock).to have_requested(:post, callback_url)
end
end

context 'with an unsuccessful response from the proofer' do
before do
expect(lexisnexis_proofer).to receive(:proof).
and_return(Proofer::Result.new(exception: RuntimeError.new))
end

it 'posts back to the callback url' do
function.proof

expect(WebMock).to have_requested(:post, callback_url)
end
end

context 'with a connection error talking to the proofer' do
before do
allow(lexisnexis_proofer).to receive(:proof).
and_raise(Faraday::ConnectionFailed.new('error')).
and_raise(Faraday::ConnectionFailed.new('error')).
and_raise(Faraday::ConnectionFailed.new('error'))
end

it 'retries 3 times then errors' do
expect { function.proof }.to raise_error(Faraday::ConnectionFailed)

expect(WebMock).to_not have_requested(:post, callback_url)
end
end

context 'with a connection error posting to the callback url' do
before do
expect(lexisnexis_proofer).to receive(:proof).
and_return(Proofer::Result.new)

stub_request(:post, callback_url).
to_timeout.
to_timeout.
to_timeout
end

it 'retries 3 then errors' do
expect { function.proof }.to raise_error(Faraday::ConnectionFailed)

expect(a_request(:post, callback_url)).to have_been_made.times(3)
end
end
end
end