Skip to content

Commit

Permalink
Merge 6403b44 into e0254c9
Browse files Browse the repository at this point in the history
  • Loading branch information
acsauk authored Nov 1, 2023
2 parents e0254c9 + 6403b44 commit 1ed68e4
Show file tree
Hide file tree
Showing 45 changed files with 2,955 additions and 773 deletions.
12 changes: 6 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ get-lpa: ##@app dumps all entries in the lpas dynamodb table that are related t
docker compose -f docker/docker-compose.yml exec localstack awslocal dynamodb \
query --table-name lpas --key-condition-expression 'PK = :pk' --expression-attribute-values '{":pk": {"S": "LPA#$(id)"}}'

get-evidence: ##@app dumps all fee evidence in the lpas dynamodb table that are related to the LPA id supplied e.g. get-evidence id=abc-123
get-documents: ##@app dumps all documents in the lpas dynamodb table that are related to the LPA id supplied e.g. get-documents lpaId=abc-123
docker compose -f docker/docker-compose.yml exec localstack awslocal dynamodb \
query --table-name lpas --key-condition-expression 'PK = :pk' --expression-attribute-values '{":pk": {"S": "LPA#$(id)"}}' --projection-expression "Evidence"
query --table-name lpas --key-condition-expression 'PK = :pk and begins_with(SK, :sk)' --expression-attribute-values '{":pk": {"S": "LPA#$(lpaId)"}, ":sk": {"S": "#DOCUMENT#"}}'

emit-evidence-received: ##@app emits an evidence-received event with the given UID e.g. emit-evidence-received uid=abc-123
curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"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)"}}'
Expand All @@ -104,17 +104,17 @@ emit-fee-denied: ##@app emits a fee-denied event with the given UID e.g. emit-fe
emit-more-evidence-required: ##@app emits a more-evidence-required event with the given UID e.g. emit-more-evidence-required uid=abc-123
curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"version":"0","id":"63eb7e5f-1f10-4744-bba9-e16d327c3b98","detail-type":"more-evidence-required","source":"opg.poas.sirius","account":"653761790766","time":"2023-08-30T13:40:30Z","region":"eu-west-1","resources":[],"detail":{"UID":"$(uid)"}}'

emit-object-tags-added-with-virus: ##@app emits a Object Tags Added 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
emit-object-tags-added-with-virus: ##@app 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" }]}'

curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"version":"0","id":"63eb7e5f-1f10-4744-bba9-e16d327c3b98","detail-type":"Object Tags Added","source":"aws.s3","account":"653761790766","time":"2023-08-30T13:40:30Z","region":"eu-west-1","resources":[],"detail":{"object":{"key":"$(key)"}}}'
curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"Records":[{"eventSource":"aws:s3","eventTime":"2023-10-23T15:58:33.081Z","eventName":"ObjectTagging:Put","s3":{"bucket":{"name":"uploads-opg-modernising-lpa-eu-west-1"},"object":{"key":"$(key)"}}}]}'

emit-object-tags-added-without-virus: ##@app emits a Object Tags Added 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 ok
emit-object-tags-added-without-virus: ##@app 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 ok
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": "ok" }]}'

curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"version":"0","id":"63eb7e5f-1f10-4744-bba9-e16d327c3b98","detail-type":"Object Tags Added","source":"aws.s3","account":"653761790766","time":"2023-08-30T13:40:30Z","region":"eu-west-1","resources":[],"detail":{"object":{"key":"$(key)"}}}'
curl "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"Records":[{"eventSource":"aws:s3","eventTime":"2023-10-23T15:58:33.081Z","eventName":"ObjectTagging:Put","s3":{"bucket":{"name":"uploads-opg-modernising-lpa-eu-west-1"},"object":{"key":"$(key)"}}}]}'

logs: ##@app tails logs for all containers running
docker compose -f docker/docker-compose.yml -f docker/docker-compose.dev.yml logs -f
35 changes: 16 additions & 19 deletions cmd/event-received/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

