diff --git a/cmd/event-received/factory.go b/cmd/event-received/factory.go index b0a15159a0..6f1dfdd9b3 100644 --- a/cmd/event-received/factory.go +++ b/cmd/event-received/factory.go @@ -51,6 +51,7 @@ type UidClient interface { type Factory struct { now func() time.Time + uuidString func() string cfg aws.Config dynamoClient dynamodbClient appPublicURL string @@ -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") diff --git a/cmd/event-received/factory_test.go b/cmd/event-received/factory_test.go index 3076e593f5..80003b7eee 100644 --- a/cmd/event-received/factory_test.go +++ b/cmd/event-received/factory_test.go @@ -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{} diff --git a/cmd/event-received/lpastore_event_handler.go b/cmd/event-received/lpastore_event_handler.go new file mode 100644 index 0000000000..d9ee0a9d48 --- /dev/null +++ b/cmd/event-received/lpastore_event_handler.go @@ -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 +} diff --git a/cmd/event-received/lpastore_event_handler_test.go b/cmd/event-received/lpastore_event_handler_test.go new file mode 100644 index 0000000000..82fd0b73a2 --- /dev/null +++ b/cmd/event-received/lpastore_event_handler_test.go @@ -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) +} diff --git a/cmd/event-received/main.go b/cmd/event-received/main.go index 9149733c6c..5b41534409 100644 --- a/cmd/event-received/main.go +++ b/cmd/event-received/main.go @@ -17,6 +17,8 @@ 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" ) @@ -24,6 +26,21 @@ 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"` } @@ -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") @@ -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() { diff --git a/cmd/event-received/main_test.go b/cmd/event-received/main_test.go index 19fcf4395c..9334155549 100644 --- a/cmd/event-received/main_test.go +++ b/cmd/event-received/main_test.go @@ -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()) -} diff --git a/cmd/event-received/makeregister_event_handler.go b/cmd/event-received/makeregister_event_handler.go new file mode 100644 index 0000000000..fdf88f3de2 --- /dev/null +++ b/cmd/event-received/makeregister_event_handler.go @@ -0,0 +1,48 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/aws/aws-lambda-go/events" + "github.com/ministryofjustice/opg-modernising-lpa/internal/event" + "github.com/ministryofjustice/opg-modernising-lpa/internal/uid" +) + +type makeregisterEventHandler struct{} + +func (h *makeregisterEventHandler) Handle(ctx context.Context, factory factory, cloudWatchEvent events.CloudWatchEvent) error { + switch cloudWatchEvent.DetailType { + case "uid-requested": + uidStore, err := factory.UidStore() + if err != nil { + return err + } + + uidClient := factory.UidClient() + + return handleUidRequested(ctx, uidStore, uidClient, cloudWatchEvent) + + default: + return fmt.Errorf("unknown makeregister event") + } +} + +func handleUidRequested(ctx context.Context, uidStore UidStore, uidClient UidClient, e events.CloudWatchEvent) error { + var v event.UidRequested + if err := json.Unmarshal(e.Detail, &v); err != nil { + return fmt.Errorf("failed to unmarshal detail: %w", err) + } + + uid, err := uidClient.CreateCase(ctx, &uid.CreateCaseRequestBody{Type: v.Type, Donor: v.Donor}) + if err != nil { + return fmt.Errorf("failed to create case: %w", err) + } + + if err := uidStore.Set(ctx, v.LpaID, v.DonorSessionID, v.OrganisationID, uid); err != nil { + return fmt.Errorf("failed to set uid: %w", err) + } + + return nil +} diff --git a/cmd/event-received/makeregister_event_handler_test.go b/cmd/event-received/makeregister_event_handler_test.go new file mode 100644 index 0000000000..50ffcc05b8 --- /dev/null +++ b/cmd/event-received/makeregister_event_handler_test.go @@ -0,0 +1,82 @@ +package main + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/aws/aws-lambda-go/events" + "github.com/ministryofjustice/opg-modernising-lpa/internal/date" + "github.com/ministryofjustice/opg-modernising-lpa/internal/uid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestMakeRegisterHandlerHandleUnknownEvent(t *testing.T) { + handler := &makeregisterEventHandler{} + + err := handler.Handle(ctx, nil, events.CloudWatchEvent{DetailType: "some-event"}) + assert.Equal(t, fmt.Errorf("unknown makeregister event"), err) +} + +func TestHandleUidRequested(t *testing.T) { + event := events.CloudWatchEvent{ + DetailType: "uid-requested", + Detail: json.RawMessage(`{"lpaID":"an-id","donorSessionID":"donor-id","organisationID":"org-id","type":"personal-welfare","donor":{"name":"a donor","dob":"2000-01-02","postcode":"F1 1FF"}}`), + } + + uidClient := newMockUidClient(t) + uidClient.EXPECT(). + CreateCase(ctx, &uid.CreateCaseRequestBody{ + Type: "personal-welfare", + Donor: uid.DonorDetails{ + Name: "a donor", + Dob: date.New("2000", "01", "02"), + Postcode: "F1 1FF", + }, + }). + Return("M-1111-2222-3333", nil) + + uidStore := newMockUidStore(t) + uidStore.EXPECT(). + Set(ctx, "an-id", "donor-id", "org-id", "M-1111-2222-3333"). + Return(nil) + + err := handleUidRequested(ctx, uidStore, uidClient, event) + assert.Nil(t, err) +} + +func TestHandleUidRequestedWhenUidClientErrors(t *testing.T) { + event := events.CloudWatchEvent{ + DetailType: "uid-requested", + Detail: json.RawMessage(`{"lpaID":"an-id","donorSessionID":"donor-id","type":"personal-welfare","donor":{"name":"a donor","dob":"2000-01-02","postcode":"F1 1FF"}}`), + } + + uidClient := newMockUidClient(t) + uidClient.EXPECT(). + CreateCase(ctx, mock.Anything). + Return("", expectedError) + + err := handleUidRequested(ctx, nil, uidClient, event) + assert.Equal(t, fmt.Errorf("failed to create case: %w", expectedError), err) +} + +func TestHandleUidRequestedWhenUidStoreErrors(t *testing.T) { + event := events.CloudWatchEvent{ + DetailType: "uid-requested", + Detail: json.RawMessage(`{"lpaID":"an-id","donorSessionID":"donor-id","type":"personal-welfare","donor":{"name":"a donor","dob":"2000-01-02","postcode":"F1 1FF"}}`), + } + + uidClient := newMockUidClient(t) + uidClient.EXPECT(). + CreateCase(ctx, mock.Anything). + Return("M-1111-2222-3333", nil) + + uidStore := newMockUidStore(t) + uidStore.EXPECT(). + Set(ctx, "an-id", "donor-id", "", "M-1111-2222-3333"). + Return(expectedError) + + err := handleUidRequested(ctx, uidStore, uidClient, event) + assert.Equal(t, fmt.Errorf("failed to set uid: %w", expectedError), err) +} diff --git a/cmd/event-received/mock_Handler_test.go b/cmd/event-received/mock_Handler_test.go new file mode 100644 index 0000000000..0577e082e0 --- /dev/null +++ b/cmd/event-received/mock_Handler_test.go @@ -0,0 +1,85 @@ +// Code generated by mockery v2.42.2. DO NOT EDIT. + +package main + +import ( + context "context" + + events "github.com/aws/aws-lambda-go/events" + mock "github.com/stretchr/testify/mock" +) + +// mockHandler is an autogenerated mock type for the Handler type +type mockHandler struct { + mock.Mock +} + +type mockHandler_Expecter struct { + mock *mock.Mock +} + +func (_m *mockHandler) EXPECT() *mockHandler_Expecter { + return &mockHandler_Expecter{mock: &_m.Mock} +} + +// Handle provides a mock function with given fields: _a0, _a1, _a2 +func (_m *mockHandler) Handle(_a0 context.Context, _a1 factory, _a2 events.CloudWatchEvent) error { + ret := _m.Called(_a0, _a1, _a2) + + if len(ret) == 0 { + panic("no return value specified for Handle") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, factory, events.CloudWatchEvent) error); ok { + r0 = rf(_a0, _a1, _a2) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockHandler_Handle_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Handle' +type mockHandler_Handle_Call struct { + *mock.Call +} + +// Handle is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 factory +// - _a2 events.CloudWatchEvent +func (_e *mockHandler_Expecter) Handle(_a0 interface{}, _a1 interface{}, _a2 interface{}) *mockHandler_Handle_Call { + return &mockHandler_Handle_Call{Call: _e.mock.On("Handle", _a0, _a1, _a2)} +} + +func (_c *mockHandler_Handle_Call) Run(run func(_a0 context.Context, _a1 factory, _a2 events.CloudWatchEvent)) *mockHandler_Handle_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(factory), args[2].(events.CloudWatchEvent)) + }) + return _c +} + +func (_c *mockHandler_Handle_Call) Return(_a0 error) *mockHandler_Handle_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockHandler_Handle_Call) RunAndReturn(run func(context.Context, factory, events.CloudWatchEvent) error) *mockHandler_Handle_Call { + _c.Call.Return(run) + return _c +} + +// newMockHandler creates a new instance of mockHandler. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func newMockHandler(t interface { + mock.TestingT + Cleanup(func()) +}) *mockHandler { + mock := &mockHandler{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/cmd/event-received/mock_factory_test.go b/cmd/event-received/mock_factory_test.go index 63e30052c9..040d9a5510 100644 --- a/cmd/event-received/mock_factory_test.go +++ b/cmd/event-received/mock_factory_test.go @@ -7,6 +7,8 @@ import ( page "github.com/ministryofjustice/opg-modernising-lpa/internal/page" mock "github.com/stretchr/testify/mock" + + time "time" ) // mockFactory is an autogenerated mock type for the factory type @@ -77,6 +79,53 @@ func (_c *mockFactory_AppData_Call) RunAndReturn(run func() (page.AppData, error return _c } +// DynamoClient provides a mock function with given fields: +func (_m *mockFactory) DynamoClient() dynamodbClient { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for DynamoClient") + } + + var r0 dynamodbClient + if rf, ok := ret.Get(0).(func() dynamodbClient); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(dynamodbClient) + } + } + + return r0 +} + +// mockFactory_DynamoClient_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'DynamoClient' +type mockFactory_DynamoClient_Call struct { + *mock.Call +} + +// DynamoClient is a helper method to define mock.On call +func (_e *mockFactory_Expecter) DynamoClient() *mockFactory_DynamoClient_Call { + return &mockFactory_DynamoClient_Call{Call: _e.mock.On("DynamoClient")} +} + +func (_c *mockFactory_DynamoClient_Call) Run(run func()) *mockFactory_DynamoClient_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockFactory_DynamoClient_Call) Return(_a0 dynamodbClient) *mockFactory_DynamoClient_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockFactory_DynamoClient_Call) RunAndReturn(run func() dynamodbClient) *mockFactory_DynamoClient_Call { + _c.Call.Return(run) + return _c +} + // LpaStoreClient provides a mock function with given fields: func (_m *mockFactory) LpaStoreClient() (LpaStoreClient, error) { ret := _m.Called() @@ -134,6 +183,53 @@ func (_c *mockFactory_LpaStoreClient_Call) RunAndReturn(run func() (LpaStoreClie return _c } +// Now provides a mock function with given fields: +func (_m *mockFactory) Now() func() time.Time { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Now") + } + + var r0 func() time.Time + if rf, ok := ret.Get(0).(func() func() time.Time); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(func() time.Time) + } + } + + return r0 +} + +// mockFactory_Now_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Now' +type mockFactory_Now_Call struct { + *mock.Call +} + +// Now is a helper method to define mock.On call +func (_e *mockFactory_Expecter) Now() *mockFactory_Now_Call { + return &mockFactory_Now_Call{Call: _e.mock.On("Now")} +} + +func (_c *mockFactory_Now_Call) Run(run func()) *mockFactory_Now_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockFactory_Now_Call) Return(_a0 func() time.Time) *mockFactory_Now_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockFactory_Now_Call) RunAndReturn(run func() func() time.Time) *mockFactory_Now_Call { + _c.Call.Return(run) + return _c +} + // ShareCodeSender provides a mock function with given fields: ctx func (_m *mockFactory) ShareCodeSender(ctx context.Context) (ShareCodeSender, error) { ret := _m.Called(ctx) @@ -296,6 +392,53 @@ func (_c *mockFactory_UidStore_Call) RunAndReturn(run func() (UidStore, error)) return _c } +// UuidString provides a mock function with given fields: +func (_m *mockFactory) UuidString() func() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for UuidString") + } + + var r0 func() string + if rf, ok := ret.Get(0).(func() func() string); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(func() string) + } + } + + return r0 +} + +// mockFactory_UuidString_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UuidString' +type mockFactory_UuidString_Call struct { + *mock.Call +} + +// UuidString is a helper method to define mock.On call +func (_e *mockFactory_Expecter) UuidString() *mockFactory_UuidString_Call { + return &mockFactory_UuidString_Call{Call: _e.mock.On("UuidString")} +} + +func (_c *mockFactory_UuidString_Call) Run(run func()) *mockFactory_UuidString_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockFactory_UuidString_Call) Return(_a0 func() string) *mockFactory_UuidString_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockFactory_UuidString_Call) RunAndReturn(run func() func() string) *mockFactory_UuidString_Call { + _c.Call.Return(run) + return _c +} + // newMockFactory creates a new instance of mockFactory. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func newMockFactory(t interface { diff --git a/cmd/event-received/cloud_watch_event_handler.go b/cmd/event-received/sirius_event_handler.go similarity index 71% rename from cmd/event-received/cloud_watch_event_handler.go rename to cmd/event-received/sirius_event_handler.go index d894533517..e1d01fa5f1 100644 --- a/cmd/event-received/cloud_watch_event_handler.go +++ b/cmd/event-received/sirius_event_handler.go @@ -10,107 +10,64 @@ import ( "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/ministryofjustice/opg-modernising-lpa/internal/event" "github.com/ministryofjustice/opg-modernising-lpa/internal/page" - "github.com/ministryofjustice/opg-modernising-lpa/internal/uid" ) -type factory interface { - AppData() (page.AppData, error) - ShareCodeSender(ctx context.Context) (ShareCodeSender, error) - LpaStoreClient() (LpaStoreClient, error) - UidStore() (UidStore, error) - UidClient() UidClient -} - -type cloudWatchEventHandler struct { - dynamoClient dynamodbClient - now func() time.Time - uuidString func() string - factory factory -} +type siriusEventHandler struct{} -func (h *cloudWatchEventHandler) Handle(ctx context.Context, cloudWatchEvent events.CloudWatchEvent) error { +func (h *siriusEventHandler) Handle(ctx context.Context, factory factory, cloudWatchEvent events.CloudWatchEvent) error { switch cloudWatchEvent.DetailType { - case "uid-requested": - uidStore, err := h.factory.UidStore() - if err != nil { - return err - } - - uidClient := h.factory.UidClient() - - return handleUidRequested(ctx, uidStore, uidClient, cloudWatchEvent) - case "evidence-received": - return handleEvidenceReceived(ctx, h.dynamoClient, cloudWatchEvent) + return handleEvidenceReceived(ctx, factory.DynamoClient(), cloudWatchEvent) case "reduced-fee-approved": - appData, err := h.factory.AppData() + appData, err := factory.AppData() if err != nil { return err } - shareCodeSender, err := h.factory.ShareCodeSender(ctx) + shareCodeSender, err := factory.ShareCodeSender(ctx) if err != nil { return err } - lpaStoreClient, err := h.factory.LpaStoreClient() + lpaStoreClient, err := factory.LpaStoreClient() if err != nil { return err } - return handleFeeApproved(ctx, h.dynamoClient, cloudWatchEvent, shareCodeSender, lpaStoreClient, appData, h.now) + return handleFeeApproved(ctx, factory.DynamoClient(), cloudWatchEvent, shareCodeSender, lpaStoreClient, appData, factory.Now()) case "reduced-fee-declined": - return handleFeeDenied(ctx, h.dynamoClient, cloudWatchEvent, h.now) + return handleFeeDenied(ctx, factory.DynamoClient(), cloudWatchEvent, factory.Now()) - case "more-evidence-required": - return handleMoreEvidenceRequired(ctx, h.dynamoClient, cloudWatchEvent, h.now) + case "further-info-requested": + return handleFurtherInfoRequested(ctx, factory.DynamoClient(), cloudWatchEvent, factory.Now()) case "donor-submission-completed": - appData, err := h.factory.AppData() + appData, err := factory.AppData() if err != nil { return err } - shareCodeSender, err := h.factory.ShareCodeSender(ctx) + shareCodeSender, err := factory.ShareCodeSender(ctx) if err != nil { return err } - lpaStoreClient, err := h.factory.LpaStoreClient() + lpaStoreClient, err := factory.LpaStoreClient() if err != nil { return err } - return handleDonorSubmissionCompleted(ctx, h.dynamoClient, cloudWatchEvent, shareCodeSender, appData, lpaStoreClient, h.uuidString, h.now) + return handleDonorSubmissionCompleted(ctx, factory.DynamoClient(), cloudWatchEvent, shareCodeSender, appData, lpaStoreClient, factory.UuidString(), factory.Now()) case "certificate-provider-submission-completed": - return handleCertificateProviderSubmissionCompleted(ctx, cloudWatchEvent, h.factory) + return handleCertificateProviderSubmissionCompleted(ctx, cloudWatchEvent, factory) default: - return fmt.Errorf("unknown cloudwatch event") - } -} - -func handleUidRequested(ctx context.Context, uidStore UidStore, uidClient UidClient, e events.CloudWatchEvent) error { - var v event.UidRequested - if err := json.Unmarshal(e.Detail, &v); err != nil { - return fmt.Errorf("failed to unmarshal detail: %w", err) - } - - uid, err := uidClient.CreateCase(ctx, &uid.CreateCaseRequestBody{Type: v.Type, Donor: v.Donor}) - if err != nil { - return fmt.Errorf("failed to create case: %w", err) - } - - if err := uidStore.Set(ctx, v.LpaID, v.DonorSessionID, v.OrganisationID, uid); err != nil { - return fmt.Errorf("failed to set uid: %w", err) + return fmt.Errorf("unknown sirius event") } - - return nil } func handleEvidenceReceived(ctx context.Context, client dynamodbClient, event events.CloudWatchEvent) error { @@ -163,7 +120,7 @@ func handleFeeApproved(ctx context.Context, client dynamodbClient, event events. return nil } -func handleMoreEvidenceRequired(ctx context.Context, client dynamodbClient, event events.CloudWatchEvent, now func() time.Time) error { +func handleFurtherInfoRequested(ctx context.Context, client dynamodbClient, event events.CloudWatchEvent, now func() time.Time) error { var v uidEvent if err := json.Unmarshal(event.Detail, &v); err != nil { return fmt.Errorf("failed to unmarshal detail: %w", err) diff --git a/cmd/event-received/cloud_watch_event_handler_test.go b/cmd/event-received/sirius_event_handler_test.go similarity index 86% rename from cmd/event-received/cloud_watch_event_handler_test.go rename to cmd/event-received/sirius_event_handler_test.go index b32b054c20..2f2b94eea7 100644 --- a/cmd/event-received/cloud_watch_event_handler_test.go +++ b/cmd/event-received/sirius_event_handler_test.go @@ -11,82 +11,18 @@ import ( "github.com/aws/aws-lambda-go/events" "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" "github.com/ministryofjustice/opg-modernising-lpa/internal/actor/actoruid" - "github.com/ministryofjustice/opg-modernising-lpa/internal/date" "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" "github.com/ministryofjustice/opg-modernising-lpa/internal/lpastore" "github.com/ministryofjustice/opg-modernising-lpa/internal/page" - "github.com/ministryofjustice/opg-modernising-lpa/internal/uid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) -func TestHandleUnknownEvent(t *testing.T) { - handler := &cloudWatchEventHandler{} +func TestSiriusEventHandlerHandleUnknownEvent(t *testing.T) { + handler := &siriusEventHandler{} - err := handler.Handle(ctx, events.CloudWatchEvent{DetailType: "some-event"}) - assert.Equal(t, fmt.Errorf("unknown cloudwatch event"), err) -} - -func TestHandleUidRequested(t *testing.T) { - event := events.CloudWatchEvent{ - DetailType: "uid-requested", - Detail: json.RawMessage(`{"lpaID":"an-id","donorSessionID":"donor-id","organisationID":"org-id","type":"personal-welfare","donor":{"name":"a donor","dob":"2000-01-02","postcode":"F1 1FF"}}`), - } - - uidClient := newMockUidClient(t) - uidClient.EXPECT(). - CreateCase(ctx, &uid.CreateCaseRequestBody{ - Type: "personal-welfare", - Donor: uid.DonorDetails{ - Name: "a donor", - Dob: date.New("2000", "01", "02"), - Postcode: "F1 1FF", - }, - }). - Return("M-1111-2222-3333", nil) - - uidStore := newMockUidStore(t) - uidStore.EXPECT(). - Set(ctx, "an-id", "donor-id", "org-id", "M-1111-2222-3333"). - Return(nil) - - err := handleUidRequested(ctx, uidStore, uidClient, event) - assert.Nil(t, err) -} - -func TestHandleUidRequestedWhenUidClientErrors(t *testing.T) { - event := events.CloudWatchEvent{ - DetailType: "uid-requested", - Detail: json.RawMessage(`{"lpaID":"an-id","donorSessionID":"donor-id","type":"personal-welfare","donor":{"name":"a donor","dob":"2000-01-02","postcode":"F1 1FF"}}`), - } - - uidClient := newMockUidClient(t) - uidClient.EXPECT(). - CreateCase(ctx, mock.Anything). - Return("", expectedError) - - err := handleUidRequested(ctx, nil, uidClient, event) - assert.Equal(t, fmt.Errorf("failed to create case: %w", expectedError), err) -} - -func TestHandleUidRequestedWhenUidStoreErrors(t *testing.T) { - event := events.CloudWatchEvent{ - DetailType: "uid-requested", - Detail: json.RawMessage(`{"lpaID":"an-id","donorSessionID":"donor-id","type":"personal-welfare","donor":{"name":"a donor","dob":"2000-01-02","postcode":"F1 1FF"}}`), - } - - uidClient := newMockUidClient(t) - uidClient.EXPECT(). - CreateCase(ctx, mock.Anything). - Return("M-1111-2222-3333", nil) - - uidStore := newMockUidStore(t) - uidStore.EXPECT(). - Set(ctx, "an-id", "donor-id", "", "M-1111-2222-3333"). - Return(expectedError) - - err := handleUidRequested(ctx, uidStore, uidClient, event) - assert.Equal(t, fmt.Errorf("failed to set uid: %w", expectedError), err) + err := handler.Handle(ctx, nil, events.CloudWatchEvent{DetailType: "some-event"}) + assert.Equal(t, fmt.Errorf("unknown sirius event"), err) } func TestHandleEvidenceReceived(t *testing.T) { @@ -326,9 +262,9 @@ func TestHandleFeeApprovedWhenLpaStoreError(t *testing.T) { assert.Equal(t, fmt.Errorf("failed to send to lpastore: %w", expectedError), err) } -func TestHandleMoreEvidenceRequired(t *testing.T) { +func TestHandleFurtherInfoRequested(t *testing.T) { event := events.CloudWatchEvent{ - DetailType: "more-evidence-required", + DetailType: "further-info-requested", Detail: json.RawMessage(`{"uid":"M-1111-2222-3333"}`), } @@ -355,13 +291,13 @@ func TestHandleMoreEvidenceRequired(t *testing.T) { Put(ctx, updated). Return(nil) - err := handleMoreEvidenceRequired(ctx, client, event, func() time.Time { return now }) + err := handleFurtherInfoRequested(ctx, client, event, func() time.Time { return now }) assert.Nil(t, err) } -func TestHandleMoreEvidenceRequiredWhenPutError(t *testing.T) { +func TestHandleFurtherInfoRequestedWhenPutError(t *testing.T) { event := events.CloudWatchEvent{ - DetailType: "more-evidence-required", + DetailType: "further-info-requested", Detail: json.RawMessage(`{"uid":"M-1111-2222-3333"}`), } @@ -388,7 +324,7 @@ func TestHandleMoreEvidenceRequiredWhenPutError(t *testing.T) { Put(ctx, updated). Return(expectedError) - err := handleMoreEvidenceRequired(ctx, client, event, func() time.Time { return now }) + err := handleFurtherInfoRequested(ctx, client, event, func() time.Time { return now }) assert.Equal(t, fmt.Errorf("failed to update LPA task status: %w", expectedError), err) } @@ -686,8 +622,8 @@ func TestHandleCertificateProviderSubmissionCompletedWhenOnline(t *testing.T) { LpaStoreClient(). Return(lpaStoreClient, nil) - handler := &cloudWatchEventHandler{factory: factory} - err := handler.Handle(ctx, certificateProviderSubmissionCompletedEvent) + handler := &siriusEventHandler{} + err := handler.Handle(ctx, factory, certificateProviderSubmissionCompletedEvent) assert.Nil(t, err) } @@ -697,8 +633,8 @@ func TestHandleCertificateProviderSubmissionCompletedWhenLpaStoreFactoryErrors(t LpaStoreClient(). Return(nil, expectedError) - handler := &cloudWatchEventHandler{factory: factory} - err := handler.Handle(ctx, certificateProviderSubmissionCompletedEvent) + handler := &siriusEventHandler{} + err := handler.Handle(ctx, factory, certificateProviderSubmissionCompletedEvent) assert.Equal(t, expectedError, err) } @@ -713,8 +649,8 @@ func TestHandleCertificateProviderSubmissionCompletedWhenLpaStoreErrors(t *testi LpaStoreClient(). Return(lpaStoreClient, nil) - handler := &cloudWatchEventHandler{factory: factory} - err := handler.Handle(ctx, certificateProviderSubmissionCompletedEvent) + handler := &siriusEventHandler{} + err := handler.Handle(ctx, factory, certificateProviderSubmissionCompletedEvent) assert.Equal(t, fmt.Errorf("failed to retrieve lpa: %w", expectedError), err) } @@ -744,8 +680,8 @@ func TestHandleCertificateProviderSubmissionCompletedWhenShareCodeSenderErrors(t AppData(). Return(page.AppData{}, nil) - handler := &cloudWatchEventHandler{factory: factory} - err := handler.Handle(ctx, certificateProviderSubmissionCompletedEvent) + handler := &siriusEventHandler{} + err := handler.Handle(ctx, factory, certificateProviderSubmissionCompletedEvent) assert.Equal(t, fmt.Errorf("failed to send share codes to attorneys: %w", expectedError), err) } @@ -767,8 +703,8 @@ func TestHandleCertificateProviderSubmissionCompletedWhenShareCodeSenderFactoryE ShareCodeSender(ctx). Return(nil, expectedError) - handler := &cloudWatchEventHandler{factory: factory} - err := handler.Handle(ctx, certificateProviderSubmissionCompletedEvent) + handler := &siriusEventHandler{} + err := handler.Handle(ctx, factory, certificateProviderSubmissionCompletedEvent) assert.Equal(t, expectedError, err) } @@ -793,7 +729,7 @@ func TestHandleCertificateProviderSubmissionCompletedWhenAppDataFactoryErrors(t AppData(). Return(page.AppData{}, expectedError) - handler := &cloudWatchEventHandler{factory: factory} - err := handler.Handle(ctx, certificateProviderSubmissionCompletedEvent) + handler := &siriusEventHandler{} + err := handler.Handle(ctx, factory, certificateProviderSubmissionCompletedEvent) assert.Equal(t, expectedError, err) } diff --git a/internal/actor/donor_provided.go b/internal/actor/donor_provided.go index 7eb16c7136..ed4eccd67d 100644 --- a/internal/actor/donor_provided.go +++ b/internal/actor/donor_provided.go @@ -106,6 +106,8 @@ type DonorProvidedDetails struct { SubmittedAt time.Time // WithdrawnAt is when the Lpa was withdrawn by the donor WithdrawnAt time.Time + // PerfectAt is when the Lpa transitioned to the PERFECT status in the lpa-store + PerfectAt time.Time // Version is the number of times the LPA has been updated (auto-incremented on PUT) Version int `hash:"-"` diff --git a/internal/actor/donor_provided_test.go b/internal/actor/donor_provided_test.go index 1209499983..5024ca9451 100644 --- a/internal/actor/donor_provided_test.go +++ b/internal/actor/donor_provided_test.go @@ -27,12 +27,12 @@ func TestGenerateHash(t *testing.T) { }} hash, err := donor.GenerateHash() assert.Nil(t, err) - assert.Equal(t, uint64(0x757f179848dc49a8), hash) + assert.Equal(t, uint64(0x77849de164f8aeb7), hash) donor.Attorneys.Attorneys[0].DateOfBirth = date.New("2001", "1", "2") hash, err = donor.GenerateHash() assert.Nil(t, err) - assert.Equal(t, uint64(0x858827c8eb6e4b8f), hash) + assert.Equal(t, uint64(0xdea85a2c62916db7), hash) } func TestIdentityConfirmed(t *testing.T) { diff --git a/internal/lpastore/lpa.go b/internal/lpastore/lpa.go index 36aa62a8ea..f6906719b2 100644 --- a/internal/lpastore/lpa.go +++ b/internal/lpastore/lpa.go @@ -360,6 +360,7 @@ type Lpa struct { LpaUID string RegisteredAt time.Time WithdrawnAt time.Time + PerfectAt time.Time UpdatedAt time.Time Type actor.LpaType Donor actor.Donor diff --git a/internal/lpastore/resolving_service.go b/internal/lpastore/resolving_service.go index 94e9191e8d..c6ed288630 100644 --- a/internal/lpastore/resolving_service.go +++ b/internal/lpastore/resolving_service.go @@ -79,6 +79,7 @@ func (s *ResolvingService) ResolveList(ctx context.Context, donors []*actor.Dono func (s *ResolvingService) merge(lpa *Lpa, donor *actor.DonorProvidedDetails) *Lpa { lpa.LpaID = donor.LpaID lpa.LpaUID = donor.LpaUID + lpa.PerfectAt = donor.PerfectAt if donor.SK.Equals(dynamo.DonorKey("PAPER")) { lpa.Submitted = true lpa.Paid = true diff --git a/internal/page/fixtures/donor.go b/internal/page/fixtures/donor.go index f8c44291c3..64f7910e60 100644 --- a/internal/page/fixtures/donor.go +++ b/internal/page/fixtures/donor.go @@ -51,6 +51,7 @@ var progressValues = []string{ "signedByCertificateProvider", "signedByAttorneys", "submitted", + "perfect", "withdrawn", "registered", } @@ -474,6 +475,10 @@ func updateLPAProgress( donorDetails.SubmittedAt = time.Now() } + if data.Progress >= slices.Index(progressValues, "perfect") { + donorDetails.PerfectAt = time.Now() + } + if data.Progress == slices.Index(progressValues, "withdrawn") { donorDetails.WithdrawnAt = time.Now() } diff --git a/internal/page/lpa_progress_tracker.go b/internal/page/lpa_progress_tracker.go index 085b0d85bd..6d954676f5 100644 --- a/internal/page/lpa_progress_tracker.go +++ b/internal/page/lpa_progress_tracker.go @@ -15,16 +15,35 @@ type ProgressTask struct { } type Progress struct { + isOrganisation bool Paid ProgressTask ConfirmedID ProgressTask DonorSigned ProgressTask CertificateProviderSigned ProgressTask AttorneysSigned ProgressTask LpaSubmitted ProgressTask + NoticesOfIntentSent ProgressTask StatutoryWaitingPeriod ProgressTask LpaRegistered ProgressTask } +func (p Progress) ToSlice() []ProgressTask { + var list []ProgressTask + if p.isOrganisation { + list = append(list, p.Paid, p.ConfirmedID) + } + + list = append(list, p.DonorSigned, p.CertificateProviderSigned, p.AttorneysSigned, p.LpaSubmitted) + + if p.NoticesOfIntentSent.State.Completed() { + list = append(list, p.NoticesOfIntentSent) + } + + list = append(list, p.StatutoryWaitingPeriod, p.LpaRegistered) + + return list +} + func (pt ProgressTracker) Progress(lpa *lpastore.Lpa) Progress { var labels map[string]string @@ -45,16 +64,16 @@ func (pt ProgressTracker) Progress(lpa *lpastore.Lpa) Progress { "certificateProviderSigned": pt.Localizer.T("theCertificateProviderHasDeclared"), "attorneysSigned": pt.Localizer.T("allAttorneysHaveSignedTheLpa"), "lpaSubmitted": pt.Localizer.T("opgHasReceivedTheLPA"), + "noticesOfIntentSent": "weSentAnEmailTheLpaIsReadyToRegister", "statutoryWaitingPeriod": pt.Localizer.T("theWaitingPeriodHasStarted"), "lpaRegistered": pt.Localizer.T("theLpaHasBeenRegistered"), } } else { labels = map[string]string{ - "paid": "", - "confirmedID": "", "donorSigned": pt.Localizer.T("youveSignedYourLpa"), "attorneysSigned": pt.Localizer.Count("attorneysHaveDeclared", len(lpa.Attorneys.Attorneys)), "lpaSubmitted": pt.Localizer.T("weHaveReceivedYourLpa"), + "noticesOfIntentSent": "weSentAnEmailYourLpaIsReadyToRegister", "statutoryWaitingPeriod": pt.Localizer.T("yourWaitingPeriodHasStarted"), "lpaRegistered": pt.Localizer.T("yourLpaHasBeenRegistered"), } @@ -70,6 +89,7 @@ func (pt ProgressTracker) Progress(lpa *lpastore.Lpa) Progress { } progress := Progress{ + isOrganisation: lpa.IsOrganisationDonor, Paid: ProgressTask{ State: actor.TaskNotStarted, Label: labels["paid"], @@ -94,6 +114,9 @@ func (pt ProgressTracker) Progress(lpa *lpastore.Lpa) Progress { State: actor.TaskNotStarted, Label: labels["lpaSubmitted"], }, + NoticesOfIntentSent: ProgressTask{ + State: actor.TaskNotStarted, + }, StatutoryWaitingPeriod: ProgressTask{ State: actor.TaskNotStarted, Label: labels["statutoryWaitingPeriod"], @@ -152,6 +175,15 @@ func (pt ProgressTracker) Progress(lpa *lpastore.Lpa) Progress { } progress.LpaSubmitted.State = actor.TaskCompleted + + if lpa.PerfectAt.IsZero() { + return progress + } + + progress.NoticesOfIntentSent.Label = pt.Localizer.Format(labels["noticesOfIntentSent"], map[string]any{ + "SentOn": pt.Localizer.FormatDate(lpa.PerfectAt), + }) + progress.NoticesOfIntentSent.State = actor.TaskCompleted progress.StatutoryWaitingPeriod.State = actor.TaskInProgress if lpa.RegisteredAt.IsZero() { diff --git a/internal/page/lpa_progress_tracker_test.go b/internal/page/lpa_progress_tracker_test.go index 636af2e3a4..47a051c80a 100644 --- a/internal/page/lpa_progress_tracker_test.go +++ b/internal/page/lpa_progress_tracker_test.go @@ -11,6 +11,82 @@ import ( "github.com/stretchr/testify/assert" ) +func TestProgressToSlice(t *testing.T) { + testcases := map[string]func(Progress) (Progress, []ProgressTask){ + "donor": func(p Progress) (Progress, []ProgressTask) { + return p, []ProgressTask{ + p.DonorSigned, + p.CertificateProviderSigned, + p.AttorneysSigned, + p.LpaSubmitted, + p.StatutoryWaitingPeriod, + p.LpaRegistered, + } + }, + "organisation": func(p Progress) (Progress, []ProgressTask) { + p.isOrganisation = true + + return p, []ProgressTask{ + p.Paid, + p.ConfirmedID, + p.DonorSigned, + p.CertificateProviderSigned, + p.AttorneysSigned, + p.LpaSubmitted, + p.StatutoryWaitingPeriod, + p.LpaRegistered, + } + }, + "donor notices sent": func(p Progress) (Progress, []ProgressTask) { + p.NoticesOfIntentSent.State = actor.TaskCompleted + + return p, []ProgressTask{ + p.DonorSigned, + p.CertificateProviderSigned, + p.AttorneysSigned, + p.LpaSubmitted, + p.NoticesOfIntentSent, + p.StatutoryWaitingPeriod, + p.LpaRegistered, + } + }, + "organisation notices sent": func(p Progress) (Progress, []ProgressTask) { + p.isOrganisation = true + p.NoticesOfIntentSent.State = actor.TaskCompleted + + return p, []ProgressTask{ + p.Paid, + p.ConfirmedID, + p.DonorSigned, + p.CertificateProviderSigned, + p.AttorneysSigned, + p.LpaSubmitted, + p.NoticesOfIntentSent, + p.StatutoryWaitingPeriod, + p.LpaRegistered, + } + }, + } + + for name, fn := range testcases { + t.Run(name, func(t *testing.T) { + progress, slice := fn(Progress{ + Paid: ProgressTask{State: actor.TaskNotStarted, Label: "Paid translation"}, + ConfirmedID: ProgressTask{State: actor.TaskNotStarted, Label: "ConfirmedID translation"}, + DonorSigned: ProgressTask{State: actor.TaskInProgress, Label: "DonorSigned translation"}, + CertificateProviderSigned: ProgressTask{State: actor.TaskNotStarted, Label: "CertificateProviderSigned translation"}, + AttorneysSigned: ProgressTask{State: actor.TaskNotStarted, Label: "AttorneysSigned translation"}, + LpaSubmitted: ProgressTask{State: actor.TaskNotStarted, Label: "LpaSubmitted translation"}, + NoticesOfIntentSent: ProgressTask{State: actor.TaskNotStarted, Label: "NoticesOfIntentSent translation"}, + StatutoryWaitingPeriod: ProgressTask{State: actor.TaskNotStarted, Label: "StatutoryWaitingPeriod translation"}, + LpaRegistered: ProgressTask{State: actor.TaskNotStarted, Label: "LpaRegistered translation"}, + }) + + assert.Equal(t, slice, progress.ToSlice()) + }) + } +} + func TestProgressTrackerProgress(t *testing.T) { lpaSignedAt := time.Now() uid1 := actoruid.New() @@ -26,34 +102,10 @@ func TestProgressTrackerProgress(t *testing.T) { LpaRegistered: ProgressTask{State: actor.TaskNotStarted, Label: "LpaRegistered translation"}, } - localizerFn := func() *mockLocalizer { - localizer := newMockLocalizer(t) - localizer.EXPECT(). - T("youveSignedYourLpa"). - Return("DonorSigned translation") - localizer.EXPECT(). - T("yourCertificateProviderHasDeclared"). - Return("CertificateProviderSigned translation") - localizer.EXPECT(). - Count("attorneysHaveDeclared", 1). - Return("AttorneysSigned translation") - localizer.EXPECT(). - T("weHaveReceivedYourLpa"). - Return("LpaSubmitted translation") - localizer.EXPECT(). - T("yourWaitingPeriodHasStarted"). - Return("StatutoryWaitingPeriod translation") - localizer.EXPECT(). - T("yourLpaHasBeenRegistered"). - Return("LpaRegistered translation") - - return localizer - } - testCases := map[string]struct { - lpa *lpastore.Lpa - expectedProgress func() Progress - expectedLocalizer func() *mockLocalizer + lpa *lpastore.Lpa + expectedProgress func() Progress + setupLocalizer func(*mockLocalizer) }{ "initial state": { lpa: &lpastore.Lpa{ @@ -62,9 +114,16 @@ func TestProgressTrackerProgress(t *testing.T) { expectedProgress: func() Progress { return initialProgress }, - expectedLocalizer: func() *mockLocalizer { return localizerFn() }, + setupLocalizer: func(localizer *mockLocalizer) { + localizer.EXPECT(). + T("yourCertificateProviderHasDeclared"). + Return("CertificateProviderSigned translation") + localizer.EXPECT(). + Count("attorneysHaveDeclared", 1). + Return("AttorneysSigned translation") + }, }, - "initial state - with certificate provider name": { + "initial state with certificate provider name": { lpa: &lpastore.Lpa{ CertificateProvider: lpastore.CertificateProvider{FirstNames: "A", LastName: "B"}, Attorneys: lpastore.Attorneys{Attorneys: []lpastore.Attorney{{}}}, @@ -72,30 +131,15 @@ func TestProgressTrackerProgress(t *testing.T) { expectedProgress: func() Progress { return initialProgress }, - expectedLocalizer: func() *mockLocalizer { - localizer := newMockLocalizer(t) + setupLocalizer: func(localizer *mockLocalizer) { localizer.EXPECT(). - T("youveSignedYourLpa"). - Return("DonorSigned translation") - localizer.EXPECT(). - Format( - "certificateProviderHasDeclared", map[string]interface{}{"CertificateProviderFullName": "A B"}, - ). + Format("certificateProviderHasDeclared", map[string]interface{}{ + "CertificateProviderFullName": "A B", + }). Return("CertificateProviderSigned translation") localizer.EXPECT(). Count("attorneysHaveDeclared", 1). Return("AttorneysSigned translation") - localizer.EXPECT(). - T("weHaveReceivedYourLpa"). - Return("LpaSubmitted translation") - localizer.EXPECT(). - T("yourWaitingPeriodHasStarted"). - Return("StatutoryWaitingPeriod translation") - localizer.EXPECT(). - T("yourLpaHasBeenRegistered"). - Return("LpaRegistered translation") - - return localizer }, }, "lpa signed": { @@ -111,7 +155,14 @@ func TestProgressTrackerProgress(t *testing.T) { return progress }, - expectedLocalizer: func() *mockLocalizer { return localizerFn() }, + setupLocalizer: func(localizer *mockLocalizer) { + localizer.EXPECT(). + T("yourCertificateProviderHasDeclared"). + Return("CertificateProviderSigned translation") + localizer.EXPECT(). + Count("attorneysHaveDeclared", 1). + Return("AttorneysSigned translation") + }, }, "certificate provider signed": { lpa: &lpastore.Lpa{ @@ -129,7 +180,14 @@ func TestProgressTrackerProgress(t *testing.T) { return progress }, - expectedLocalizer: func() *mockLocalizer { return localizerFn() }, + setupLocalizer: func(localizer *mockLocalizer) { + localizer.EXPECT(). + T("yourCertificateProviderHasDeclared"). + Return("CertificateProviderSigned translation") + localizer.EXPECT(). + Count("attorneysHaveDeclared", 1). + Return("AttorneysSigned translation") + }, }, "attorneys signed": { lpa: &lpastore.Lpa{ @@ -148,31 +206,43 @@ func TestProgressTrackerProgress(t *testing.T) { return progress }, - expectedLocalizer: func() *mockLocalizer { - localizer := newMockLocalizer(t) - localizer.EXPECT(). - T("youveSignedYourLpa"). - Return("DonorSigned translation") + setupLocalizer: func(localizer *mockLocalizer) { localizer.EXPECT(). T("yourCertificateProviderHasDeclared"). Return("CertificateProviderSigned translation") localizer.EXPECT(). Count("attorneysHaveDeclared", 2). Return("AttorneysSigned translation") + }, + }, + "submitted": { + lpa: &lpastore.Lpa{ + Paid: true, + Donor: actor.Donor{FirstNames: "a", LastName: "b"}, + CertificateProvider: lpastore.CertificateProvider{SignedAt: lpaSignedAt}, + Attorneys: lpastore.Attorneys{Attorneys: []lpastore.Attorney{{UID: uid1, SignedAt: lpaSignedAt}}}, + SignedAt: lpaSignedAt, + Submitted: true, + }, + expectedProgress: func() Progress { + progress := initialProgress + progress.DonorSigned.State = actor.TaskCompleted + progress.CertificateProviderSigned.State = actor.TaskCompleted + progress.AttorneysSigned.State = actor.TaskCompleted + progress.LpaSubmitted.State = actor.TaskCompleted + + return progress + }, + setupLocalizer: func(localizer *mockLocalizer) { localizer.EXPECT(). - T("weHaveReceivedYourLpa"). - Return("LpaSubmitted translation") - localizer.EXPECT(). - T("yourWaitingPeriodHasStarted"). - Return("StatutoryWaitingPeriod translation") + T("yourCertificateProviderHasDeclared"). + Return("CertificateProviderSigned translation") localizer.EXPECT(). - T("yourLpaHasBeenRegistered"). - Return("LpaRegistered translation") - - return localizer + Count("attorneysHaveDeclared", 1). + Return("AttorneysSigned translation") }, }, - "submitted": { + "perfect": { lpa: &lpastore.Lpa{ Paid: true, Donor: actor.Donor{FirstNames: "a", LastName: "b"}, @@ -180,6 +250,7 @@ func TestProgressTrackerProgress(t *testing.T) { Attorneys: lpastore.Attorneys{Attorneys: []lpastore.Attorney{{UID: uid1, SignedAt: lpaSignedAt}}}, SignedAt: lpaSignedAt, Submitted: true, + PerfectAt: lpaSignedAt, }, expectedProgress: func() Progress { progress := initialProgress @@ -187,11 +258,26 @@ func TestProgressTrackerProgress(t *testing.T) { progress.CertificateProviderSigned.State = actor.TaskCompleted progress.AttorneysSigned.State = actor.TaskCompleted progress.LpaSubmitted.State = actor.TaskCompleted + progress.NoticesOfIntentSent.State = actor.TaskCompleted + progress.NoticesOfIntentSent.Label = "NoticesOfIntentSent translation" progress.StatutoryWaitingPeriod.State = actor.TaskInProgress return progress }, - expectedLocalizer: func() *mockLocalizer { return localizerFn() }, + setupLocalizer: func(localizer *mockLocalizer) { + localizer.EXPECT(). + T("yourCertificateProviderHasDeclared"). + Return("CertificateProviderSigned translation") + localizer.EXPECT(). + Count("attorneysHaveDeclared", 1). + Return("AttorneysSigned translation") + localizer.EXPECT(). + Format("weSentAnEmailYourLpaIsReadyToRegister", map[string]any{"SentOn": "perfect-on"}). + Return("NoticesOfIntentSent translation") + localizer.EXPECT(). + FormatDate(lpaSignedAt). + Return("perfect-on") + }, }, "registered": { lpa: &lpastore.Lpa{ @@ -201,6 +287,7 @@ func TestProgressTrackerProgress(t *testing.T) { Attorneys: lpastore.Attorneys{Attorneys: []lpastore.Attorney{{UID: uid1, SignedAt: lpaSignedAt.Add(time.Minute)}}}, CertificateProvider: lpastore.CertificateProvider{SignedAt: lpaSignedAt}, Submitted: true, + PerfectAt: lpaSignedAt, RegisteredAt: lpaSignedAt, }, expectedProgress: func() Progress { @@ -209,18 +296,51 @@ func TestProgressTrackerProgress(t *testing.T) { progress.CertificateProviderSigned.State = actor.TaskCompleted progress.AttorneysSigned.State = actor.TaskCompleted progress.LpaSubmitted.State = actor.TaskCompleted + progress.NoticesOfIntentSent.State = actor.TaskCompleted + progress.NoticesOfIntentSent.Label = "NoticesOfIntentSent translation" progress.StatutoryWaitingPeriod.State = actor.TaskCompleted progress.LpaRegistered.State = actor.TaskCompleted return progress }, - expectedLocalizer: func() *mockLocalizer { return localizerFn() }, + setupLocalizer: func(localizer *mockLocalizer) { + localizer.EXPECT(). + T("yourCertificateProviderHasDeclared"). + Return("CertificateProviderSigned translation") + localizer.EXPECT(). + Count("attorneysHaveDeclared", 1). + Return("AttorneysSigned translation") + localizer.EXPECT(). + Format("weSentAnEmailYourLpaIsReadyToRegister", map[string]any{"SentOn": "perfect-on"}). + Return("NoticesOfIntentSent translation") + localizer.EXPECT(). + FormatDate(lpaSignedAt). + Return("perfect-on") + }, }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { - progressTracker := ProgressTracker{Localizer: tc.expectedLocalizer()} + localizer := newMockLocalizer(t) + localizer.EXPECT(). + T("youveSignedYourLpa"). + Return("DonorSigned translation") + localizer.EXPECT(). + T("weHaveReceivedYourLpa"). + Return("LpaSubmitted translation") + localizer.EXPECT(). + T("yourWaitingPeriodHasStarted"). + Return("StatutoryWaitingPeriod translation") + localizer.EXPECT(). + T("yourLpaHasBeenRegistered"). + Return("LpaRegistered translation") + + if tc.setupLocalizer != nil { + tc.setupLocalizer(localizer) + } + + progressTracker := ProgressTracker{Localizer: localizer} assert.Equal(t, tc.expectedProgress(), progressTracker.Progress(tc.lpa)) }) @@ -232,59 +352,22 @@ func TestLpaProgressAsSupporter(t *testing.T) { lpaSignedAt := time.Now() uid := actoruid.New() initialProgress := Progress{ + isOrganisation: true, Paid: ProgressTask{State: actor.TaskInProgress, Label: "Paid translation"}, ConfirmedID: ProgressTask{State: actor.TaskNotStarted, Label: "ConfirmedID translation"}, DonorSigned: ProgressTask{State: actor.TaskNotStarted, Label: "DonorSigned translation"}, CertificateProviderSigned: ProgressTask{State: actor.TaskNotStarted, Label: "CertificateProviderSigned translation"}, AttorneysSigned: ProgressTask{State: actor.TaskNotStarted, Label: "AttorneysSigned translation"}, LpaSubmitted: ProgressTask{State: actor.TaskNotStarted, Label: "LpaSubmitted translation"}, + NoticesOfIntentSent: ProgressTask{State: actor.TaskNotStarted}, StatutoryWaitingPeriod: ProgressTask{State: actor.TaskNotStarted, Label: "StatutoryWaitingPeriod translation"}, LpaRegistered: ProgressTask{State: actor.TaskNotStarted, Label: "LpaRegistered translation"}, } - localizerFn := func() *mockLocalizer { - localizer := newMockLocalizer(t) - localizer.EXPECT(). - Format( - "donorFullNameHasPaid", - map[string]interface{}{"DonorFullName": "a b"}, - ). - Return("Paid translation") - localizer.EXPECT(). - Format( - "donorFullNameHasConfirmedTheirIdentity", - map[string]interface{}{"DonorFullName": "a b"}, - ). - Return("ConfirmedID translation") - localizer.EXPECT(). - Format( - "donorFullNameHasSignedTheLPA", - map[string]interface{}{"DonorFullName": "a b"}, - ). - Return("DonorSigned translation") - localizer.EXPECT(). - T("theCertificateProviderHasDeclared"). - Return("CertificateProviderSigned translation") - localizer.EXPECT(). - T("allAttorneysHaveSignedTheLpa"). - Return("AttorneysSigned translation") - localizer.EXPECT(). - T("opgHasReceivedTheLPA"). - Return("LpaSubmitted translation") - localizer.EXPECT(). - T("theWaitingPeriodHasStarted"). - Return("StatutoryWaitingPeriod translation") - localizer.EXPECT(). - T("theLpaHasBeenRegistered"). - Return("LpaRegistered translation") - - return localizer - } - testCases := map[string]struct { - lpa *lpastore.Lpa - expectedProgress func() Progress - expectedLocalizer func() *mockLocalizer + lpa *lpastore.Lpa + expectedProgress func() Progress + setupLocalizer func(*mockLocalizer) }{ "initial state": { lpa: &lpastore.Lpa{ @@ -295,7 +378,6 @@ func TestLpaProgressAsSupporter(t *testing.T) { expectedProgress: func() Progress { return initialProgress }, - expectedLocalizer: func() *mockLocalizer { return localizerFn() }, }, "paid": { lpa: &lpastore.Lpa{ @@ -311,7 +393,6 @@ func TestLpaProgressAsSupporter(t *testing.T) { return progress }, - expectedLocalizer: func() *mockLocalizer { return localizerFn() }, }, "confirmed ID": { lpa: &lpastore.Lpa{ @@ -329,7 +410,6 @@ func TestLpaProgressAsSupporter(t *testing.T) { return progress }, - expectedLocalizer: func() *mockLocalizer { return localizerFn() }, }, "donor signed": { lpa: &lpastore.Lpa{ @@ -349,7 +429,6 @@ func TestLpaProgressAsSupporter(t *testing.T) { return progress }, - expectedLocalizer: func() *mockLocalizer { return localizerFn() }, }, "certificate provider signed": { lpa: &lpastore.Lpa{ @@ -371,7 +450,6 @@ func TestLpaProgressAsSupporter(t *testing.T) { return progress }, - expectedLocalizer: func() *mockLocalizer { return localizerFn() }, }, "attorneys signed": { lpa: &lpastore.Lpa{ @@ -394,7 +472,6 @@ func TestLpaProgressAsSupporter(t *testing.T) { return progress }, - expectedLocalizer: func() *mockLocalizer { return localizerFn() }, }, "submitted": { lpa: &lpastore.Lpa{ @@ -415,11 +492,44 @@ func TestLpaProgressAsSupporter(t *testing.T) { progress.CertificateProviderSigned.State = actor.TaskCompleted progress.AttorneysSigned.State = actor.TaskCompleted progress.LpaSubmitted.State = actor.TaskCompleted + + return progress + }, + }, + "perfect": { + lpa: &lpastore.Lpa{ + IsOrganisationDonor: true, + Donor: actor.Donor{FirstNames: "a", LastName: "b", DateOfBirth: dateOfBirth}, + DonorIdentityConfirmed: true, + Attorneys: lpastore.Attorneys{Attorneys: []lpastore.Attorney{{UID: uid, SignedAt: lpaSignedAt.Add(time.Minute)}}}, + CertificateProvider: lpastore.CertificateProvider{SignedAt: lpaSignedAt}, + Paid: true, + SignedAt: lpaSignedAt, + Submitted: true, + PerfectAt: lpaSignedAt, + }, + expectedProgress: func() Progress { + progress := initialProgress + progress.Paid.State = actor.TaskCompleted + progress.ConfirmedID.State = actor.TaskCompleted + progress.DonorSigned.State = actor.TaskCompleted + progress.CertificateProviderSigned.State = actor.TaskCompleted + progress.AttorneysSigned.State = actor.TaskCompleted + progress.LpaSubmitted.State = actor.TaskCompleted + progress.NoticesOfIntentSent.Label = "NoticesOfIntentSent translation" + progress.NoticesOfIntentSent.State = actor.TaskCompleted progress.StatutoryWaitingPeriod.State = actor.TaskInProgress return progress }, - expectedLocalizer: func() *mockLocalizer { return localizerFn() }, + setupLocalizer: func(localizer *mockLocalizer) { + localizer.EXPECT(). + Format("weSentAnEmailTheLpaIsReadyToRegister", map[string]any{"SentOn": "perfect-on"}). + Return("NoticesOfIntentSent translation") + localizer.EXPECT(). + FormatDate(lpaSignedAt). + Return("perfect-on") + }, }, "registered": { lpa: &lpastore.Lpa{ @@ -431,6 +541,7 @@ func TestLpaProgressAsSupporter(t *testing.T) { Paid: true, SignedAt: lpaSignedAt, Submitted: true, + PerfectAt: lpaSignedAt, RegisteredAt: lpaSignedAt, }, expectedProgress: func() Progress { @@ -441,18 +552,66 @@ func TestLpaProgressAsSupporter(t *testing.T) { progress.CertificateProviderSigned.State = actor.TaskCompleted progress.AttorneysSigned.State = actor.TaskCompleted progress.LpaSubmitted.State = actor.TaskCompleted + progress.NoticesOfIntentSent.Label = "NoticesOfIntentSent translation" + progress.NoticesOfIntentSent.State = actor.TaskCompleted progress.StatutoryWaitingPeriod.State = actor.TaskCompleted progress.LpaRegistered.State = actor.TaskCompleted return progress }, - expectedLocalizer: func() *mockLocalizer { return localizerFn() }, + setupLocalizer: func(localizer *mockLocalizer) { + localizer.EXPECT(). + Format("weSentAnEmailTheLpaIsReadyToRegister", map[string]any{"SentOn": "perfect-on"}). + Return("NoticesOfIntentSent translation") + localizer.EXPECT(). + FormatDate(lpaSignedAt). + Return("perfect-on") + }, }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { - progressTracker := ProgressTracker{Localizer: tc.expectedLocalizer()} + localizer := newMockLocalizer(t) + localizer.EXPECT(). + Format( + "donorFullNameHasPaid", + map[string]interface{}{"DonorFullName": "a b"}, + ). + Return("Paid translation") + localizer.EXPECT(). + Format( + "donorFullNameHasConfirmedTheirIdentity", + map[string]interface{}{"DonorFullName": "a b"}, + ). + Return("ConfirmedID translation") + localizer.EXPECT(). + Format( + "donorFullNameHasSignedTheLPA", + map[string]interface{}{"DonorFullName": "a b"}, + ). + Return("DonorSigned translation") + localizer.EXPECT(). + T("theCertificateProviderHasDeclared"). + Return("CertificateProviderSigned translation") + localizer.EXPECT(). + T("allAttorneysHaveSignedTheLpa"). + Return("AttorneysSigned translation") + localizer.EXPECT(). + T("opgHasReceivedTheLPA"). + Return("LpaSubmitted translation") + localizer.EXPECT(). + T("theWaitingPeriodHasStarted"). + Return("StatutoryWaitingPeriod translation") + localizer.EXPECT(). + T("theLpaHasBeenRegistered"). + Return("LpaRegistered translation") + + if tc.setupLocalizer != nil { + tc.setupLocalizer(localizer) + } + + progressTracker := ProgressTracker{Localizer: localizer} assert.Equal(t, tc.expectedProgress(), progressTracker.Progress(tc.lpa)) }) diff --git a/lang/cy.json b/lang/cy.json index 6dbf2292bf..eb14fdd261 100644 --- a/lang/cy.json +++ b/lang/cy.json @@ -1183,5 +1183,7 @@ "chooseWhoElseCorrespondentShareOptional": "Welsh", "attorneysAndReplacementAttorneys": "Welsh", "addMyLpa": "Welsh", - "contactNumber": "Welsh" + "contactNumber": "Welsh", + "weSentAnEmailYourLpaIsReadyToRegister": "Welsh {{.SentOn}}", + "weSentAnEmailTheLpaIsReadyToRegister": "Welsh {{.SentOn}}" } diff --git a/lang/en.json b/lang/en.json index 08d492b991..8fe12e4292 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1115,5 +1115,7 @@ "chooseWhoElseCorrespondentShareOptional": "Choose who else you would like to share the correspondent’s details with (optional)", "attorneysAndReplacementAttorneys": "Attorneys and replacement attorneys", "addMyLpa": "Add my LPA", - "contactNumber": "Contact number" + "contactNumber": "Contact number", + "weSentAnEmailYourLpaIsReadyToRegister": "We sent an email on {{.SentOn}} that your LPA is ready to register", + "weSentAnEmailTheLpaIsReadyToRegister": "We sent an email on {{.SentOn}} that the LPA is ready to register" } diff --git a/terraform/environment/region/modules/event_received/lambda.tf b/terraform/environment/region/modules/event_received/lambda.tf index c3b0cf594b..156ae73de3 100644 --- a/terraform/environment/region/modules/event_received/lambda.tf +++ b/terraform/environment/region/modules/event_received/lambda.tf @@ -47,7 +47,14 @@ resource "aws_cloudwatch_event_rule" "receive_events_sirius" { event_pattern = jsonencode({ source = ["opg.poas.sirius"], - detail-type = ["evidence-received", "reduced-fee-approved", "reduced-fee-declined", "more-evidence-required"], + detail-type = [ + "certificate-provider-submission-completed", + "donor-submission-completed", + "evidence-received", + "further-evidence-requested", + "reduced-fee-approved", + "reduced-fee-declined", + ], }) provider = aws.region } @@ -60,6 +67,26 @@ resource "aws_cloudwatch_event_target" "receive_events_sirius" { provider = aws.region } +resource "aws_cloudwatch_event_rule" "receive_events_lpa_store" { + name = "${data.aws_default_tags.current.tags.environment-name}-receive-events-lpa-store" + description = "receive events from lpa store" + event_bus_name = var.event_bus_name + + event_pattern = jsonencode({ + source = ["opg.poas.lpastore"], + detail-type = ["lpa-updated"], + }) + provider = aws.region +} + +resource "aws_cloudwatch_event_target" "receive_events_lpa_store" { + target_id = "${data.aws_default_tags.current.tags.environment-name}-receive-events-lpa-store" + event_bus_name = var.event_bus_name + rule = aws_cloudwatch_event_rule.receive_events_lpa_store.name + arn = module.event_received.lambda.arn + provider = aws.region +} + resource "aws_cloudwatch_event_rule" "receive_events_mlpa" { name = "${data.aws_default_tags.current.tags.environment-name}-receive-events-mlpa" description = "receive events from mlpa" diff --git a/web/template/fixtures.gohtml b/web/template/fixtures.gohtml index bfa3b1350a..ac2adbf8e4 100644 --- a/web/template/fixtures.gohtml +++ b/web/template/fixtures.gohtml @@ -65,6 +65,7 @@ (item "signedByCertificateProvider" "Signed by certificate provider") (item "signedByAttorneys" "Signed by attorneys") (item "submitted" "Submitted") + (item "perfect" "Perfect") (item "withdrawn" "Withdrawn") (item "registered" "registered") ) }} diff --git a/web/template/layout/donor-lpa-progress.gohtml b/web/template/layout/donor-lpa-progress.gohtml index 4fc1244b5c..42d47e3882 100644 --- a/web/template/layout/donor-lpa-progress.gohtml +++ b/web/template/layout/donor-lpa-progress.gohtml @@ -1,65 +1,14 @@ {{ define "donor-lpa-progress" }} -
- + {{ end }}