Skip to content

Commit

Permalink
MLPAB-1777 Show when notices of intent to register have been sent (#1201
Browse files Browse the repository at this point in the history
)
  • Loading branch information
hawx authored Apr 29, 2024
1 parent 05a5c07 commit d8504bf
Show file tree
Hide file tree
Showing 24 changed files with 1,051 additions and 380 deletions.
13 changes: 13 additions & 0 deletions cmd/event-received/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ type UidClient interface {

type Factory struct {
now func() time.Time
uuidString func() string
cfg aws.Config
dynamoClient dynamodbClient
appPublicURL string
Expand All @@ -73,6 +74,18 @@ type Factory struct {
uidClient UidClient
}

func (f *Factory) Now() func() time.Time {
return f.now
}

func (f *Factory) DynamoClient() dynamodbClient {
return f.dynamoClient
}

func (f *Factory) UuidString() func() string {
return f.uuidString
}

func (f *Factory) AppData() (page.AppData, error) {
if f.appData == nil {
bundle, err := localize.NewBundle("./lang/en.json", "./lang/cy.json")
Expand Down
19 changes: 19 additions & 0 deletions cmd/event-received/factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,25 @@ import (
"github.com/stretchr/testify/assert"
)

func TestNow(t *testing.T) {
factory := &Factory{now: testNowFn}

assert.Equal(t, testNow, factory.Now()())
}

func TestDynamoClient(t *testing.T) {
dynamoClient := newMockDynamodbClient(t)
factory := &Factory{dynamoClient: dynamoClient}

assert.Equal(t, dynamoClient, factory.DynamoClient())
}

func TestUuidString(t *testing.T) {
factory := &Factory{uuidString: testUuidStringFn}

assert.Equal(t, testUuidString, factory.UuidString()())
}

func TestAppData(t *testing.T) {
factory := &Factory{}

Expand Down
51 changes: 51 additions & 0 deletions cmd/event-received/lpastore_event_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package main

import (
"context"
"encoding/json"
"fmt"
"time"

"github.com/aws/aws-lambda-go/events"
)

type lpastoreEventHandler struct{}

func (h *lpastoreEventHandler) Handle(ctx context.Context, factory factory, cloudWatchEvent events.CloudWatchEvent) error {
switch cloudWatchEvent.DetailType {
case "lpa-updated":
return handleLpaUpdated(ctx, factory.DynamoClient(), cloudWatchEvent, factory.Now())

default:
return fmt.Errorf("unknown lpastore event")
}
}

type lpaUpdatedEvent struct {
UID string `json:"uid"`
ChangeType string `json:"changeType"`
}

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

if v.ChangeType != "PERFECT" {
return nil
}

donor, err := getDonorByLpaUID(ctx, client, v.UID)
if err != nil {
return err
}

donor.PerfectAt = now()

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

return nil
}
152 changes: 152 additions & 0 deletions cmd/event-received/lpastore_event_handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package main

import (
"context"
"encoding/json"
"fmt"
"testing"

"github.com/aws/aws-lambda-go/events"
"github.com/ministryofjustice/opg-modernising-lpa/internal/actor"
"github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

func TestLpaStoreEventHandlerHandleUnknownEvent(t *testing.T) {
handler := &lpastoreEventHandler{}

err := handler.Handle(ctx, nil, events.CloudWatchEvent{DetailType: "some-event"})
assert.Equal(t, fmt.Errorf("unknown lpastore event"), err)
}

func TestLpaStoreEventHandlerHandleLpaUpdated(t *testing.T) {
event := events.CloudWatchEvent{
DetailType: "lpa-updated",
Detail: json.RawMessage(`{"uid":"M-1111-2222-3333","changeType":"PERFECT"}`),
}

updated := &actor.DonorProvidedDetails{
PK: dynamo.LpaKey("123"),
SK: dynamo.LpaOwnerKey(dynamo.DonorKey("456")),
PerfectAt: testNow,
UpdatedAt: testNow,
}
updated.Hash, _ = updated.GenerateHash()

client := newMockDynamodbClient(t)
client.
On("OneByUID", ctx, "M-1111-2222-3333", mock.Anything).
Return(func(ctx context.Context, uid string, v interface{}) error {
b, _ := json.Marshal(dynamo.Keys{PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("456")})
json.Unmarshal(b, v)
return nil
})
client.
On("One", ctx, dynamo.LpaKey("123"), dynamo.DonorKey("456"), mock.Anything).
Return(func(ctx context.Context, pk dynamo.PK, sk dynamo.SK, v interface{}) error {
b, _ := json.Marshal(actor.DonorProvidedDetails{PK: dynamo.LpaKey("123"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("456"))})
json.Unmarshal(b, v)
return nil
})
client.EXPECT().
Put(ctx, updated).
Return(nil)

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

handler := &lpastoreEventHandler{}

err := handler.Handle(ctx, factory, event)
assert.Nil(t, err)
}

func TestLpaStoreEventHandlerHandleLpaUpdatedWhenChangeTypeNotPerfect(t *testing.T) {
event := events.CloudWatchEvent{
DetailType: "lpa-updated",
Detail: json.RawMessage(`{"uid":"M-1111-2222-3333","changeType":"WHAT"}`),
}

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

handler := &lpastoreEventHandler{}

err := handler.Handle(ctx, factory, event)
assert.Nil(t, err)
}

func TestLpaStoreEventHandlerHandleLpaUpdatedWhenDynamoGetErrors(t *testing.T) {
event := events.CloudWatchEvent{
DetailType: "lpa-updated",
Detail: json.RawMessage(`{"uid":"M-1111-2222-3333","changeType":"PERFECT"}`),
}

updated := &actor.DonorProvidedDetails{
PK: dynamo.LpaKey("123"),
SK: dynamo.LpaOwnerKey(dynamo.DonorKey("456")),
PerfectAt: testNow,
UpdatedAt: testNow,
}
updated.Hash, _ = updated.GenerateHash()

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

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

handler := &lpastoreEventHandler{}

err := handler.Handle(ctx, factory, event)
assert.ErrorIs(t, err, expectedError)
}

func TestLpaStoreEventHandlerHandleLpaUpdatedWhenDynamoPutErrors(t *testing.T) {
event := events.CloudWatchEvent{
DetailType: "lpa-updated",
Detail: json.RawMessage(`{"uid":"M-1111-2222-3333","changeType":"PERFECT"}`),
}

updated := &actor.DonorProvidedDetails{
PK: dynamo.LpaKey("123"),
SK: dynamo.LpaOwnerKey(dynamo.DonorKey("456")),
PerfectAt: testNow,
UpdatedAt: testNow,
}
updated.Hash, _ = updated.GenerateHash()

client := newMockDynamodbClient(t)
client.
On("OneByUID", ctx, "M-1111-2222-3333", mock.Anything).
Return(func(ctx context.Context, uid string, v interface{}) error {
b, _ := json.Marshal(dynamo.Keys{PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("456")})
json.Unmarshal(b, v)
return nil
})
client.
On("One", ctx, dynamo.LpaKey("123"), dynamo.DonorKey("456"), mock.Anything).
Return(func(ctx context.Context, pk dynamo.PK, sk dynamo.SK, v interface{}) error {
b, _ := json.Marshal(actor.DonorProvidedDetails{PK: dynamo.LpaKey("123"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("456"))})
json.Unmarshal(b, v)
return nil
})
client.EXPECT().
Put(ctx, updated).
Return(expectedError)

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

handler := &lpastoreEventHandler{}

err := handler.Handle(ctx, factory, event)
assert.ErrorIs(t, err, expectedError)
}
80 changes: 49 additions & 31 deletions cmd/event-received/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,30 @@ import (
"github.com/aws/aws-sdk-go-v2/service/s3/types"
"github.com/ministryofjustice/opg-modernising-lpa/internal/app"
"github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo"
"github.com/ministryofjustice/opg-modernising-lpa/internal/page"
"github.com/ministryofjustice/opg-modernising-lpa/internal/random"
"github.com/ministryofjustice/opg-modernising-lpa/internal/s3"
)

const (
virusFound = "infected"
)

type factory interface {
Now() func() time.Time
DynamoClient() dynamodbClient
UuidString() func() string
AppData() (page.AppData, error)
ShareCodeSender(ctx context.Context) (ShareCodeSender, error)
LpaStoreClient() (LpaStoreClient, error)
UidStore() (UidStore, error)
UidClient() UidClient
}

type Handler interface {
Handle(context.Context, factory, events.CloudWatchEvent) error
}

type uidEvent struct {
UID string `json:"uid"`
}
Expand Down Expand Up @@ -55,10 +72,6 @@ func (e Event) isS3Event() bool {
return len(e.Records) > 0
}

func (e Event) isCloudWatchEvent() bool {
return e.Source == "aws.cloudwatch" || e.Source == "opg.poas.makeregister" || e.Source == "opg.poas.sirius"
}

func handler(ctx context.Context, event Event) error {
var (
tableName = os.Getenv("LPAS_TABLE")
Expand Down Expand Up @@ -100,38 +113,43 @@ func handler(ctx context.Context, event Event) error {
return nil
}

if event.isCloudWatchEvent() {
factory := &Factory{
now: time.Now,
cfg: cfg,
dynamoClient: dynamoClient,
appPublicURL: appPublicURL,
lpaStoreBaseURL: lpaStoreBaseURL,
uidBaseURL: uidBaseURL,
notifyBaseURL: notifyBaseURL,
notifyIsProduction: notifyIsProduction,
eventBusName: eventBusName,
searchEndpoint: searchEndpoint,
searchIndexName: searchIndexName,
searchIndexingEnabled: searchIndexingEnabled,
}
factory := &Factory{
now: time.Now,
uuidString: random.UuidString,
cfg: cfg,
dynamoClient: dynamoClient,
appPublicURL: appPublicURL,
lpaStoreBaseURL: lpaStoreBaseURL,
uidBaseURL: uidBaseURL,
notifyBaseURL: notifyBaseURL,
notifyIsProduction: notifyIsProduction,
eventBusName: eventBusName,
searchEndpoint: searchEndpoint,
searchIndexName: searchIndexName,
searchIndexingEnabled: searchIndexingEnabled,
}

handler := &cloudWatchEventHandler{
dynamoClient: dynamoClient,
now: time.Now,
factory: factory,
}
var handler Handler
switch event.Source {
case "opg.poas.sirius":
handler = &siriusEventHandler{}
case "opg.poas.makeregister":
handler = &makeregisterEventHandler{}
case "opg.poas.lpastore":
handler = &lpastoreEventHandler{}
}

if err := handler.Handle(ctx, event.CloudWatchEvent); err != nil {
return fmt.Errorf("%s: %w", event.DetailType, err)
}
if handler == nil {
eJson, _ := json.Marshal(event)
return fmt.Errorf("unknown event received: %s", string(eJson))
}

log.Println("successfully handled ", event.DetailType)
return nil
if err := handler.Handle(ctx, factory, event.CloudWatchEvent); err != nil {
return fmt.Errorf("%s: %w", event.DetailType, err)
}

eJson, _ := json.Marshal(event)
return fmt.Errorf("unknown event type received: %s", string(eJson))
log.Println("successfully handled ", event.DetailType)
return nil
}

func main() {
Expand Down
14 changes: 0 additions & 14 deletions cmd/event-received/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,3 @@ func TestIsS3Event(t *testing.T) {

assert.False(t, s3Event.isS3Event())
}

func TestIsCloudWatchEvent(t *testing.T) {
cloudwatchEvents := []Event{
{CloudWatchEvent: events.CloudWatchEvent{Source: "aws.cloudwatch"}},
{CloudWatchEvent: events.CloudWatchEvent{Source: "opg.poas.makeregister"}},
{CloudWatchEvent: events.CloudWatchEvent{Source: "opg.poas.sirius"}},
}

for _, e := range cloudwatchEvents {
assert.True(t, e.isCloudWatchEvent())
}

assert.False(t, Event{CloudWatchEvent: events.CloudWatchEvent{Source: "somewhere else"}}.isCloudWatchEvent())
}
Loading

0 comments on commit d8504bf

Please sign in to comment.