type Event struct {
events.S3Event
events.CloudWatchEvent
Expand Down Expand Up @@ -110,6 +115,8 @@ func Handler(ctx context.Context, event Event) error {

notifyClient, err := notify.New(notifyIsProduction, notifyBaseURL, notifyApiKey, http.DefaultClient)

documentStore := app.NewDocumentStore(dynamoClient, s3Client)

bundle := localize.NewBundle("./lang/en.json", "./lang/cy.json")

//TODO do this in handleFeeApproved when/if we save lang preference in LPA
Expand All @@ -119,7 +126,7 @@ func Handler(ctx context.Context, event Event) error {
now := time.Now

if event.isS3Event() {
return handleObjectTagsAdded(ctx, dynamoClient, event.S3Event, now, s3Client)
return handleObjectTagsAdded(ctx, dynamoClient, event.S3Event, s3Client, documentStore)
}

if event.isCloudWatchEvent() {
Expand Down Expand Up @@ -230,7 +237,7 @@ func handleFeeDenied(ctx context.Context, client dynamodbClient, event events.Cl
return nil
}

func handleObjectTagsAdded(ctx context.Context, client dynamodbClient, event events.S3Event, now func() time.Time, s3Client s3Client) error {
func handleObjectTagsAdded(ctx context.Context, dynamodbClient dynamodbClient, event events.S3Event, s3Client s3Client, documentStore DocumentStore) error {
objectKey := event.Records[0].S3.Object.Key
if objectKey == "" {
return fmt.Errorf("object key missing in event in '%s'", objectTagsAddedEventName)
Expand All @@ -256,26 +263,16 @@ func handleObjectTagsAdded(ctx context.Context, client dynamodbClient, event eve
return nil
}

uid := strings.Split(objectKey, "/")

lpa, err := getLpaByUID(ctx, client, uid[0], objectTagsAddedEventName)
if err != nil {
return err
}
parts := strings.Split(objectKey, "/")

document := lpa.Evidence.Get(objectKey)
if document.Key == "" {
return fmt.Errorf("LPA did not contain a document with key %s for '%s'", objectKey, objectTagsAddedEventName)
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)
}

document.Scanned = now()
document.VirusDetected = hasVirus

lpa.Evidence.Put(document)
lpa.UpdatedAt = now()

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)
if err != nil {
return fmt.Errorf("failed to update scan results for '%s': %w", objectTagsAddedEventName, err)
}

return nil
Expand Down
147 changes: 63 additions & 84 deletions cmd/event-received/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,97 +334,98 @@ func TestHandleFeeDeniedWhenPutError(t *testing.T) {
}

func TestHandleObjectTagsAdded(t *testing.T) {
testCases := map[string]bool{
"ok": false,
"infected": true,
}

for scanResult, hasVirus := range testCases {
t.Run(scanResult, func(t *testing.T) {
event := Event{
S3Event: events.S3Event{Records: []events.S3EventRecord{
{S3: events.S3Entity{Object: events.S3Object{Key: "M-1111-2222-3333/evidence/a-uid"}}},
}},
}

s3Client := newMockS3Client(t)
s3Client.
On("GetObjectTags", ctx, "M-1111-2222-3333/evidence/a-uid").
Return([]types.Tag{
{Key: aws.String("virus-scan-status"), Value: aws.String(scanResult)},
}, nil)

dynamoClient := newMockDynamodbClient(t)
dynamoClient.
On("OneByUID", ctx, "M-1111-2222-3333", mock.Anything).
Return(func(ctx context.Context, uid string, v interface{}) error {
b, _ := json.Marshal(dynamo.Key{PK: "LPA#123", SK: "#DONOR#456"})
json.Unmarshal(b, v)
return nil
})

documentStore := newMockDocumentStore(t)
documentStore.
On("UpdateScanResults", ctx, "LPA#123", "#DOCUMENT#M-1111-2222-3333/evidence/a-uid", hasVirus).
Return(nil)

err := handleObjectTagsAdded(ctx, dynamoClient, event.S3Event, s3Client, documentStore)
assert.Nil(t, err)
})
}
}

func TestHandleObjectTagsAddedWhenScannedTagMissing(t *testing.T) {
event := Event{
S3Event: events.S3Event{Records: []events.S3EventRecord{
{S3: events.S3Entity{Object: events.S3Object{Key: "M-1111-2222-3333/evidence/a-uid"}}},
}},
}

now := time.Now()

s3Client := newMockS3Client(t)
s3Client.
On("GetObjectTags", ctx, "M-1111-2222-3333/evidence/a-uid").
Return([]types.Tag{
{Key: aws.String("virus-scan-status"), Value: aws.String("ok")},
{Key: aws.String("not-virus-scan-status"), Value: aws.String("ok")},
}, nil)

dynamoClient := newMockDynamodbClient(t)
dynamoClient.
On("OneByUID", ctx, "M-1111-2222-3333", mock.Anything).
Return(func(ctx context.Context, uid string, v interface{}) error {
b, _ := json.Marshal(dynamo.Key{PK: "LPA#123", SK: "#DONOR#456"})
json.Unmarshal(b, v)
return nil
})
dynamoClient.
On("One", ctx, "LPA#123", "#DONOR#456", mock.Anything).
Return(func(ctx context.Context, pk, sk string, v interface{}) error {
b, _ := json.Marshal(page.Lpa{PK: "LPA#123", SK: "#DONOR#456", Evidence: page.Evidence{
Documents: []page.Document{{Key: "M-1111-2222-3333/evidence/a-uid"}},
}})
json.Unmarshal(b, v)
return nil
})
dynamoClient.
On("Put", ctx, page.Lpa{PK: "LPA#123", SK: "#DONOR#456", UpdatedAt: now, Evidence: page.Evidence{
Documents: []page.Document{{Key: "M-1111-2222-3333/evidence/a-uid", Scanned: now, VirusDetected: false}},
}}).
Return(nil)

err := handleObjectTagsAdded(ctx, dynamoClient, event.S3Event, func() time.Time { return now }, s3Client)
err := handleObjectTagsAdded(ctx, nil, event.S3Event, s3Client, nil)
assert.Nil(t, err)
}

func TestHandleObjectTagsAddedWhenGetObjectTagsError(t *testing.T) {
func TestHandleObjectTagsAddedWhenObjectKeyMissing(t *testing.T) {
event := Event{
S3Event: events.S3Event{Records: []events.S3EventRecord{
{S3: events.S3Entity{Object: events.S3Object{Key: "M-1111-2222-3333/evidence/a-uid"}}},
{S3: events.S3Entity{Object: events.S3Object{}}},
}},
}

now := time.Now()

s3Client := newMockS3Client(t)
s3Client.
On("GetObjectTags", ctx, "M-1111-2222-3333/evidence/a-uid").
Return([]types.Tag{
{Key: aws.String("virus-scan-status"), Value: aws.String("ok")},
}, expectedError)

err := handleObjectTagsAdded(ctx, nil, event.S3Event, func() time.Time { return now }, s3Client)
assert.Equal(t, fmt.Errorf("failed to get tags for object in 'ObjectTagging:Put': %w", expectedError), err)
err := handleObjectTagsAdded(ctx, nil, event.S3Event, nil, nil)
assert.Equal(t, fmt.Errorf("object key missing in event in '%s'", objectTagsAddedEventName), err)
}

func TestHandleObjectTagsAddedWhenDoesNotContainVirusScanTag(t *testing.T) {
func TestHandleObjectTagsAddedWhenS3ClientGetObjectTagsError(t *testing.T) {
event := Event{
S3Event: events.S3Event{Records: []events.S3EventRecord{
{S3: events.S3Entity{Object: events.S3Object{Key: "M-1111-2222-3333/evidence/a-uid"}}},
}},
}

now := time.Now()

s3Client := newMockS3Client(t)
s3Client.
On("GetObjectTags", ctx, "M-1111-2222-3333/evidence/a-uid").
Return([]types.Tag{
{Key: aws.String("not-virus-scan-status")},
}, nil)
Return([]types.Tag{}, expectedError)

err := handleObjectTagsAdded(ctx, nil, event.S3Event, func() time.Time { return now }, s3Client)
assert.Nil(t, err)
err := handleObjectTagsAdded(ctx, nil, event.S3Event, s3Client, nil)
assert.Equal(t, fmt.Errorf("failed to get tags for object in '%s': %w", objectTagsAddedEventName, expectedError), err)
}

func TestHandleObjectTagsAddedWhenLpaEvidenceDoesNotContainDocument(t *testing.T) {
func TestHandleObjectTagsAddedWhenDynamoClientOneByUIDError(t *testing.T) {
event := Event{
S3Event: events.S3Event{Records: []events.S3EventRecord{
{S3: events.S3Entity{Object: events.S3Object{Key: "M-1111-2222-3333/evidence/a-uid"}}},
}},
}

now := time.Now()

s3Client := newMockS3Client(t)
s3Client.
On("GetObjectTags", ctx, "M-1111-2222-3333/evidence/a-uid").
Expand All @@ -438,31 +439,20 @@ func TestHandleObjectTagsAddedWhenLpaEvidenceDoesNotContainDocument(t *testing.T
Return(func(ctx context.Context, uid string, v interface{}) error {
b, _ := json.Marshal(dynamo.Key{PK: "LPA#123", SK: "#DONOR#456"})
json.Unmarshal(b, v)
return nil
})
dynamoClient.
On("One", ctx, "LPA#123", "#DONOR#456", mock.Anything).
Return(func(ctx context.Context, pk, sk string, v interface{}) error {
b, _ := json.Marshal(page.Lpa{PK: "LPA#123", SK: "#DONOR#456", Evidence: page.Evidence{
Documents: []page.Document{{Key: "M-1111-2222-3333/evidence/a-different-uid"}},
}})
json.Unmarshal(b, v)
return nil
return expectedError
})

err := handleObjectTagsAdded(ctx, dynamoClient, event.S3Event, func() time.Time { return now }, s3Client)
assert.Equal(t, fmt.Errorf("LPA did not contain a document with key %s for 'ObjectTagging:Put'", "M-1111-2222-3333/evidence/a-uid"), err)
err := handleObjectTagsAdded(ctx, dynamoClient, event.S3Event, s3Client, nil)
assert.Equal(t, fmt.Errorf("failed to resolve uid for '%s': %w", objectTagsAddedEventName, expectedError), err)
}

func TestHandleObjectTagsAddedWhenDynamoPutError(t *testing.T) {
func TestHandleObjectTagsAddedWhenDocumentStoreUpdateScanResultsError(t *testing.T) {
event := Event{
S3Event: events.S3Event{Records: []events.S3EventRecord{
{S3: events.S3Entity{Object: events.S3Object{Key: "M-1111-2222-3333/evidence/a-uid"}}},
}},
}

now := time.Now()

s3Client := newMockS3Client(t)
s3Client.
On("GetObjectTags", ctx, "M-1111-2222-3333/evidence/a-uid").
Expand All @@ -478,29 +468,18 @@ func TestHandleObjectTagsAddedWhenDynamoPutError(t *testing.T) {
json.Unmarshal(b, v)
return nil
})
dynamoClient.
On("One", ctx, "LPA#123", "#DONOR#456", mock.Anything).
Return(func(ctx context.Context, pk, sk string, v interface{}) error {
b, _ := json.Marshal(page.Lpa{PK: "LPA#123", SK: "#DONOR#456", Evidence: page.Evidence{
Documents: []page.Document{{Key: "M-1111-2222-3333/evidence/a-uid"}},
}})
json.Unmarshal(b, v)
return nil
})
dynamoClient.
On("Put", ctx, page.Lpa{PK: "LPA#123", SK: "#DONOR#456", UpdatedAt: now, Evidence: page.Evidence{
Documents: []page.Document{{Key: "M-1111-2222-3333/evidence/a-uid", Scanned: now, VirusDetected: false}},
}}).

documentStore := newMockDocumentStore(t)
documentStore.
On("UpdateScanResults", ctx, "LPA#123", "#DOCUMENT#M-1111-2222-3333/evidence/a-uid", false).
Return(expectedError)

err := handleObjectTagsAdded(ctx, dynamoClient, event.S3Event, func() time.Time { return now }, s3Client)
assert.Equal(t, fmt.Errorf("failed to update LPA for 'ObjectTagging:Put': %w", expectedError), err)
err := handleObjectTagsAdded(ctx, dynamoClient, event.S3Event, s3Client, documentStore)
assert.Equal(t, fmt.Errorf("failed to update scan results for '%s': %w", objectTagsAddedEventName, expectedError), err)
}

func TestGetLpaByUID(t *testing.T) {
expectedLpa := page.Lpa{PK: "LPA#123", SK: "#DONOR#456", Evidence: page.Evidence{
Documents: []page.Document{{Key: "document/key"}},
}}
expectedLpa := page.Lpa{PK: "LPA#123", SK: "#DONOR#456"}

client := newMockDynamodbClient(t)
client.
Expand Down
43 changes: 43 additions & 0 deletions cmd/event-received/mock_DocumentStore_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 1ed68e4

Please sign in to comment.