From c3125991beaa3012ee8b4af38d9b0b3b47a4329f Mon Sep 17 00:00:00 2001 From: Joshua Hawxwell Date: Wed, 11 Dec 2024 12:44:28 +0000 Subject: [PATCH] Emit lpa-access-granted when an lpa is registered --- cmd/event-received/lpastore_event_handler.go | 46 ++++++ .../lpastore_event_handler_test.go | 133 ++++++++++++++++++ cmd/event-received/main.go | 1 + cmd/event-received/mock_EventClient_test.go | 47 +++++++ cmd/event-received/mock_test.go | 9 +- .../attorneypage/mock_Handler_test.go | 23 +-- internal/attorney/store.go | 1 + internal/attorney/store_test.go | 1 + internal/certificateprovider/store.go | 1 + internal/certificateprovider/store_test.go | 1 + internal/dashboard/dashboarddata/lpa_link.go | 3 + internal/donor/store.go | 2 + internal/donor/store_test.go | 9 +- internal/event/client.go | 5 + internal/event/client_test.go | 5 + internal/event/events.go | 11 ++ internal/event/events_test.go | 10 ++ .../event/testdata/lpa-access-granted.json | 38 +++++ scripts/get_event_schemas.sh | 3 +- 19 files changed, 336 insertions(+), 13 deletions(-) create mode 100644 internal/event/testdata/lpa-access-granted.json diff --git a/cmd/event-received/lpastore_event_handler.go b/cmd/event-received/lpastore_event_handler.go index ee5db73544..f02ec66045 100644 --- a/cmd/event-received/lpastore_event_handler.go +++ b/cmd/event-received/lpastore_event_handler.go @@ -2,11 +2,15 @@ package main import ( "context" + "encoding/base64" "encoding/json" "fmt" "time" "github.com/aws/aws-lambda-go/events" + "github.com/ministryofjustice/opg-modernising-lpa/internal/dashboard/dashboarddata" + "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" + "github.com/ministryofjustice/opg-modernising-lpa/internal/event" ) type lpastoreEventHandler struct{} @@ -24,6 +28,14 @@ func (h *lpastoreEventHandler) Handle(ctx context.Context, factory factory, clou } switch v.ChangeType { + case "REGISTER": + lpaStoreClient, err := factory.LpaStoreClient() + if err != nil { + return fmt.Errorf("could not create LpaStoreClient: %w", err) + } + + return handleRegister(ctx, factory.DynamoClient(), lpaStoreClient, factory.EventClient(), v) + case "STATUTORY_WAITING_PERIOD": return handleStatutoryWaitingPeriod(ctx, factory.DynamoClient(), factory.Now(), v) @@ -38,6 +50,40 @@ func (h *lpastoreEventHandler) Handle(ctx context.Context, factory factory, clou return fmt.Errorf("unknown lpastore event") } +func handleRegister(ctx context.Context, client dynamodbClient, lpaStoreClient LpaStoreClient, eventClient EventClient, v lpaUpdatedEvent) error { + lpa, err := lpaStoreClient.Lpa(ctx, v.UID) + if err != nil { + return fmt.Errorf("error getting lpa: %w", err) + } + + var links []dashboarddata.LpaLink + if err := client.AllByLpaUIDAndPartialSK(ctx, v.UID, dynamo.SubKey(""), &links); err != nil { + return fmt.Errorf("error getting all subs for uid: %w", err) + } + + data := event.LpaAccessGranted{ + UID: v.UID, + LpaType: lpa.Type.String(), + } + + for _, link := range links { + if !link.ActorType.IsDonor() && + !link.ActorType.IsAttorney() && !link.ActorType.IsReplacementAttorney() && + !link.ActorType.IsTrustCorporation() && !link.ActorType.IsReplacementTrustCorporation() { + continue + } + + sub, _ := base64.StdEncoding.DecodeString(link.UserSub()) + + data.Actors = append(data.Actors, event.LpaAccessGrantedActor{ + SubjectID: string(sub), + ActorUID: link.UID.String(), + }) + } + + return eventClient.SendLpaAccessGranted(ctx, data) +} + func handleStatutoryWaitingPeriod(ctx context.Context, client dynamodbClient, now func() time.Time, event lpaUpdatedEvent) error { donor, err := getDonorByLpaUID(ctx, client, event.UID) if err != nil { diff --git a/cmd/event-received/lpastore_event_handler_test.go b/cmd/event-received/lpastore_event_handler_test.go index 5bf6d1d483..e4945a2ce2 100644 --- a/cmd/event-received/lpastore_event_handler_test.go +++ b/cmd/event-received/lpastore_event_handler_test.go @@ -1,13 +1,19 @@ package main import ( + "encoding/base64" "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/actor/actoruid" + "github.com/ministryofjustice/opg-modernising-lpa/internal/dashboard/dashboarddata" "github.com/ministryofjustice/opg-modernising-lpa/internal/donor/donordata" "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" + "github.com/ministryofjustice/opg-modernising-lpa/internal/event" + "github.com/ministryofjustice/opg-modernising-lpa/internal/lpastore/lpadata" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -31,6 +37,133 @@ func TestLpaStoreEventHandlerHandleLpaUpdatedWhenChangeTypeNotExpected(t *testin assert.Nil(t, err) } +func TestLpaStoreEventHandlerHandleLpaUpdatedRegister(t *testing.T) { + v := &events.CloudWatchEvent{ + DetailType: "lpa-updated", + Detail: json.RawMessage(`{"uid":"M-1111-2222-3333","changeType":"REGISTER"}`), + } + + lpaStoreClient := newMockLpaStoreClient(t) + lpaStoreClient.EXPECT(). + Lpa(ctx, "M-1111-2222-3333"). + Return(&lpadata.Lpa{Type: lpadata.LpaTypePersonalWelfare}, nil) + + donorUID := actoruid.New() + attorneyUID := actoruid.New() + replacementTrustCorporationUID := actoruid.New() + + client := newMockDynamodbClient(t) + client.EXPECT(). + AllByLpaUIDAndPartialSK(ctx, "M-1111-2222-3333", dynamo.SubKey(""), mock.Anything). + Return(nil). + SetData([]dashboarddata.LpaLink{{ + SK: dynamo.SubKey(base64.StdEncoding.EncodeToString([]byte("donor-sub"))), + UID: donorUID, + ActorType: actor.TypeDonor, + }, { + SK: dynamo.SubKey(base64.StdEncoding.EncodeToString([]byte("attorney-sub"))), + UID: attorneyUID, + ActorType: actor.TypeAttorney, + }, { + SK: dynamo.SubKey(base64.StdEncoding.EncodeToString([]byte("certificate-provided-sub"))), + UID: actoruid.New(), + ActorType: actor.TypeCertificateProvider, + }, { + SK: dynamo.SubKey(base64.StdEncoding.EncodeToString([]byte("replacement-trust-sub"))), + UID: replacementTrustCorporationUID, + ActorType: actor.TypeReplacementTrustCorporation, + }}) + + eventClient := newMockEventClient(t) + eventClient.EXPECT(). + SendLpaAccessGranted(ctx, event.LpaAccessGranted{ + UID: "M-1111-2222-3333", + LpaType: "personal-welfare", + Actors: []event.LpaAccessGrantedActor{{ + SubjectID: "donor-sub", + ActorUID: donorUID.String(), + }, { + SubjectID: "attorney-sub", + ActorUID: attorneyUID.String(), + }, { + SubjectID: "replacement-trust-sub", + ActorUID: replacementTrustCorporationUID.String(), + }}, + }). + Return(nil) + + factory := newMockFactory(t) + factory.EXPECT().DynamoClient().Return(client) + factory.EXPECT().LpaStoreClient().Return(lpaStoreClient, nil) + factory.EXPECT().EventClient().Return(eventClient) + + handler := &lpastoreEventHandler{} + + err := handler.Handle(ctx, factory, v) + assert.Nil(t, err) +} + +func TestLpaStoreEventHandlerHandleLpaUpdatedRegisterWhenErrorCreatingLpaStore(t *testing.T) { + v := &events.CloudWatchEvent{ + DetailType: "lpa-updated", + Detail: json.RawMessage(`{"uid":"M-1111-2222-3333","changeType":"REGISTER"}`), + } + + factory := newMockFactory(t) + factory.EXPECT().LpaStoreClient().Return(nil, expectedError) + + handler := &lpastoreEventHandler{} + + err := handler.Handle(ctx, factory, v) + assert.ErrorIs(t, err, expectedError) +} + +func TestLpaStoreEventHandlerHandleLpaUpdatedRegisterWhenLpaStoreErrors(t *testing.T) { + lpaStoreClient := newMockLpaStoreClient(t) + lpaStoreClient.EXPECT(). + Lpa(mock.Anything, mock.Anything). + Return(nil, expectedError) + + err := handleRegister(ctx, nil, lpaStoreClient, nil, lpaUpdatedEvent{}) + assert.ErrorIs(t, err, expectedError) +} + +func TestLpaStoreEventHandlerHandleLpaUpdatedRegisterWhenDynamoErrors(t *testing.T) { + lpaStoreClient := newMockLpaStoreClient(t) + lpaStoreClient.EXPECT(). + Lpa(mock.Anything, mock.Anything). + Return(&lpadata.Lpa{}, nil) + + client := newMockDynamodbClient(t) + client.EXPECT(). + AllByLpaUIDAndPartialSK(mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(expectedError) + + err := handleRegister(ctx, client, lpaStoreClient, nil, lpaUpdatedEvent{}) + assert.ErrorIs(t, err, expectedError) +} + +func TestLpaStoreEventHandlerHandleLpaUpdatedRegisterWhenEventClientErrors(t *testing.T) { + lpaStoreClient := newMockLpaStoreClient(t) + lpaStoreClient.EXPECT(). + Lpa(mock.Anything, mock.Anything). + Return(&lpadata.Lpa{}, nil) + + client := newMockDynamodbClient(t) + client.EXPECT(). + AllByLpaUIDAndPartialSK(mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(nil). + SetData([]dashboarddata.LpaLink{}) + + eventClient := newMockEventClient(t) + eventClient.EXPECT(). + SendLpaAccessGranted(mock.Anything, mock.Anything). + Return(expectedError) + + err := handleRegister(ctx, client, lpaStoreClient, eventClient, lpaUpdatedEvent{}) + assert.ErrorIs(t, err, expectedError) +} + func TestLpaStoreEventHandlerHandleLpaUpdatedStatutoryWaitingPeriod(t *testing.T) { event := &events.CloudWatchEvent{ DetailType: "lpa-updated", diff --git a/cmd/event-received/main.go b/cmd/event-received/main.go index 80c39476e3..52842a095a 100644 --- a/cmd/event-received/main.go +++ b/cmd/event-received/main.go @@ -112,6 +112,7 @@ type DocumentStore interface { type EventClient interface { SendApplicationUpdated(ctx context.Context, event event.ApplicationUpdated) error SendCertificateProviderStarted(ctx context.Context, event event.CertificateProviderStarted) error + SendLpaAccessGranted(ctx context.Context, event event.LpaAccessGranted) error } type ScheduledStore interface { diff --git a/cmd/event-received/mock_EventClient_test.go b/cmd/event-received/mock_EventClient_test.go index e25bf44f02..503b3b2bfa 100644 --- a/cmd/event-received/mock_EventClient_test.go +++ b/cmd/event-received/mock_EventClient_test.go @@ -116,6 +116,53 @@ func (_c *mockEventClient_SendCertificateProviderStarted_Call) RunAndReturn(run return _c } +// SendLpaAccessGranted provides a mock function with given fields: ctx, _a1 +func (_m *mockEventClient) SendLpaAccessGranted(ctx context.Context, _a1 event.LpaAccessGranted) error { + ret := _m.Called(ctx, _a1) + + if len(ret) == 0 { + panic("no return value specified for SendLpaAccessGranted") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, event.LpaAccessGranted) error); ok { + r0 = rf(ctx, _a1) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// mockEventClient_SendLpaAccessGranted_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendLpaAccessGranted' +type mockEventClient_SendLpaAccessGranted_Call struct { + *mock.Call +} + +// SendLpaAccessGranted is a helper method to define mock.On call +// - ctx context.Context +// - _a1 event.LpaAccessGranted +func (_e *mockEventClient_Expecter) SendLpaAccessGranted(ctx interface{}, _a1 interface{}) *mockEventClient_SendLpaAccessGranted_Call { + return &mockEventClient_SendLpaAccessGranted_Call{Call: _e.mock.On("SendLpaAccessGranted", ctx, _a1)} +} + +func (_c *mockEventClient_SendLpaAccessGranted_Call) Run(run func(ctx context.Context, _a1 event.LpaAccessGranted)) *mockEventClient_SendLpaAccessGranted_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(event.LpaAccessGranted)) + }) + return _c +} + +func (_c *mockEventClient_SendLpaAccessGranted_Call) Return(_a0 error) *mockEventClient_SendLpaAccessGranted_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockEventClient_SendLpaAccessGranted_Call) RunAndReturn(run func(context.Context, event.LpaAccessGranted) error) *mockEventClient_SendLpaAccessGranted_Call { + _c.Call.Return(run) + return _c +} + // newMockEventClient creates a new instance of mockEventClient. 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 newMockEventClient(t interface { diff --git a/cmd/event-received/mock_test.go b/cmd/event-received/mock_test.go index ea102fb2a7..449965bfd8 100644 --- a/cmd/event-received/mock_test.go +++ b/cmd/event-received/mock_test.go @@ -15,7 +15,14 @@ func (c *mockDynamodbClient_OneByUID_Call) SetData(data any) { } func (c *mockDynamodbClient_One_Call) SetData(data any) { - c.Run(func(ctx context.Context, pk dynamo.PK, sk dynamo.SK, v interface{}) { + c.Run(func(_ context.Context, _ dynamo.PK, _ dynamo.SK, v interface{}) { + b, _ := attributevalue.Marshal(data) + attributevalue.Unmarshal(b, v) + }) +} + +func (c *mockDynamodbClient_AllByLpaUIDAndPartialSK_Call) SetData(data any) { + c.Run(func(_ context.Context, _ string, _ dynamo.SK, v interface{}) { b, _ := attributevalue.Marshal(data) attributevalue.Unmarshal(b, v) }) diff --git a/internal/attorney/attorneypage/mock_Handler_test.go b/internal/attorney/attorneypage/mock_Handler_test.go index c5560b6549..bb99d53534 100644 --- a/internal/attorney/attorneypage/mock_Handler_test.go +++ b/internal/attorney/attorneypage/mock_Handler_test.go @@ -8,6 +8,8 @@ import ( http "net/http" + lpadata "github.com/ministryofjustice/opg-modernising-lpa/internal/lpastore/lpadata" + mock "github.com/stretchr/testify/mock" ) @@ -24,17 +26,17 @@ func (_m *mockHandler) EXPECT() *mockHandler_Expecter { return &mockHandler_Expecter{mock: &_m.Mock} } -// Execute provides a mock function with given fields: data, w, r, details -func (_m *mockHandler) Execute(data appcontext.Data, w http.ResponseWriter, r *http.Request, details *attorneydata.Provided) error { - ret := _m.Called(data, w, r, details) +// Execute provides a mock function with given fields: data, w, r, details, lpa +func (_m *mockHandler) Execute(data appcontext.Data, w http.ResponseWriter, r *http.Request, details *attorneydata.Provided, lpa *lpadata.Lpa) error { + ret := _m.Called(data, w, r, details, lpa) if len(ret) == 0 { panic("no return value specified for Execute") } var r0 error - if rf, ok := ret.Get(0).(func(appcontext.Data, http.ResponseWriter, *http.Request, *attorneydata.Provided) error); ok { - r0 = rf(data, w, r, details) + if rf, ok := ret.Get(0).(func(appcontext.Data, http.ResponseWriter, *http.Request, *attorneydata.Provided, *lpadata.Lpa) error); ok { + r0 = rf(data, w, r, details, lpa) } else { r0 = ret.Error(0) } @@ -52,13 +54,14 @@ type mockHandler_Execute_Call struct { // - w http.ResponseWriter // - r *http.Request // - details *attorneydata.Provided -func (_e *mockHandler_Expecter) Execute(data interface{}, w interface{}, r interface{}, details interface{}) *mockHandler_Execute_Call { - return &mockHandler_Execute_Call{Call: _e.mock.On("Execute", data, w, r, details)} +// - lpa *lpadata.Lpa +func (_e *mockHandler_Expecter) Execute(data interface{}, w interface{}, r interface{}, details interface{}, lpa interface{}) *mockHandler_Execute_Call { + return &mockHandler_Execute_Call{Call: _e.mock.On("Execute", data, w, r, details, lpa)} } -func (_c *mockHandler_Execute_Call) Run(run func(data appcontext.Data, w http.ResponseWriter, r *http.Request, details *attorneydata.Provided)) *mockHandler_Execute_Call { +func (_c *mockHandler_Execute_Call) Run(run func(data appcontext.Data, w http.ResponseWriter, r *http.Request, details *attorneydata.Provided, lpa *lpadata.Lpa)) *mockHandler_Execute_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(appcontext.Data), args[1].(http.ResponseWriter), args[2].(*http.Request), args[3].(*attorneydata.Provided)) + run(args[0].(appcontext.Data), args[1].(http.ResponseWriter), args[2].(*http.Request), args[3].(*attorneydata.Provided), args[4].(*lpadata.Lpa)) }) return _c } @@ -68,7 +71,7 @@ func (_c *mockHandler_Execute_Call) Return(_a0 error) *mockHandler_Execute_Call return _c } -func (_c *mockHandler_Execute_Call) RunAndReturn(run func(appcontext.Data, http.ResponseWriter, *http.Request, *attorneydata.Provided) error) *mockHandler_Execute_Call { +func (_c *mockHandler_Execute_Call) RunAndReturn(run func(appcontext.Data, http.ResponseWriter, *http.Request, *attorneydata.Provided, *lpadata.Lpa) error) *mockHandler_Execute_Call { _c.Call.Return(run) return _c } diff --git a/internal/attorney/store.go b/internal/attorney/store.go index 6ba6898536..c4764662e3 100644 --- a/internal/attorney/store.go +++ b/internal/attorney/store.go @@ -70,6 +70,7 @@ func (s *Store) Create(ctx context.Context, shareCode sharecodedata.Link, email Create(dashboarddata.LpaLink{ PK: dynamo.LpaKey(data.LpaID), SK: dynamo.SubKey(data.SessionID), + UID: shareCode.ActorUID, DonorKey: shareCode.LpaOwnerKey, ActorType: actor.TypeAttorney, UpdatedAt: s.now(), diff --git a/internal/attorney/store_test.go b/internal/attorney/store_test.go index a132bbd097..353893b82c 100644 --- a/internal/attorney/store_test.go +++ b/internal/attorney/store_test.go @@ -62,6 +62,7 @@ func TestAttorneyStoreCreate(t *testing.T) { PK: dynamo.LpaKey("123"), SK: dynamo.SubKey("456"), DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey("donor")), + UID: uid, ActorType: actor.TypeAttorney, UpdatedAt: now, }, diff --git a/internal/certificateprovider/store.go b/internal/certificateprovider/store.go index 19ce5e85d9..f38ee08ed8 100644 --- a/internal/certificateprovider/store.go +++ b/internal/certificateprovider/store.go @@ -70,6 +70,7 @@ func (s *Store) Create(ctx context.Context, shareCode sharecodedata.Link, email PK: dynamo.LpaKey(data.LpaID), SK: dynamo.SubKey(data.SessionID), DonorKey: shareCode.LpaOwnerKey, + UID: shareCode.ActorUID, ActorType: actor.TypeCertificateProvider, UpdatedAt: s.now(), }). diff --git a/internal/certificateprovider/store_test.go b/internal/certificateprovider/store_test.go index ceae138ea3..a6dce5a281 100644 --- a/internal/certificateprovider/store_test.go +++ b/internal/certificateprovider/store_test.go @@ -38,6 +38,7 @@ func TestCertificateProviderStoreCreate(t *testing.T) { PK: dynamo.LpaKey("lpa-id"), SK: dynamo.SubKey("session-id"), DonorKey: shareCode.LpaOwnerKey, + UID: uid, ActorType: actor.TypeCertificateProvider, UpdatedAt: testNow, }, diff --git a/internal/dashboard/dashboarddata/lpa_link.go b/internal/dashboard/dashboarddata/lpa_link.go index ad1056cf9c..b1114661f5 100644 --- a/internal/dashboard/dashboarddata/lpa_link.go +++ b/internal/dashboard/dashboarddata/lpa_link.go @@ -5,6 +5,7 @@ import ( "time" "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" + "github.com/ministryofjustice/opg-modernising-lpa/internal/actor/actoruid" "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" ) @@ -16,6 +17,8 @@ type LpaLink struct { SK dynamo.SubKeyType // DonorKey is the donorKey for the donor DonorKey dynamo.LpaOwnerKeyType + // UID is the UID for the linked actor + UID actoruid.UID // ActorType is the type for the current user ActorType actor.Type // UpdatedAt is set to allow this data to be queried from SKUpdatedAtIndex diff --git a/internal/donor/store.go b/internal/donor/store.go index c0af093518..faa7273dcd 100644 --- a/internal/donor/store.go +++ b/internal/donor/store.go @@ -137,6 +137,7 @@ func (s *Store) Create(ctx context.Context) (*donordata.Provided, error) { PK: dynamo.LpaKey(lpaID), SK: dynamo.SubKey(data.SessionID), DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey(data.SessionID)), + UID: donor.Donor.UID, ActorType: actor.TypeDonor, UpdatedAt: s.now(), }) @@ -212,6 +213,7 @@ func (s *Store) Link(ctx context.Context, shareCode sharecodedata.Link, donorEma PK: shareCode.LpaKey, SK: dynamo.SubKey(data.SessionID), DonorKey: shareCode.LpaOwnerKey, + UID: shareCode.ActorUID, ActorType: actor.TypeDonor, UpdatedAt: s.now(), }). diff --git a/internal/donor/store_test.go b/internal/donor/store_test.go index 3f82f3c766..7509e57a19 100644 --- a/internal/donor/store_test.go +++ b/internal/donor/store_test.go @@ -543,7 +543,14 @@ func TestDonorStoreCreate(t *testing.T) { Creates: []any{ dynamo.Keys{PK: dynamo.LpaKey("10100000"), SK: dynamo.ReservedKey(dynamo.DonorKey)}, donor, - dashboarddata.LpaLink{PK: dynamo.LpaKey("10100000"), SK: dynamo.SubKey("an-id"), DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), ActorType: actor.TypeDonor, UpdatedAt: testNow}, + dashboarddata.LpaLink{ + PK: dynamo.LpaKey("10100000"), + SK: dynamo.SubKey("an-id"), + DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), + UID: donor.Donor.UID, + ActorType: actor.TypeDonor, + UpdatedAt: testNow, + }, }, }). Return(nil) diff --git a/internal/event/client.go b/internal/event/client.go index e3c84bf0b4..27325f5e96 100644 --- a/internal/event/client.go +++ b/internal/event/client.go @@ -25,6 +25,7 @@ var events = map[any]string{ (*AttorneyStarted)(nil): "attorney-started", (*IdentityCheckMismatched)(nil): "identity-check-mismatched", (*CorrespondentUpdated)(nil): "correspondent-updated", + (*LpaAccessGranted)(nil): "lpa-access-granted", } type eventbridgeClient interface { @@ -87,6 +88,10 @@ func (c *Client) SendCorrespondentUpdated(ctx context.Context, event Corresponde return send[CorrespondentUpdated](ctx, c, event) } +func (c *Client) SendLpaAccessGranted(ctx context.Context, event LpaAccessGranted) error { + return send[LpaAccessGranted](ctx, c, event) +} + func send[T any](ctx context.Context, c *Client, detail any) error { detailType, ok := events[(*T)(nil)] if !ok { diff --git a/internal/event/client_test.go b/internal/event/client_test.go index 4fba00b058..a092a3d4d1 100644 --- a/internal/event/client_test.go +++ b/internal/event/client_test.go @@ -78,6 +78,11 @@ func TestClientSendEvents(t *testing.T) { return func(client *Client) error { return client.SendCorrespondentUpdated(ctx, event) }, event }, + "lpa-access-granted": func() (func(*Client) error, any) { + event := LpaAccessGranted{UID: "a"} + + return func(client *Client) error { return client.SendLpaAccessGranted(ctx, event) }, event + }, } for eventName, setup := range testcases { diff --git a/internal/event/events.go b/internal/event/events.go index a6b874950b..61d66d02b3 100644 --- a/internal/event/events.go +++ b/internal/event/events.go @@ -95,3 +95,14 @@ type CorrespondentUpdated struct { Phone string `json:"phone,omitempty"` Address *place.Address `json:"address,omitempty"` } + +type LpaAccessGranted struct { + UID string `json:"uid"` + LpaType string `json:"lpaType"` + Actors []LpaAccessGrantedActor `json:"actors"` +} + +type LpaAccessGrantedActor struct { + ActorUID string `json:"actorUid"` + SubjectID string `json:"subjectId"` +} diff --git a/internal/event/events_test.go b/internal/event/events_test.go index 88b5654298..7b00b81f3f 100644 --- a/internal/event/events_test.go +++ b/internal/event/events_test.go @@ -161,6 +161,16 @@ var eventTests = map[string]map[string]any{ }, }, }, + "lpa-access-granted": { + "valid": LpaAccessGranted{ + UID: "M-1111-2222-3333", + LpaType: "personal-welfare", + Actors: []LpaAccessGrantedActor{{ + ActorUID: "9ac5cb7c-fc75-40c7-8e53-059f36dbbe3d", + SubjectID: "urn:fdc:gov.uk:2022:XXXX-XXXXXX", + }}, + }, + }, } func TestEventSchema(t *testing.T) { diff --git a/internal/event/testdata/lpa-access-granted.json b/internal/event/testdata/lpa-access-granted.json new file mode 100644 index 0000000000..ec558de209 --- /dev/null +++ b/internal/event/testdata/lpa-access-granted.json @@ -0,0 +1,38 @@ +{ + "$id": "https://opg.service.justice.gov.uk/opg.poas.use/lpa-access-granted.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "opg.poas.use/lpa-access-granted", + "type": "object", + "properties": { + "uid": { + "type": "string", + "description": "The UID of the LPA", + "pattern": "^M(-[A-Z0-9]{4}){3}$" + }, + "lpaType": { + "type": "string", + "description": "The type of LPA", + "enum": ["personal-welfare", "property-and-affairs"] + }, + "actors": { + "type": "array", + "items": { + "type": "object", + "required": [ + "actorUid", + "subjectId" + ], + "properties": { + "actorUid": { + "type": "string", + "format": "uuid" + }, + "subjectId": { + "type": "string" + } + } + } + } + }, + "required": ["uid", "lpaType", "actors"] +} diff --git a/scripts/get_event_schemas.sh b/scripts/get_event_schemas.sh index 23cb390a20..b994edc685 100644 --- a/scripts/get_event_schemas.sh +++ b/scripts/get_event_schemas.sh @@ -13,7 +13,8 @@ for v in uid-requested \ certificate-provider-started \ attorney-started \ identity-check-mismatched \ - correspondent-updated + correspondent-updated \ + lpa-access-granted do echo $v curl -o internal/event/testdata/$v.json "https://raw.githubusercontent.com/ministryofjustice/opg-event-store/main/domains/POAS/events/$v/schema.json"