Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MLPAB-1776 Handle priority-correspondence-sent #1749

Merged
merged 1 commit into from
Jan 31, 2025
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 30 additions & 21 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ ECR_LOGIN ?= @aws-vault exec management -- aws ecr get-login-password --region e
# A category can be added with @category
# This was made possible by https://gist.github.com/prwhite/8168133#gistcomment-1727513
HELP_FUN = \
%help; \
while(<>) { push @{$$help{$$2 // 'options'}}, [$$1, $$3] if /^([a-zA-Z0-9\-]+)\s*:.*\#\#(?:@([a-zA-Z\-]+))?\s(.*)$$/ }; \
print "usage: make [target]\n\n"; \
for (sort keys %help) { \
print "${WHITE}$$_:${RESET}\n"; \
for (@{$$help{$$_}}) { \
$$sep = " " x (32 - length $$_->[0]); \
print " ${YELLOW}$$_->[0]${RESET}$$sep${GREEN}$$_->[1]${RESET}\n"; \
}; \
print "\n"; }
%help; \
while(<>) { push @{$$help{$$2 // 'options'}}, [$$1, $$3] if /^([a-zA-Z0-9\-]+)\s*:.*\#\#(?:@([a-zA-Z\-]+))?\s(.*)$$/ }; \
print "usage: make [target]\n\n"; \
for (sort keys %help) { \
print "${WHITE}$$_:${RESET}\n"; \
for (@{$$help{$$_}}) { \
$$sep = " " x (32 - length $$_->[0]); \
print " ${YELLOW}$$_->[0]${RESET}$$sep${GREEN}$$_->[1]${RESET}\n"; \
}; \
print "\n"; }

help: ##@other Show this help.
@perl -e '$(HELP_FUN)' $(MAKEFILE_LIST)
Expand Down Expand Up @@ -119,12 +119,12 @@ delete-all-items: ##@dynamodb deletes and recreates lpas dynamodb table
delete-table --table-name lpas

docker compose -f docker/docker-compose.yml exec localstack awslocal dynamodb create-table \
--region eu-west-1 \
--table-name lpas \
--attribute-definitions AttributeName=PK,AttributeType=S AttributeName=SK,AttributeType=S AttributeName=LpaUID,AttributeType=S AttributeName=UpdatedAt,AttributeType=S \
--key-schema AttributeName=PK,KeyType=HASH AttributeName=SK,KeyType=RANGE \
--provisioned-throughput ReadCapacityUnits=1000,WriteCapacityUnits=1000 \
--global-secondary-indexes file://dynamodb-lpa-gsi-schema.json
--region eu-west-1 \
--table-name lpas \
--attribute-definitions AttributeName=PK,AttributeType=S AttributeName=SK,AttributeType=S AttributeName=LpaUID,AttributeType=S AttributeName=UpdatedAt,AttributeType=S \
--key-schema AttributeName=PK,KeyType=HASH AttributeName=SK,KeyType=RANGE \
--provisioned-throughput ReadCapacityUnits=1000,WriteCapacityUnits=1000 \
--global-secondary-indexes file://dynamodb-lpa-gsi-schema.json

emit-evidence-received: ##@events emits an evidence-received event with the given LpaUID e.g. emit-evidence-received uid=abc-123
$(eval BODY := $(shell echo '{"version":"0","id":"63eb7e5f-1f10-4744-bba9-e16d327c3b98","detail-type":"evidence-received","source":"opg.poas.sirius","account":"653761790766","time":"2023-08-30T13:40:30Z","region":"eu-west-1","resources":[],"detail":{"UID":"$(uid)"}}' | sed 's/"/\\"/g'))
Expand Down Expand Up @@ -181,6 +181,15 @@ emit-lpa-updated-event: ##@events emits an lpa-updated event with the given chan
--function-name event-received text \
--payload '{"Records": [{"messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78", "body": "$(BODY)"}]}'

emit-priority-correspondence-sent: ##@events emits a priority-correspondence-sent event with the given LpaUID e.g. emit-priority-correspondence-sent uid=abc-123
$(eval BODY := $(shell echo '{"version":"0","id":"63eb7e5f-1f10-4744-bba9-e16d327c3b98","detail-type":"priority-correspondence-sent","source":"opg.poas.sirius","account":"653761790766","time":"2023-08-30T13:40:30Z","region":"eu-west-1","resources":[],"detail":{"uid":"$(uid)","sentDate":"2024-01-02T12:13:14.000006Z"}}' | sed 's/"/\\"/g'))

docker compose -f docker/docker-compose.yml exec localstack awslocal lambda invoke \
--endpoint-url=http://localhost:4566 \
--region eu-west-1 \
--function-name event-received text \
--payload '{"Records": [{"messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78", "body": "$(BODY)"}]}'

emit-object-tags-added-with-virus: ##@events emits a ObjectTagging:Put event with the given S3 key e.g. emit-object-tags-added-with-virus key=doc/key. Also ensures a tag with virus-scan-status exists on an existing object set to infected
docker compose -f docker/docker-compose.yml exec localstack awslocal s3api \
put-object-tagging --bucket evidence --key $(key) --tagging '{"TagSet": [{ "Key": "virus-scan-status", "Value": "infected" }]}'
Expand All @@ -205,12 +214,12 @@ emit-object-tags-added-without-virus: ##@events emits a ObjectTagging:Put event
set-uploads-clean: ##@events calls emit-object-tags-added-without-virus for all documents on a given lpa e.g. set-uploads-clean lpaId=abc
for k in $$(docker compose -f docker/docker-compose.yml exec localstack awslocal dynamodb --region eu-west-1 query --table-name lpas --key-condition-expression 'PK = :pk and begins_with(SK, :sk)' --expression-attribute-values '{":pk": {"S": "LPA#$(lpaId)"}, ":sk": {"S": "DOCUMENT#"}}' | jq -c -r '.Items[] | .Key[]'); do \
key=$$k $(MAKE) emit-object-tags-added-without-virus ; \
done
done

set-uploads-infected: ##@events calls emit-object-tags-added-with-virus for all documents on a given lpa e.g. set-uploads-clean lpaId=abc
for k in $$(docker compose -f docker/docker-compose.yml exec localstack awslocal dynamodb --region eu-west-1 query --table-name lpas --key-condition-expression 'PK = :pk and begins_with(SK, :sk)' --expression-attribute-values '{":pk": {"S": "LPA#$(lpaId)"}, ":sk": {"S": "DOCUMENT#"}}' | jq -c -r '.Items[] | .Key[]'); do \
key=$$k $(MAKE) emit-object-tags-added-with-virus ; \
done
done

tail-logs: ##@app tails logs for app mock-notify, mock-onelogin, mock-lpa-store, mock-uid and mock-pay
docker compose --ansi=always -f docker/docker-compose.yml -f docker/docker-compose.dev.yml logs app mock-notify mock-onelogin mock-lpa-store mock-uid mock-pay -f
Expand Down Expand Up @@ -246,9 +255,9 @@ endif

run-schedule-runner: ##@scheduler invokes the schedule-runner lambda
docker compose -f docker/docker-compose.yml exec localstack awslocal lambda invoke \
--endpoint-url=http://localhost:4566 \
--region eu-west-1 \
--function-name schedule-runner text
--endpoint-url=http://localhost:4566 \
--region eu-west-1 \
--function-name schedule-runner text

test-schedule-runner: add-scheduled-tasks run-schedule-runner ##@scheduler seeds scheduled tasks and runs the schedule-runner (defaults to 10 seeded tasks) e.g. test-schedule-runner count=100
docker compose -f docker/docker-compose.yml exec localstack awslocal cloudwatch get-metric-data \
Expand Down
28 changes: 28 additions & 0 deletions cmd/event-received/sirius_event_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@
case "certificate-provider-submission-completed":
return handleCertificateProviderSubmissionCompleted(ctx, cloudWatchEvent, factory)

case "priority-correspondence-sent":
return handlePriorityCorrespondenceSent(ctx, factory.DynamoClient(), cloudWatchEvent, factory.Now())

default:
return fmt.Errorf("unknown sirius event")
}
Expand Down Expand Up @@ -316,3 +319,28 @@

return nil
}

type priorityCorrespondenceSentEvent struct {
UID string `json:"uid"`
SentDate time.Time `json:"sentDate"`
}

func handlePriorityCorrespondenceSent(ctx context.Context, client dynamodbClient, event *events.CloudWatchEvent, now func() time.Time) error {
var v priorityCorrespondenceSentEvent
if err := json.Unmarshal(event.Detail, &v); err != nil {
return fmt.Errorf("failed to unmarshal detail: %w", err)
}

Check warning on line 332 in cmd/event-received/sirius_event_handler.go

View check run for this annotation

Codecov / codecov/patch

cmd/event-received/sirius_event_handler.go#L331-L332

Added lines #L331 - L332 were not covered by tests

donor, err := getDonorByLpaUID(ctx, client, v.UID)
if err != nil {
return fmt.Errorf("failed to get lpa: %w", err)
}

donor.PriorityCorrespondenceSentAt = v.SentDate

if err := putDonor(ctx, donor, now, client); err != nil {
return fmt.Errorf("failed to update lpa: %w", err)
}

return nil
}
74 changes: 74 additions & 0 deletions cmd/event-received/sirius_event_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1332,3 +1332,77 @@ func TestHandleCertificateProviderSubmissionCompletedWhenAppDataFactoryErrors(t
err := handler.Handle(ctx, factory, certificateProviderSubmissionCompletedEvent)
assert.Equal(t, expectedError, err)
}

func TestHandlePriorityCorrespondenceSent(t *testing.T) {
event := &events.CloudWatchEvent{
DetailType: "priority-correspondence-sent",
Detail: json.RawMessage(`{"uid":"M-1111-2222-3333","sentAt":"2024-01-18T00:00:00.000Z"}`),
}

updated := &donordata.Provided{PK: dynamo.LpaKey("123"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("456")), UpdatedAt: testNow}
updated.UpdateHash()

client := newMockDynamodbClient(t)
client.EXPECT().
OneByUID(ctx, "M-1111-2222-3333", mock.Anything).
Return(nil).
SetData(dynamo.Keys{PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("456")})
client.EXPECT().
One(ctx, dynamo.LpaKey("123"), dynamo.DonorKey("456"), mock.Anything).
Return(nil).
SetData(donordata.Provided{PK: dynamo.LpaKey("123"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("456"))})
client.EXPECT().
Put(ctx, updated).
Return(nil)

factory := newMockFactory(t)
factory.EXPECT().
DynamoClient().
Return(client)
factory.EXPECT().
Now().
Return(testNowFn)

handler := &siriusEventHandler{}
err := handler.Handle(ctx, factory, event)

assert.Nil(t, err)
}

func TestHandlePriorityCorrespondenceSentWhenGetError(t *testing.T) {
event := &events.CloudWatchEvent{
DetailType: "priority-correspondence-sent",
Detail: json.RawMessage(`{"uid":"M-1111-2222-3333","sentAt":"2024-01-18T00:00:00.000Z"}`),
}

client := newMockDynamodbClient(t)
client.EXPECT().
OneByUID(ctx, "M-1111-2222-3333", mock.Anything).
Return(expectedError)

err := handlePriorityCorrespondenceSent(ctx, client, event, testNowFn)
assert.ErrorIs(t, err, expectedError)
}

func TestHandlePriorityCorrespondenceSentWhenPutError(t *testing.T) {
event := &events.CloudWatchEvent{
DetailType: "priority-correspondence-sent",
Detail: json.RawMessage(`{"uid":"M-1111-2222-3333","sentAt":"2024-01-18T00:00:00.000Z"}`),
}

client := newMockDynamodbClient(t)
client.EXPECT().
OneByUID(ctx, "M-1111-2222-3333", mock.Anything).
Return(nil).
SetData(dynamo.Keys{PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("456")})
client.EXPECT().
One(ctx, dynamo.LpaKey("123"), dynamo.DonorKey("456"), mock.Anything).
Return(nil).
SetData(donordata.Provided{PK: dynamo.LpaKey("123"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("456"))})
client.EXPECT().
Put(ctx, mock.Anything).
Return(expectedError)

err := handlePriorityCorrespondenceSent(ctx, client, event, testNowFn)
assert.ErrorIs(t, err, expectedError)
}
12 changes: 8 additions & 4 deletions cypress/e2e/donor/you-cannot-sign-your-lpa-yet.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ describe('You cannot sign your LPA yet', () => {
cy.visit('/fixtures?redirect=/choose-attorneys-summary&progress=peopleToNotifyAboutYourLpa');

cy.contains('.govuk-summary-card', 'Jessie Jones').contains('a', 'Change').click();
cy.get('#f-date-of-birth-year').clear().type(today.getFullYear() - 1)
cy.get('#f-date-of-birth-year').clear();
cy.get('#f-date-of-birth-year').type(today.getFullYear() - 1);
cy.contains('button', 'Save and continue').click()
cy.contains('button', 'Save and continue').click()
cy.visitLpa('/choose-replacement-attorneys-summary')

cy.contains('.govuk-summary-card', 'Blake Buckley').contains('a', 'Change').click();
cy.get('#f-date-of-birth-year').clear().type(today.getFullYear() - 1)
cy.get('#f-date-of-birth-year').clear();
cy.get('#f-date-of-birth-year').type(today.getFullYear() - 1);
cy.contains('button', 'Save and continue').click()
cy.contains('button', 'Save and continue').click()
cy.contains('a', 'Return to task list').click()
Expand All @@ -22,14 +24,16 @@ describe('You cannot sign your LPA yet', () => {
cy.contains('.govuk-summary-list__row', 'Jessie Jones').contains('a', 'Change').click();

cy.url().should('contain', '/choose-attorneys')
cy.get('#f-date-of-birth-year').clear().type("2000")
cy.get('#f-date-of-birth-year').clear();
cy.get('#f-date-of-birth-year').type("2000");
cy.contains('button', 'Save and continue').click()
cy.url().should('contain', '/you-cannot-sign-your-lpa-yet')

cy.contains('.govuk-summary-list__row', 'Blake Buckley').contains('a', 'Change').click();

cy.url().should('contain', '/choose-replacement-attorneys')
cy.get('#f-date-of-birth-year').clear().type("2000")
cy.get('#f-date-of-birth-year').clear();
cy.get('#f-date-of-birth-year').type("2000");
cy.contains('button', 'Save and continue').click()
cy.url().should('contain', '/task-list')
});
Expand Down
7 changes: 6 additions & 1 deletion internal/donor/donordata/provided.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,14 @@ type Provided struct {
// VoucherInvitedAt records when the invite is sent to the voucher to vouch.
VoucherInvitedAt time.Time `checkhash:"-"`

// MoreEvidenceRequiredAt records when a request for further information on an exemption/remission was received.
// MoreEvidenceRequiredAt records when a request for further information on an
// exemption/remission was received.
MoreEvidenceRequiredAt time.Time `checkhash:"-"`

// PriorityCorrespondenceSentAt records when a caseworker sent a letter to the
// donor informing them of a problem.
PriorityCorrespondenceSentAt time.Time `checkhash:"-"`

// HasSeenSuccessfulVouchBanner records if the donor has seen the progress tracker successful vouch banner
HasSeenSuccessfulVouchBanner bool `checkhash:"-"`

Expand Down
4 changes: 2 additions & 2 deletions internal/donor/donordata/provided_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,14 @@ func TestGenerateHash(t *testing.T) {
}

// DO change this value to match the updates
const modified uint64 = 0xe3bd213ba882ed4f
const modified uint64 = 0x44d4ab7efe0264f8

// DO NOT change these initial hash values. If a field has been added/removed
// you will need to handle the version gracefully by modifying
// (*Provided).HashInclude and adding another testcase for the new
// version.
testcases := map[uint8]uint64{
0: 0x3730007cdf45a8cd,
0: 0x48b67658eed0ea5c,
}

for version, initial := range testcases {
Expand Down
10 changes: 10 additions & 0 deletions internal/donor/donorpage/progress.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,16 @@ func Progress(tmpl template.Template, lpaStoreResolvingService LpaStoreResolving
})
}

if !lpa.Status.IsRegistered() && !donor.PriorityCorrespondenceSentAt.IsZero() {
data.InfoNotifications = append(data.InfoNotifications, progressNotification{
Heading: appData.Localizer.T("thereIsAProblemWithYourLpa"),
Body: appData.Localizer.Format(
"weContactedYouOnWithGuidanceAboutWhatToDoNext",
map[string]any{"ContactedDate": appData.Localizer.FormatDate(donor.PriorityCorrespondenceSentAt)},
),
})
}

return tmpl(w, data)
}
}
26 changes: 26 additions & 0 deletions internal/donor/donorpage/progress_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,32 @@ func TestGetProgress(t *testing.T) {
return l
},
},
"priority correspondence sent": {
donor: &donordata.Provided{
PriorityCorrespondenceSentAt: testNow,
},
lpa: &lpadata.Lpa{},
setupCertificateProviderStore: certificateProviderStoreNotFound,
infoNotifications: []progressNotification{
{Heading: "H", Body: "B"},
},
setupLocalizer: func(t *testing.T) *mockLocalizer {
l := newMockLocalizer(t)
l.EXPECT().T("thereIsAProblemWithYourLpa").Return("H")
l.EXPECT().Format("weContactedYouOnWithGuidanceAboutWhatToDoNext", map[string]any{"ContactedDate": "translated date"}).Return("B")
l.EXPECT().FormatDate(testNow).Return("translated date")
return l
},
},
"priority correspondence sent registered": {
donor: &donordata.Provided{
PriorityCorrespondenceSentAt: testNow,
},
lpa: &lpadata.Lpa{
Status: lpadata.StatusRegistered,
},
setupCertificateProviderStore: certificateProviderStoreNotFound,
},
}

for name, tc := range testCases {
Expand Down