-
Notifications
You must be signed in to change notification settings - Fork 2
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-1473: Add holding modal while reduced fee evidence documents are scanned #805
Conversation
Codecov ReportAttention:
📢 Thoughts on this report? Let us know!. |
PR Environment Terraform Plan SummaryPlan: 1 to add, 4 to change, 1 to destroy |
@@ -840,5 +844,9 @@ | |||
"lpaWithdrawn": "LPA withdrawn", | |||
"youHaveWithdrawnLpaNumber": "You have withdrawn LPA number <span class=\"govuk-!-font-weight-bold\">{{.UID}}</span>.", | |||
"opgWillNowContactAnyoneWhoHasAlreadyBeenContacted": "OPG will now contact anyone who has already been contacted in relation to your LPA and notify them of your decision to withdraw.", | |||
"withdrawn": "Withdrawn" | |||
"withdrawn": "Withdrawn", | |||
"yourFilesAreUploading": "Your files are uploading", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missed this - it will make more sense to have the modal appear when clicking upload files rather than continue.
I also think it would be nice if it were stored in separate rows, I think you'd be able to simplify the polling then instead of getting the Lpa each time |
cmd/event-received/main.go
Outdated
@@ -55,6 +55,11 @@ type shareCodeSender interface { | |||
SendCertificateProvider(context.Context, notify.Template, page.AppData, bool, *page.Lpa) error | |||
} | |||
|
|||
//go:generate mockery --testonly --inpackage --name DocumentStore --structname mockDocumentStore | |||
type DocumentStore interface { | |||
UpdateScanResults(ctx context.Context, PK, SK string, virusDetected bool) error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
keep pk, sk
lowercase
cmd/event-received/main.go
Outdated
|
||
if err := client.Put(ctx, lpa); err != nil { | ||
return fmt.Errorf("failed to update LPA for '%s': %w", objectTagsAddedEventName, err) | ||
err = documentStore.UpdateScanResults(ctx, lpaKey.PK, "#DOCUMENT#"+objectKey, hasVirus) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good to push #DOCUMENT#
into the store, so outside only has to deal with IDs and not how it is stored in dynamo. And the same with changing PK
to be the Lpa ID (even though it is awkward)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Much simpler - I was going around in circles with circular dependencies but makes more sense to hide the detail.
internal/app/app.go
Outdated
@@ -49,13 +50,16 @@ type DynamoClient interface { | |||
Put(ctx context.Context, v interface{}) error | |||
Create(ctx context.Context, v interface{}) error | |||
DeleteKeys(ctx context.Context, keys []dynamo.Key) error | |||
DeleteOne(ctx context.Context, key dynamo.Key) error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe I should have made it DeleteKeys(ctx context.Context, keys ...dynamo.Key)
internal/app/app.go
Outdated
@@ -49,13 +50,16 @@ type DynamoClient interface { | |||
Put(ctx context.Context, v interface{}) error | |||
Create(ctx context.Context, v interface{}) error | |||
DeleteKeys(ctx context.Context, keys []dynamo.Key) error | |||
DeleteOne(ctx context.Context, key dynamo.Key) error | |||
Update(ctx context.Context, input *dynamodb.UpdateItemInput) error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about exposing the dynamodb
type here, we avoid it in the other signatures. Would Update(ctx context.Context, pk, sk string, values map[string]types.AttributeValue, expression string) error
be any nicer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That works for me - slightly less boilerplate code to be passing in.
internal/app/document_store.go
Outdated
} | ||
|
||
func NewDocumentStore(dynamoClient DynamoClient, s3Client S3Client) DocumentStore { | ||
return documentStore{dynamoClient: dynamoClient, s3Client: s3Client} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably makes sense to have this return &documentStore
and change all the receivers to (s *documentStore)
internal/app/donor_store.go
Outdated
if document.Sent.IsZero() && !document.Scanned { | ||
document.Sent = s.now() | ||
if err := s.documentStore.Put(ctx, document, nil); err != nil { | ||
return err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't be returning errors from here. It should just fail, log, and retry next time
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also would be nice to batch these changes to documents up (but don't worry if it ends up being too much)
var updatedAtTime time.Time | ||
err = attributevalue.Unmarshal(updatedAt, &updatedAtTime) | ||
// Tracking Value equality against data on write allows for optimistic locking | ||
if currentVersion, exists := item["Version"]; exists { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we still need this if documents are split out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there's still a (admittedly small) chance that an LPA could be updated at the same time by a donor and an event coming in. Maybe we could: look at seeing if there's a way to restructure all event data persistence to rows to be safe or stick with optimistic locking OR remove and monitor once we go live 🤷
internal/page/data.go
Outdated
@@ -177,56 +177,101 @@ type Lpa struct { | |||
HasSentPreviousApplicationLinkedEvent bool | |||
} | |||
|
|||
type Evidence struct { | |||
Documents []Document | |||
type Document struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could all the document related stuff live in a new file
for i, evidence := range lpa.Evidence.Documents { | ||
if evidence.Sent.IsZero() { | ||
err := evidenceS3Client.PutObjectTagging(r.Context(), evidence.Key, []types.Tag{ | ||
documents, err := documentStore.GetAll(r.Context()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need all of this since it happens in payHelper.Pay
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does, but not for HalfFee
- we wanted to be sure that the payment had been successful before tagging the docs as being ready for replication hence the replication.
Filename: file.Filename, | ||
Key: key, | ||
} | ||
if err := documentStore.Put(r.Context(), document, file.Data); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it would be nice to provide a documentStore.Create(ctx, lpa.ID, key, file.Filename)
method, then you won't have LPA#
and #DOCUMENT#
exposed here
var lpaKey dynamo.Key | ||
if err := dynamodbClient.OneByUID(ctx, parts[0], &lpaKey); err != nil { | ||
return fmt.Errorf("failed to resolve uid for '%s': %w", objectTagsAddedEventName, err) | ||
lpa, err := getLpaByUID(ctx, dynamodbClient, parts[0], objectTagsAddedEventName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it worth introducing another dynamo call vs _, lpaID, _ := strings.Cut(lpaKey.PK, "#")
} | ||
|
||
return []page.Document{}, err | ||
if err := s.dynamoClient.AllByPartialSk(ctx, lpaKey(data.LpaID), documentKey(""), &ds); err != nil && !errors.Is(err, dynamo.NotFoundError{}) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hadn't thought before, but AllByPartialSk
doesn't return dynamo.NotFoundError
, we only do that in the One...
methods
return s.dynamoClient.Update(ctx, input) | ||
} | ||
toWrite := len(converted) | ||
written, err := s.dynamoClient.BatchPut(ctx, converted) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I shouldn't have mentioned this, it was fine before. For batch stuff I think TransactWriteItem
is better because it either works or fails
Purpose
Adds a holding modal that initiates a SSE connection to monitor when files are scanned.
I also had to add some optimistic locking to dynamo PUTs as the virus scan results were sometimes arriving within milliseconds creating a race condition that was overwriting data.
I've added 1 automatic retry to the lambda to try to mitigate this where we can but we'll need to see how this works to see if we need to rethink how we save the data (maybe to separate rows like theI've moved what was calledevidence-received
event?)Evidence
on theLpa
intoDocuments
on individual rows to account for this.I also found the spinner component in the Home Office design system but it felt like overkill to install the whole design system for what is a few lines of CSS.
Fixes MLPAB-1473