From 5c2ada2d71d84c67d860944b6323ef8c4d0bb4a0 Mon Sep 17 00:00:00 2001 From: Joshua Hawxwell Date: Thu, 25 Apr 2024 13:13:00 +0100 Subject: [PATCH] MLPAB-2034 Type the dynamo keys (#1198) --- .../cloud_watch_event_handler.go | 10 +- .../cloud_watch_event_handler_test.go | 90 +++--- cmd/event-received/handlers.go | 4 +- cmd/event-received/handlers_test.go | 32 +- cmd/event-received/main.go | 10 +- .../mock_dynamodbClient_test.go | 74 ++--- cypress/e2e/attorney/trust-corporation.cy.js | 2 +- internal/actor/attorney_provided.go | 4 +- .../actor/certificate_provider_provided.go | 4 +- internal/actor/donor_provided.go | 4 +- internal/actor/donor_provided_test.go | 4 +- internal/actor/organisation.go | 10 +- internal/actor/share_code_data.go | 4 +- internal/app/app.go | 24 +- internal/app/attorney_store.go | 4 +- internal/app/attorney_store_test.go | 19 +- internal/app/certificate_provider_store.go | 4 +- .../app/certificate_provider_store_test.go | 21 +- internal/app/dashboard_store.go | 24 +- internal/app/dashboard_store_test.go | 100 +++--- internal/app/document_store.go | 4 +- internal/app/document_store_test.go | 58 ++-- internal/app/donor_store.go | 25 +- internal/app/donor_store_test.go | 180 +++++------ internal/app/evidence_received_store_test.go | 6 +- internal/app/member_store_test.go | 75 ++--- internal/app/mock_DynamoClient_test.go | 168 +++++----- internal/app/mock_DynamoUpdateClient_test.go | 22 +- .../mock_ShareCodeStoreDynamoClient_test.go | 53 ++-- internal/app/organisation_store.go | 8 +- internal/app/organisation_store_test.go | 34 +- internal/app/share_code_store.go | 34 +- internal/app/share_code_store_test.go | 58 +++- internal/app/uid_store.go | 8 +- internal/app/uid_store_test.go | 17 +- internal/dynamo/client.go | 75 +++-- internal/dynamo/client_test.go | 104 +++--- internal/dynamo/complex_keys.go | 254 +++++++++++++++ internal/dynamo/complex_keys_test.go | 297 ++++++++++++++++++ internal/dynamo/keys.go | 224 ++++++++++--- internal/dynamo/keys_test.go | 137 +++++--- internal/dynamo/mock_PK_test.go | 77 +++++ internal/dynamo/mock_SK_test.go | 77 +++++ internal/lpastore/resolving_service.go | 5 +- internal/lpastore/resolving_service_test.go | 16 +- internal/page/document.go | 5 +- internal/page/donor/upload_evidence_test.go | 49 +-- .../page/fixtures/certificate_provider.go | 2 +- internal/page/fixtures/supporter.go | 13 +- internal/page/supporter/dashboard_test.go | 6 +- internal/page/supporter/donor_access_test.go | 22 +- .../page/supporter/mock_DonorStore_test.go | 16 +- internal/page/supporter/register.go | 2 +- internal/search/client.go | 12 +- internal/search/client_test.go | 32 +- .../opensearch_ingestion_pipeline.tf | 4 +- 56 files changed, 1806 insertions(+), 821 deletions(-) create mode 100644 internal/dynamo/complex_keys.go create mode 100644 internal/dynamo/complex_keys_test.go create mode 100644 internal/dynamo/mock_PK_test.go create mode 100644 internal/dynamo/mock_SK_test.go diff --git a/cmd/event-received/cloud_watch_event_handler.go b/cmd/event-received/cloud_watch_event_handler.go index d03e3ea3de..d894533517 100644 --- a/cmd/event-received/cloud_watch_event_handler.go +++ b/cmd/event-received/cloud_watch_event_handler.go @@ -119,16 +119,16 @@ func handleEvidenceReceived(ctx context.Context, client dynamodbClient, event ev return fmt.Errorf("failed to unmarshal detail: %w", err) } - var key dynamo.Key + var key dynamo.Keys if err := client.OneByUID(ctx, v.UID, &key); err != nil { return fmt.Errorf("failed to resolve uid: %w", err) } - if key.PK == "" { + if key.PK == nil { return fmt.Errorf("PK missing from LPA in response") } - if err := client.Put(ctx, map[string]string{"PK": key.PK, "SK": dynamo.EvidenceReceivedKey()}); err != nil { + if err := client.Put(ctx, map[string]string{"PK": key.PK.PK(), "SK": dynamo.EvidenceReceivedKey().SK()}); err != nil { return fmt.Errorf("failed to persist evidence received: %w", err) } @@ -209,7 +209,7 @@ func handleDonorSubmissionCompleted(ctx context.Context, client dynamodbClient, return fmt.Errorf("failed to unmarshal detail: %w", err) } - var key dynamo.Key + var key dynamo.Keys if err := client.OneByUID(ctx, v.UID, &key); !errors.Is(err, dynamo.NotFoundError{}) { return err } @@ -218,7 +218,7 @@ func handleDonorSubmissionCompleted(ctx context.Context, client dynamodbClient, if err := client.Put(ctx, &actor.DonorProvidedDetails{ PK: dynamo.LpaKey(lpaID), - SK: dynamo.DonorKey("PAPER"), + SK: dynamo.LpaOwnerKey(dynamo.DonorKey("PAPER")), LpaID: lpaID, LpaUID: v.UID, CreatedAt: now(), diff --git a/cmd/event-received/cloud_watch_event_handler_test.go b/cmd/event-received/cloud_watch_event_handler_test.go index f3e983f769..b32b054c20 100644 --- a/cmd/event-received/cloud_watch_event_handler_test.go +++ b/cmd/event-received/cloud_watch_event_handler_test.go @@ -99,14 +99,14 @@ func TestHandleEvidenceReceived(t *testing.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.Key{PK: "LPA#123"}) + b, _ := json.Marshal(dynamo.Keys{PK: dynamo.LpaKey("123")}) json.Unmarshal(b, v) return nil }) client.EXPECT(). Put(ctx, map[string]string{ - "PK": "LPA#123", - "SK": "#EVIDENCE_RECEIVED", + "PK": dynamo.LpaKey("123").PK(), + "SK": dynamo.EvidenceReceivedKey().SK(), }). Return(nil) @@ -139,7 +139,7 @@ func TestHandleEvidenceReceivedWhenLpaMissingPK(t *testing.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.Key{}) + b, _ := json.Marshal(dynamo.Keys{}) json.Unmarshal(b, v) return nil }) @@ -158,14 +158,14 @@ func TestHandleEvidenceReceivedWhenClientPutError(t *testing.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.Key{PK: "LPA#123"}) + b, _ := json.Marshal(dynamo.Keys{PK: dynamo.LpaKey("123")}) json.Unmarshal(b, v) return nil }) client.EXPECT(). Put(ctx, map[string]string{ - "PK": "LPA#123", - "SK": "#EVIDENCE_RECEIVED", + "PK": dynamo.LpaKey("123").PK(), + "SK": dynamo.EvidenceReceivedKey().SK(), }). Return(expectedError) @@ -180,21 +180,21 @@ func TestHandleFeeApproved(t *testing.T) { } now := time.Now() - updated := &actor.DonorProvidedDetails{PK: "LPA#123", SK: "#DONOR#456", Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskCompleted}, UpdatedAt: now} + updated := &actor.DonorProvidedDetails{PK: dynamo.LpaKey("123"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("456")), Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskCompleted}, UpdatedAt: now} 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.Key{PK: "LPA#123", SK: "#DONOR#456"}) + b, _ := json.Marshal(dynamo.Keys{PK: dynamo.LpaKey("123"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("456"))}) json.Unmarshal(b, v) return nil }) client. - On("One", ctx, "LPA#123", "#DONOR#456", mock.Anything). - Return(func(ctx context.Context, pk, sk string, v interface{}) error { - b, _ := json.Marshal(actor.DonorProvidedDetails{PK: "LPA#123", SK: "#DONOR#456", Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskPending}}) + 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")), Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskPending}}) json.Unmarshal(b, v) return nil }) @@ -228,14 +228,14 @@ func TestHandleFeeApprovedWhenDynamoClientPutError(t *testing.T) { client. On("OneByUID", ctx, "M-1111-2222-3333", mock.Anything). Return(func(ctx context.Context, uid string, v interface{}) error { - b, _ := json.Marshal(actor.DonorProvidedDetails{PK: "LPA#123", SK: "#DONOR#456"}) + b, _ := json.Marshal(actor.DonorProvidedDetails{PK: dynamo.LpaKey("123"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("456"))}) json.Unmarshal(b, v) return nil }) client. - On("One", ctx, "LPA#123", "#DONOR#456", mock.Anything). - Return(func(ctx context.Context, pk, sk string, v interface{}) error { - b, _ := json.Marshal(actor.DonorProvidedDetails{PK: "LPA#123", SK: "#DONOR#456", Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskPending}}) + 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")), Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskPending}}) json.Unmarshal(b, v) return nil }) @@ -259,14 +259,14 @@ func TestHandleFeeApprovedWhenShareCodeSenderError(t *testing.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.Key{PK: "LPA#123", SK: "#DONOR#456"}) + b, _ := json.Marshal(dynamo.Keys{PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("456")}) json.Unmarshal(b, v) return nil }) client. - On("One", ctx, "LPA#123", "#DONOR#456", mock.Anything). - Return(func(ctx context.Context, pk, sk string, v interface{}) error { - b, _ := json.Marshal(actor.DonorProvidedDetails{PK: "LPA#123", SK: "#DONOR#456", Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskPending}}) + 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")), Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskPending}}) json.Unmarshal(b, v) return nil }) @@ -290,21 +290,21 @@ func TestHandleFeeApprovedWhenLpaStoreError(t *testing.T) { } now := time.Now() - updated := &actor.DonorProvidedDetails{PK: "LPA#123", SK: "#DONOR#456", Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskCompleted}, UpdatedAt: now} + updated := &actor.DonorProvidedDetails{PK: dynamo.LpaKey("123"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("456")), Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskCompleted}, UpdatedAt: now} 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.Key{PK: "LPA#123", SK: "#DONOR#456"}) + b, _ := json.Marshal(dynamo.Keys{PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("456")}) json.Unmarshal(b, v) return nil }) client. - On("One", ctx, "LPA#123", "#DONOR#456", mock.Anything). - Return(func(ctx context.Context, pk, sk string, v interface{}) error { - b, _ := json.Marshal(actor.DonorProvidedDetails{PK: "LPA#123", SK: "#DONOR#456", Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskPending}}) + 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")), Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskPending}}) json.Unmarshal(b, v) return nil }) @@ -333,21 +333,21 @@ func TestHandleMoreEvidenceRequired(t *testing.T) { } now := time.Now() - updated := &actor.DonorProvidedDetails{PK: "LPA#123", SK: "#DONOR#456", Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskMoreEvidenceRequired}, UpdatedAt: now} + updated := &actor.DonorProvidedDetails{PK: dynamo.LpaKey("123"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("456")), Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskMoreEvidenceRequired}, UpdatedAt: now} 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.Key{PK: "LPA#123", SK: "#DONOR#456"}) + b, _ := json.Marshal(dynamo.Keys{PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("456")}) json.Unmarshal(b, v) return nil }) client. - On("One", ctx, "LPA#123", "#DONOR#456", mock.Anything). - Return(func(ctx context.Context, pk, sk string, v interface{}) error { - b, _ := json.Marshal(actor.DonorProvidedDetails{PK: "LPA#123", SK: "#DONOR#456", Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskPending}}) + 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")), Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskPending}}) json.Unmarshal(b, v) return nil }) @@ -366,21 +366,21 @@ func TestHandleMoreEvidenceRequiredWhenPutError(t *testing.T) { } now := time.Now() - updated := &actor.DonorProvidedDetails{PK: "LPA#123", SK: "#DONOR#456", Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskMoreEvidenceRequired}, UpdatedAt: now} + updated := &actor.DonorProvidedDetails{PK: dynamo.LpaKey("123"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("456")), Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskMoreEvidenceRequired}, UpdatedAt: now} 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.Key{PK: "LPA#123", SK: "#DONOR#456"}) + b, _ := json.Marshal(dynamo.Keys{PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("456")}) json.Unmarshal(b, v) return nil }) client. - On("One", ctx, "LPA#123", "#DONOR#456", mock.Anything). - Return(func(ctx context.Context, pk, sk string, v interface{}) error { - b, _ := json.Marshal(actor.DonorProvidedDetails{PK: "LPA#123", SK: "#DONOR#456", Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskPending}}) + 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")), Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskPending}}) json.Unmarshal(b, v) return nil }) @@ -399,21 +399,21 @@ func TestHandleFeeDenied(t *testing.T) { } now := time.Now() - updated := &actor.DonorProvidedDetails{PK: "LPA#123", SK: "#DONOR#456", Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskDenied}, UpdatedAt: now} + updated := &actor.DonorProvidedDetails{PK: dynamo.LpaKey("123"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("456")), Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskDenied}, UpdatedAt: now} 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.Key{PK: "LPA#123", SK: "#DONOR#456"}) + b, _ := json.Marshal(dynamo.Keys{PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("456")}) json.Unmarshal(b, v) return nil }) client. - On("One", ctx, "LPA#123", "#DONOR#456", mock.Anything). - Return(func(ctx context.Context, pk, sk string, v interface{}) error { - b, _ := json.Marshal(actor.DonorProvidedDetails{PK: "LPA#123", SK: "#DONOR#456", Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskPending}}) + 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")), Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskPending}}) json.Unmarshal(b, v) return nil }) @@ -437,14 +437,14 @@ func TestHandleFeeDeniedWhenPutError(t *testing.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.Key{PK: "LPA#123", SK: "#DONOR#456"}) + b, _ := json.Marshal(dynamo.Keys{PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("456")}) json.Unmarshal(b, v) return nil }) client. - On("One", ctx, "LPA#123", "#DONOR#456", mock.Anything). - Return(func(ctx context.Context, pk, sk string, v interface{}) error { - b, _ := json.Marshal(actor.DonorProvidedDetails{PK: "LPA#123", SK: "#DONOR#456", Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskPending}}) + 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")), Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskPending}}) json.Unmarshal(b, v) return nil }) @@ -482,7 +482,7 @@ func TestHandleDonorSubmissionCompleted(t *testing.T) { client.EXPECT(). Put(ctx, &actor.DonorProvidedDetails{ PK: dynamo.LpaKey(testUuidString), - SK: dynamo.DonorKey("PAPER"), + SK: dynamo.LpaOwnerKey(dynamo.DonorKey("PAPER")), LpaID: testUuidString, LpaUID: "M-1111-2222-3333", CreatedAt: testNow, diff --git a/cmd/event-received/handlers.go b/cmd/event-received/handlers.go index a5b656ee7f..e767ca27cc 100644 --- a/cmd/event-received/handlers.go +++ b/cmd/event-received/handlers.go @@ -66,12 +66,12 @@ func putDonor(ctx context.Context, donor *actor.DonorProvidedDetails, now func() } func getDonorByLpaUID(ctx context.Context, client dynamodbClient, uid string) (*actor.DonorProvidedDetails, error) { - var key dynamo.Key + var key dynamo.Keys if err := client.OneByUID(ctx, uid, &key); err != nil { return nil, fmt.Errorf("failed to resolve uid: %w", err) } - if key.PK == "" { + if key.PK == nil { return nil, fmt.Errorf("PK missing from LPA in response") } diff --git a/cmd/event-received/handlers_test.go b/cmd/event-received/handlers_test.go index 72a635e487..5a7fe13e46 100644 --- a/cmd/event-received/handlers_test.go +++ b/cmd/event-received/handlers_test.go @@ -41,13 +41,13 @@ func TestHandleObjectTagsAdded(t *testing.T) { dynamoClient. On("OneByUID", ctx, "M-1111-2222-3333", mock.Anything). Return(func(ctx context.Context, uid string, v interface{}) error { - b, _ := json.Marshal(dynamo.Key{PK: "LPA#123", SK: "#DONOR#456"}) + b, _ := json.Marshal(dynamo.Keys{PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("456")}) json.Unmarshal(b, v) return nil }) dynamoClient. - On("One", ctx, "LPA#123", "#DONOR#456", mock.Anything). - Return(func(ctx context.Context, pk, sk string, v interface{}) error { + 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{LpaID: "123", Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskPending}}) json.Unmarshal(b, v) return nil @@ -127,13 +127,13 @@ func TestHandleObjectTagsAddedWhenDynamoClientOneByUIDError(t *testing.T) { dynamoClient. On("OneByUID", ctx, "M-1111-2222-3333", mock.Anything). Return(func(ctx context.Context, uid string, v interface{}) error { - b, _ := json.Marshal(dynamo.Key{PK: "LPA#123", SK: "#DONOR#456"}) + b, _ := json.Marshal(dynamo.Keys{PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("456")}) json.Unmarshal(b, v) return nil }) dynamoClient. - On("One", ctx, "LPA#123", "#DONOR#456", mock.Anything). - Return(func(ctx context.Context, pk, sk string, v interface{}) error { + 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{LpaID: "123", Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskPending}}) json.Unmarshal(b, v) return expectedError @@ -161,13 +161,13 @@ func TestHandleObjectTagsAddedWhenDocumentStoreUpdateScanResultsError(t *testing dynamoClient. On("OneByUID", ctx, "M-1111-2222-3333", mock.Anything). Return(func(ctx context.Context, uid string, v interface{}) error { - b, _ := json.Marshal(dynamo.Key{PK: "LPA#123", SK: "#DONOR#456"}) + b, _ := json.Marshal(dynamo.Keys{PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("456")}) json.Unmarshal(b, v) return nil }) dynamoClient. - On("One", ctx, "LPA#123", "#DONOR#456", mock.Anything). - Return(func(ctx context.Context, pk, sk string, v interface{}) error { + 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{LpaID: "123", Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskPending}}) json.Unmarshal(b, v) return nil @@ -183,19 +183,19 @@ func TestHandleObjectTagsAddedWhenDocumentStoreUpdateScanResultsError(t *testing } func TestGetLpaByUID(t *testing.T) { - expectedDonor := &actor.DonorProvidedDetails{PK: "LPA#123", SK: "#DONOR#456"} + expectedDonor := &actor.DonorProvidedDetails{PK: dynamo.LpaKey("123"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("456"))} 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.Key{PK: "LPA#123", SK: "#DONOR#456"}) + b, _ := json.Marshal(dynamo.Keys{PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("456")}) json.Unmarshal(b, v) return nil }) client. - On("One", ctx, "LPA#123", "#DONOR#456", mock.Anything). - Return(func(ctx context.Context, pk, sk string, v interface{}) error { + 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(expectedDonor) json.Unmarshal(b, v) return nil @@ -224,7 +224,7 @@ func TestGetLpaByUIDWhenPKMissing(t *testing.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.Key{SK: "#DONOR#456"}) + b, _ := json.Marshal(dynamo.Keys{SK: dynamo.DonorKey("456")}) json.Unmarshal(b, v) return nil }) @@ -240,12 +240,12 @@ func TestGetLpaByUIDWhenClientOneError(t *testing.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.Key{PK: "LPA#123", SK: "#DONOR#456"}) + b, _ := json.Marshal(dynamo.Keys{PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("456")}) json.Unmarshal(b, v) return nil }) client.EXPECT(). - One(ctx, "LPA#123", "#DONOR#456", mock.Anything). + One(ctx, dynamo.LpaKey("123"), dynamo.DonorKey("456"), mock.Anything). Return(expectedError) lpa, err := getDonorByLpaUID(ctx, client, "M-1111-2222-3333") diff --git a/cmd/event-received/main.go b/cmd/event-received/main.go index 7106aee619..ed2e2ecf68 100644 --- a/cmd/event-received/main.go +++ b/cmd/event-received/main.go @@ -29,13 +29,13 @@ type uidEvent struct { } type dynamodbClient interface { - One(ctx context.Context, pk, sk string, v interface{}) error + One(ctx context.Context, pk dynamo.PK, sk dynamo.SK, v interface{}) error OneByUID(ctx context.Context, uid string, v interface{}) error - OneByPK(ctx context.Context, pk string, v interface{}) error - OneBySK(ctx context.Context, sk string, v interface{}) error + OneByPK(ctx context.Context, pk dynamo.PK, v interface{}) error + OneBySK(ctx context.Context, sk dynamo.SK, v interface{}) error Put(ctx context.Context, v interface{}) error - UpdateReturn(ctx context.Context, pk, sk string, values map[string]dynamodbtypes.AttributeValue, expression string) (map[string]dynamodbtypes.AttributeValue, error) - DeleteOne(ctx context.Context, pk, sk string) error + UpdateReturn(ctx context.Context, pk dynamo.PK, sk dynamo.SK, values map[string]dynamodbtypes.AttributeValue, expression string) (map[string]dynamodbtypes.AttributeValue, error) + DeleteOne(ctx context.Context, pk dynamo.PK, sk dynamo.SK) error } type s3Client interface { diff --git a/cmd/event-received/mock_dynamodbClient_test.go b/cmd/event-received/mock_dynamodbClient_test.go index 71207b3f9f..7695f79dbb 100644 --- a/cmd/event-received/mock_dynamodbClient_test.go +++ b/cmd/event-received/mock_dynamodbClient_test.go @@ -5,8 +5,10 @@ package main import ( context "context" - types "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" + dynamo "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" mock "github.com/stretchr/testify/mock" + + types "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" ) // mockDynamodbClient is an autogenerated mock type for the dynamodbClient type @@ -23,7 +25,7 @@ func (_m *mockDynamodbClient) EXPECT() *mockDynamodbClient_Expecter { } // DeleteOne provides a mock function with given fields: ctx, pk, sk -func (_m *mockDynamodbClient) DeleteOne(ctx context.Context, pk string, sk string) error { +func (_m *mockDynamodbClient) DeleteOne(ctx context.Context, pk dynamo.PK, sk dynamo.SK) error { ret := _m.Called(ctx, pk, sk) if len(ret) == 0 { @@ -31,7 +33,7 @@ func (_m *mockDynamodbClient) DeleteOne(ctx context.Context, pk string, sk strin } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.PK, dynamo.SK) error); ok { r0 = rf(ctx, pk, sk) } else { r0 = ret.Error(0) @@ -47,15 +49,15 @@ type mockDynamodbClient_DeleteOne_Call struct { // DeleteOne is a helper method to define mock.On call // - ctx context.Context -// - pk string -// - sk string +// - pk dynamo.PK +// - sk dynamo.SK func (_e *mockDynamodbClient_Expecter) DeleteOne(ctx interface{}, pk interface{}, sk interface{}) *mockDynamodbClient_DeleteOne_Call { return &mockDynamodbClient_DeleteOne_Call{Call: _e.mock.On("DeleteOne", ctx, pk, sk)} } -func (_c *mockDynamodbClient_DeleteOne_Call) Run(run func(ctx context.Context, pk string, sk string)) *mockDynamodbClient_DeleteOne_Call { +func (_c *mockDynamodbClient_DeleteOne_Call) Run(run func(ctx context.Context, pk dynamo.PK, sk dynamo.SK)) *mockDynamodbClient_DeleteOne_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(string)) + run(args[0].(context.Context), args[1].(dynamo.PK), args[2].(dynamo.SK)) }) return _c } @@ -65,13 +67,13 @@ func (_c *mockDynamodbClient_DeleteOne_Call) Return(_a0 error) *mockDynamodbClie return _c } -func (_c *mockDynamodbClient_DeleteOne_Call) RunAndReturn(run func(context.Context, string, string) error) *mockDynamodbClient_DeleteOne_Call { +func (_c *mockDynamodbClient_DeleteOne_Call) RunAndReturn(run func(context.Context, dynamo.PK, dynamo.SK) error) *mockDynamodbClient_DeleteOne_Call { _c.Call.Return(run) return _c } // One provides a mock function with given fields: ctx, pk, sk, v -func (_m *mockDynamodbClient) One(ctx context.Context, pk string, sk string, v interface{}) error { +func (_m *mockDynamodbClient) One(ctx context.Context, pk dynamo.PK, sk dynamo.SK, v interface{}) error { ret := _m.Called(ctx, pk, sk, v) if len(ret) == 0 { @@ -79,7 +81,7 @@ func (_m *mockDynamodbClient) One(ctx context.Context, pk string, sk string, v i } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, interface{}) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.PK, dynamo.SK, interface{}) error); ok { r0 = rf(ctx, pk, sk, v) } else { r0 = ret.Error(0) @@ -95,16 +97,16 @@ type mockDynamodbClient_One_Call struct { // One is a helper method to define mock.On call // - ctx context.Context -// - pk string -// - sk string +// - pk dynamo.PK +// - sk dynamo.SK // - v interface{} func (_e *mockDynamodbClient_Expecter) One(ctx interface{}, pk interface{}, sk interface{}, v interface{}) *mockDynamodbClient_One_Call { return &mockDynamodbClient_One_Call{Call: _e.mock.On("One", ctx, pk, sk, v)} } -func (_c *mockDynamodbClient_One_Call) Run(run func(ctx context.Context, pk string, sk string, v interface{})) *mockDynamodbClient_One_Call { +func (_c *mockDynamodbClient_One_Call) Run(run func(ctx context.Context, pk dynamo.PK, sk dynamo.SK, v interface{})) *mockDynamodbClient_One_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(interface{})) + run(args[0].(context.Context), args[1].(dynamo.PK), args[2].(dynamo.SK), args[3].(interface{})) }) return _c } @@ -114,13 +116,13 @@ func (_c *mockDynamodbClient_One_Call) Return(_a0 error) *mockDynamodbClient_One return _c } -func (_c *mockDynamodbClient_One_Call) RunAndReturn(run func(context.Context, string, string, interface{}) error) *mockDynamodbClient_One_Call { +func (_c *mockDynamodbClient_One_Call) RunAndReturn(run func(context.Context, dynamo.PK, dynamo.SK, interface{}) error) *mockDynamodbClient_One_Call { _c.Call.Return(run) return _c } // OneByPK provides a mock function with given fields: ctx, pk, v -func (_m *mockDynamodbClient) OneByPK(ctx context.Context, pk string, v interface{}) error { +func (_m *mockDynamodbClient) OneByPK(ctx context.Context, pk dynamo.PK, v interface{}) error { ret := _m.Called(ctx, pk, v) if len(ret) == 0 { @@ -128,7 +130,7 @@ func (_m *mockDynamodbClient) OneByPK(ctx context.Context, pk string, v interfac } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, interface{}) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.PK, interface{}) error); ok { r0 = rf(ctx, pk, v) } else { r0 = ret.Error(0) @@ -144,15 +146,15 @@ type mockDynamodbClient_OneByPK_Call struct { // OneByPK is a helper method to define mock.On call // - ctx context.Context -// - pk string +// - pk dynamo.PK // - v interface{} func (_e *mockDynamodbClient_Expecter) OneByPK(ctx interface{}, pk interface{}, v interface{}) *mockDynamodbClient_OneByPK_Call { return &mockDynamodbClient_OneByPK_Call{Call: _e.mock.On("OneByPK", ctx, pk, v)} } -func (_c *mockDynamodbClient_OneByPK_Call) Run(run func(ctx context.Context, pk string, v interface{})) *mockDynamodbClient_OneByPK_Call { +func (_c *mockDynamodbClient_OneByPK_Call) Run(run func(ctx context.Context, pk dynamo.PK, v interface{})) *mockDynamodbClient_OneByPK_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(interface{})) + run(args[0].(context.Context), args[1].(dynamo.PK), args[2].(interface{})) }) return _c } @@ -162,13 +164,13 @@ func (_c *mockDynamodbClient_OneByPK_Call) Return(_a0 error) *mockDynamodbClient return _c } -func (_c *mockDynamodbClient_OneByPK_Call) RunAndReturn(run func(context.Context, string, interface{}) error) *mockDynamodbClient_OneByPK_Call { +func (_c *mockDynamodbClient_OneByPK_Call) RunAndReturn(run func(context.Context, dynamo.PK, interface{}) error) *mockDynamodbClient_OneByPK_Call { _c.Call.Return(run) return _c } // OneBySK provides a mock function with given fields: ctx, sk, v -func (_m *mockDynamodbClient) OneBySK(ctx context.Context, sk string, v interface{}) error { +func (_m *mockDynamodbClient) OneBySK(ctx context.Context, sk dynamo.SK, v interface{}) error { ret := _m.Called(ctx, sk, v) if len(ret) == 0 { @@ -176,7 +178,7 @@ func (_m *mockDynamodbClient) OneBySK(ctx context.Context, sk string, v interfac } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, interface{}) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.SK, interface{}) error); ok { r0 = rf(ctx, sk, v) } else { r0 = ret.Error(0) @@ -192,15 +194,15 @@ type mockDynamodbClient_OneBySK_Call struct { // OneBySK is a helper method to define mock.On call // - ctx context.Context -// - sk string +// - sk dynamo.SK // - v interface{} func (_e *mockDynamodbClient_Expecter) OneBySK(ctx interface{}, sk interface{}, v interface{}) *mockDynamodbClient_OneBySK_Call { return &mockDynamodbClient_OneBySK_Call{Call: _e.mock.On("OneBySK", ctx, sk, v)} } -func (_c *mockDynamodbClient_OneBySK_Call) Run(run func(ctx context.Context, sk string, v interface{})) *mockDynamodbClient_OneBySK_Call { +func (_c *mockDynamodbClient_OneBySK_Call) Run(run func(ctx context.Context, sk dynamo.SK, v interface{})) *mockDynamodbClient_OneBySK_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(interface{})) + run(args[0].(context.Context), args[1].(dynamo.SK), args[2].(interface{})) }) return _c } @@ -210,7 +212,7 @@ func (_c *mockDynamodbClient_OneBySK_Call) Return(_a0 error) *mockDynamodbClient return _c } -func (_c *mockDynamodbClient_OneBySK_Call) RunAndReturn(run func(context.Context, string, interface{}) error) *mockDynamodbClient_OneBySK_Call { +func (_c *mockDynamodbClient_OneBySK_Call) RunAndReturn(run func(context.Context, dynamo.SK, interface{}) error) *mockDynamodbClient_OneBySK_Call { _c.Call.Return(run) return _c } @@ -311,7 +313,7 @@ func (_c *mockDynamodbClient_Put_Call) RunAndReturn(run func(context.Context, in } // UpdateReturn provides a mock function with given fields: ctx, pk, sk, values, expression -func (_m *mockDynamodbClient) UpdateReturn(ctx context.Context, pk string, sk string, values map[string]types.AttributeValue, expression string) (map[string]types.AttributeValue, error) { +func (_m *mockDynamodbClient) UpdateReturn(ctx context.Context, pk dynamo.PK, sk dynamo.SK, values map[string]types.AttributeValue, expression string) (map[string]types.AttributeValue, error) { ret := _m.Called(ctx, pk, sk, values, expression) if len(ret) == 0 { @@ -320,10 +322,10 @@ func (_m *mockDynamodbClient) UpdateReturn(ctx context.Context, pk string, sk st var r0 map[string]types.AttributeValue var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, map[string]types.AttributeValue, string) (map[string]types.AttributeValue, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.PK, dynamo.SK, map[string]types.AttributeValue, string) (map[string]types.AttributeValue, error)); ok { return rf(ctx, pk, sk, values, expression) } - if rf, ok := ret.Get(0).(func(context.Context, string, string, map[string]types.AttributeValue, string) map[string]types.AttributeValue); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.PK, dynamo.SK, map[string]types.AttributeValue, string) map[string]types.AttributeValue); ok { r0 = rf(ctx, pk, sk, values, expression) } else { if ret.Get(0) != nil { @@ -331,7 +333,7 @@ func (_m *mockDynamodbClient) UpdateReturn(ctx context.Context, pk string, sk st } } - if rf, ok := ret.Get(1).(func(context.Context, string, string, map[string]types.AttributeValue, string) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, dynamo.PK, dynamo.SK, map[string]types.AttributeValue, string) error); ok { r1 = rf(ctx, pk, sk, values, expression) } else { r1 = ret.Error(1) @@ -347,17 +349,17 @@ type mockDynamodbClient_UpdateReturn_Call struct { // UpdateReturn is a helper method to define mock.On call // - ctx context.Context -// - pk string -// - sk string +// - pk dynamo.PK +// - sk dynamo.SK // - values map[string]types.AttributeValue // - expression string func (_e *mockDynamodbClient_Expecter) UpdateReturn(ctx interface{}, pk interface{}, sk interface{}, values interface{}, expression interface{}) *mockDynamodbClient_UpdateReturn_Call { return &mockDynamodbClient_UpdateReturn_Call{Call: _e.mock.On("UpdateReturn", ctx, pk, sk, values, expression)} } -func (_c *mockDynamodbClient_UpdateReturn_Call) Run(run func(ctx context.Context, pk string, sk string, values map[string]types.AttributeValue, expression string)) *mockDynamodbClient_UpdateReturn_Call { +func (_c *mockDynamodbClient_UpdateReturn_Call) Run(run func(ctx context.Context, pk dynamo.PK, sk dynamo.SK, values map[string]types.AttributeValue, expression string)) *mockDynamodbClient_UpdateReturn_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(map[string]types.AttributeValue), args[4].(string)) + run(args[0].(context.Context), args[1].(dynamo.PK), args[2].(dynamo.SK), args[3].(map[string]types.AttributeValue), args[4].(string)) }) return _c } @@ -367,7 +369,7 @@ func (_c *mockDynamodbClient_UpdateReturn_Call) Return(_a0 map[string]types.Attr return _c } -func (_c *mockDynamodbClient_UpdateReturn_Call) RunAndReturn(run func(context.Context, string, string, map[string]types.AttributeValue, string) (map[string]types.AttributeValue, error)) *mockDynamodbClient_UpdateReturn_Call { +func (_c *mockDynamodbClient_UpdateReturn_Call) RunAndReturn(run func(context.Context, dynamo.PK, dynamo.SK, map[string]types.AttributeValue, string) (map[string]types.AttributeValue, error)) *mockDynamodbClient_UpdateReturn_Call { _c.Call.Return(run) return _c } diff --git a/cypress/e2e/attorney/trust-corporation.cy.js b/cypress/e2e/attorney/trust-corporation.cy.js index 87da44f5e1..377684ddf3 100644 --- a/cypress/e2e/attorney/trust-corporation.cy.js +++ b/cypress/e2e/attorney/trust-corporation.cy.js @@ -7,7 +7,7 @@ describe('As a trust corporation', () => { // start cy.contains('a', 'Start').click(); - cy.contains('button', 'Continue').click(); + cy.get('form').submit(); // enter reference number cy.get('#f-reference-number').type(shareCode); diff --git a/internal/actor/attorney_provided.go b/internal/actor/attorney_provided.go index 14a3e51947..379e31306d 100644 --- a/internal/actor/attorney_provided.go +++ b/internal/actor/attorney_provided.go @@ -4,6 +4,7 @@ import ( "time" "github.com/ministryofjustice/opg-modernising-lpa/internal/actor/actoruid" + "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" "github.com/ministryofjustice/opg-modernising-lpa/internal/form" "github.com/ministryofjustice/opg-modernising-lpa/internal/localize" ) @@ -11,7 +12,8 @@ import ( // AttorneyProvidedDetails contains details about an attorney or replacement // attorney, provided by the attorney or replacement attorney type AttorneyProvidedDetails struct { - PK, SK string + PK dynamo.LpaKeyType + SK dynamo.AttorneyKeyType // The identifier of the attorney or replacement attorney being edited UID actoruid.UID // The identifier of the LPA the attorney or replacement attorney is named in diff --git a/internal/actor/certificate_provider_provided.go b/internal/actor/certificate_provider_provided.go index cb25ea181d..a2fcd7b4ae 100644 --- a/internal/actor/certificate_provider_provided.go +++ b/internal/actor/certificate_provider_provided.go @@ -5,6 +5,7 @@ import ( "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/identity" "github.com/ministryofjustice/opg-modernising-lpa/internal/localize" "github.com/ministryofjustice/opg-modernising-lpa/internal/place" @@ -12,7 +13,8 @@ import ( // CertificateProviderProvidedDetails contains details about the certificate provider, provided by the certificate provider type CertificateProviderProvidedDetails struct { - PK, SK string + PK dynamo.LpaKeyType + SK dynamo.CertificateProviderKeyType // UID of the actor UID actoruid.UID // The identifier of the LPA the certificate provider is providing a certificate for diff --git a/internal/actor/donor_provided.go b/internal/actor/donor_provided.go index 50e0936bba..7eb16c7136 100644 --- a/internal/actor/donor_provided.go +++ b/internal/actor/donor_provided.go @@ -7,6 +7,7 @@ import ( "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/form" "github.com/ministryofjustice/opg-modernising-lpa/internal/identity" "github.com/ministryofjustice/opg-modernising-lpa/internal/pay" @@ -32,7 +33,8 @@ type DonorTasks struct { // DonorProvidedDetails contains all the data related to the LPA application type DonorProvidedDetails struct { - PK, SK string + PK dynamo.LpaKeyType `hash:"-"` + SK dynamo.LpaOwnerKeyType `hash:"-"` // Hash is used to determine whether the Lpa has been changed since last read Hash uint64 `hash:"-"` // LpaID identifies the LPA being drafted diff --git a/internal/actor/donor_provided_test.go b/internal/actor/donor_provided_test.go index 57579b9d31..1209499983 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(0xa66ff60a82e03c43), hash) + assert.Equal(t, uint64(0x757f179848dc49a8), hash) donor.Attorneys.Attorneys[0].DateOfBirth = date.New("2001", "1", "2") hash, err = donor.GenerateHash() assert.Nil(t, err) - assert.Equal(t, uint64(0x51fb6f3976645210), hash) + assert.Equal(t, uint64(0x858827c8eb6e4b8f), hash) } func TestIdentityConfirmed(t *testing.T) { diff --git a/internal/actor/organisation.go b/internal/actor/organisation.go index 0ca85a8543..8fb60ded6d 100644 --- a/internal/actor/organisation.go +++ b/internal/actor/organisation.go @@ -3,6 +3,8 @@ package actor import ( "fmt" "time" + + "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" ) const memberInviteExpireAfter = time.Hour * 48 @@ -10,7 +12,7 @@ const memberInviteExpireAfter = time.Hour * 48 // An Organisation contains users associated with a set of permissions that work on the // same set of LPAs. type Organisation struct { - PK, SK string + PK, SK dynamo.OrganisationKeyType // CreatedAt is when the Organisation was created CreatedAt time.Time // UpdatedAt is when the Organisation was last updated @@ -25,7 +27,8 @@ type Organisation struct { // A Member is the association of a OneLogin user with an Organisation. type Member struct { - PK, SK string + PK dynamo.OrganisationKeyType + SK dynamo.MemberKeyType // CreatedAt is when the Member was created CreatedAt time.Time // UpdatedAt is when the Member was last updated @@ -51,7 +54,8 @@ func (i Member) FullName() string { // A MemberInvite is created to allow a new Member to join an Organisation type MemberInvite struct { - PK, SK string + PK dynamo.OrganisationKeyType + SK dynamo.MemberInviteKeyType // CreatedAt is when the MemberInvite was created CreatedAt time.Time // UpdatedAt is when the MemberInvite was last updated diff --git a/internal/actor/share_code_data.go b/internal/actor/share_code_data.go index dfc10fb7a0..951d26ee88 100644 --- a/internal/actor/share_code_data.go +++ b/internal/actor/share_code_data.go @@ -4,10 +4,12 @@ import ( "time" "github.com/ministryofjustice/opg-modernising-lpa/internal/actor/actoruid" + "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" ) type ShareCodeData struct { - PK, SK string + PK dynamo.ShareKeyType + SK dynamo.ShareSortKeyType UpdatedAt time.Time SessionID string LpaID string diff --git a/internal/app/app.go b/internal/app/app.go index 71e09bc9ce..c31e393233 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -39,21 +39,21 @@ type Logger interface { } type DynamoClient interface { - One(ctx context.Context, pk, sk string, v interface{}) error - OneByPK(ctx context.Context, pk string, v interface{}) error - OneByPartialSK(ctx context.Context, pk, partialSK string, v interface{}) error - AllByPartialSK(ctx context.Context, pk, partialSK string, v interface{}) error - LatestForActor(ctx context.Context, sk string, v interface{}) error - AllBySK(ctx context.Context, sk string, v interface{}) error - AllByKeys(ctx context.Context, keys []dynamo.Key) ([]map[string]dynamodbtypes.AttributeValue, error) - AllKeysByPK(ctx context.Context, pk string) ([]dynamo.Key, error) + One(ctx context.Context, pk dynamo.PK, sk dynamo.SK, v interface{}) error + OneByPK(ctx context.Context, pk dynamo.PK, v interface{}) error + OneByPartialSK(ctx context.Context, pk dynamo.PK, partialSK dynamo.SK, v interface{}) error + AllByPartialSK(ctx context.Context, pk dynamo.PK, partialSK dynamo.SK, v interface{}) error + LatestForActor(ctx context.Context, sk dynamo.SK, v interface{}) error + AllBySK(ctx context.Context, sk dynamo.SK, v interface{}) error + AllByKeys(ctx context.Context, keys []dynamo.Keys) ([]map[string]dynamodbtypes.AttributeValue, error) + AllKeysByPK(ctx context.Context, pk dynamo.PK) ([]dynamo.Keys, error) Put(ctx context.Context, v interface{}) error Create(ctx context.Context, v interface{}) error - DeleteKeys(ctx context.Context, keys []dynamo.Key) error - DeleteOne(ctx context.Context, pk, sk string) error - Update(ctx context.Context, pk, sk string, values map[string]dynamodbtypes.AttributeValue, expression string) error + DeleteKeys(ctx context.Context, keys []dynamo.Keys) error + DeleteOne(ctx context.Context, pk dynamo.PK, sk dynamo.SK) error + Update(ctx context.Context, pk dynamo.PK, sk dynamo.SK, values map[string]dynamodbtypes.AttributeValue, expression string) error BatchPut(ctx context.Context, items []interface{}) error - OneBySK(ctx context.Context, sk string, v interface{}) error + OneBySK(ctx context.Context, sk dynamo.SK, v interface{}) error OneByUID(ctx context.Context, uid string, v interface{}) error } diff --git a/internal/app/attorney_store.go b/internal/app/attorney_store.go index 108fe052ae..2a4ddb4992 100644 --- a/internal/app/attorney_store.go +++ b/internal/app/attorney_store.go @@ -42,7 +42,7 @@ func (s *attorneyStore) Create(ctx context.Context, donorSessionID string, attor if err := s.dynamoClient.Create(ctx, lpaLink{ PK: dynamo.LpaKey(data.LpaID), SK: dynamo.SubKey(data.SessionID), - DonorKey: dynamo.DonorKey(donorSessionID), + DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey(donorSessionID)), ActorType: actor.TypeAttorney, UpdatedAt: s.now(), }); err != nil { @@ -79,7 +79,7 @@ func (s *attorneyStore) GetAny(ctx context.Context) ([]*actor.AttorneyProvidedDe } var attorneys []*actor.AttorneyProvidedDetails - err = s.dynamoClient.AllByPartialSK(ctx, dynamo.LpaKey(data.LpaID), "#ATTORNEY#", &attorneys) + err = s.dynamoClient.AllByPartialSK(ctx, dynamo.LpaKey(data.LpaID), dynamo.AttorneyKey(""), &attorneys) return attorneys, err } diff --git a/internal/app/attorney_store_test.go b/internal/app/attorney_store_test.go index eb83ffa7c7..ff45fd1858 100644 --- a/internal/app/attorney_store_test.go +++ b/internal/app/attorney_store_test.go @@ -8,6 +8,7 @@ import ( "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" "github.com/ministryofjustice/opg-modernising-lpa/internal/page" "github.com/stretchr/testify/assert" mock "github.com/stretchr/testify/mock" @@ -29,14 +30,14 @@ func TestAttorneyStoreCreate(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{LpaID: "123", SessionID: "456"}) now := time.Now() uid := actoruid.New() - details := &actor.AttorneyProvidedDetails{PK: "LPA#123", SK: "#ATTORNEY#456", UID: uid, LpaID: "123", UpdatedAt: now, IsReplacement: tc.replacement, IsTrustCorporation: tc.trustCorporation} + details := &actor.AttorneyProvidedDetails{PK: dynamo.LpaKey("123"), SK: dynamo.AttorneyKey("456"), UID: uid, LpaID: "123", UpdatedAt: now, IsReplacement: tc.replacement, IsTrustCorporation: tc.trustCorporation} dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). Create(ctx, details). Return(nil) dynamoClient.EXPECT(). - Create(ctx, lpaLink{PK: "LPA#123", SK: "#SUB#456", DonorKey: "#DONOR#session-id", ActorType: actor.TypeAttorney, UpdatedAt: now}). + Create(ctx, lpaLink{PK: dynamo.LpaKey("123"), SK: dynamo.SubKey("456"), DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey("session-id")), ActorType: actor.TypeAttorney, UpdatedAt: now}). Return(nil) attorneyStore := &attorneyStore{dynamoClient: dynamoClient, now: func() time.Time { return now }} @@ -119,7 +120,7 @@ func TestAttorneyStoreGet(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectOne(ctx, "LPA#123", "#ATTORNEY#456", + ExpectOne(ctx, dynamo.LpaKey("123"), dynamo.AttorneyKey("456"), &actor.AttorneyProvidedDetails{LpaID: "123"}, nil) attorneyStore := &attorneyStore{dynamoClient: dynamoClient, now: nil} @@ -161,7 +162,7 @@ func TestAttorneyStoreGetOnError(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectOne(ctx, "LPA#123", "#ATTORNEY#456", + ExpectOne(ctx, dynamo.LpaKey("123"), dynamo.AttorneyKey("456"), &actor.AttorneyProvidedDetails{LpaID: "123"}, expectedError) attorneyStore := &attorneyStore{dynamoClient: dynamoClient, now: nil} @@ -175,7 +176,7 @@ func TestAttorneyStoreGetAny(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectAllByPartialSK(ctx, "LPA#123", "#ATTORNEY#", + ExpectAllByPartialSK(ctx, dynamo.LpaKey("123"), dynamo.AttorneyKey(""), []*actor.AttorneyProvidedDetails{{LpaID: "123"}}, nil) attorneyStore := &attorneyStore{dynamoClient: dynamoClient} @@ -209,7 +210,7 @@ func TestAttorneyStorePut(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). - Put(ctx, &actor.AttorneyProvidedDetails{PK: "LPA#123", SK: "#ATTORNEY#456", LpaID: "123", UpdatedAt: now}). + Put(ctx, &actor.AttorneyProvidedDetails{PK: dynamo.LpaKey("123"), SK: dynamo.AttorneyKey("456"), LpaID: "123", UpdatedAt: now}). Return(nil) attorneyStore := &attorneyStore{ @@ -217,7 +218,7 @@ func TestAttorneyStorePut(t *testing.T) { now: func() time.Time { return now }, } - err := attorneyStore.Put(ctx, &actor.AttorneyProvidedDetails{PK: "LPA#123", SK: "#ATTORNEY#456", LpaID: "123"}) + err := attorneyStore.Put(ctx, &actor.AttorneyProvidedDetails{PK: dynamo.LpaKey("123"), SK: dynamo.AttorneyKey("456"), LpaID: "123"}) assert.Nil(t, err) } @@ -227,7 +228,7 @@ func TestAttorneyStorePutOnError(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). - Put(ctx, &actor.AttorneyProvidedDetails{PK: "LPA#123", SK: "#ATTORNEY#456", LpaID: "123", UpdatedAt: now}). + Put(ctx, &actor.AttorneyProvidedDetails{PK: dynamo.LpaKey("123"), SK: dynamo.AttorneyKey("456"), LpaID: "123", UpdatedAt: now}). Return(expectedError) attorneyStore := &attorneyStore{ @@ -235,6 +236,6 @@ func TestAttorneyStorePutOnError(t *testing.T) { now: func() time.Time { return now }, } - err := attorneyStore.Put(ctx, &actor.AttorneyProvidedDetails{PK: "LPA#123", SK: "#ATTORNEY#456", LpaID: "123"}) + err := attorneyStore.Put(ctx, &actor.AttorneyProvidedDetails{PK: dynamo.LpaKey("123"), SK: dynamo.AttorneyKey("456"), LpaID: "123"}) assert.Equal(t, expectedError, err) } diff --git a/internal/app/certificate_provider_store.go b/internal/app/certificate_provider_store.go index dc943d3b89..29d8367711 100644 --- a/internal/app/certificate_provider_store.go +++ b/internal/app/certificate_provider_store.go @@ -41,7 +41,7 @@ func (s *certificateProviderStore) Create(ctx context.Context, donorSessionID st if err := s.dynamoClient.Create(ctx, lpaLink{ PK: dynamo.LpaKey(data.LpaID), SK: dynamo.SubKey(data.SessionID), - DonorKey: dynamo.DonorKey(donorSessionID), + DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey(donorSessionID)), ActorType: actor.TypeCertificateProvider, UpdatedAt: s.now(), }); err != nil { @@ -62,7 +62,7 @@ func (s *certificateProviderStore) GetAny(ctx context.Context) (*actor.Certifica } var certificateProvider actor.CertificateProviderProvidedDetails - err = s.dynamoClient.OneByPartialSK(ctx, dynamo.LpaKey(data.LpaID), "#CERTIFICATE_PROVIDER#", &certificateProvider) + err = s.dynamoClient.OneByPartialSK(ctx, dynamo.LpaKey(data.LpaID), dynamo.CertificateProviderKey(""), &certificateProvider) return &certificateProvider, err } diff --git a/internal/app/certificate_provider_store_test.go b/internal/app/certificate_provider_store_test.go index 4b85f1a4ba..8cae52361c 100644 --- a/internal/app/certificate_provider_store_test.go +++ b/internal/app/certificate_provider_store_test.go @@ -8,6 +8,7 @@ import ( "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" "github.com/ministryofjustice/opg-modernising-lpa/internal/page" "github.com/stretchr/testify/assert" mock "github.com/stretchr/testify/mock" @@ -17,14 +18,14 @@ func TestCertificateProviderStoreCreate(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{LpaID: "123", SessionID: "456"}) uid := actoruid.New() now := time.Now() - details := &actor.CertificateProviderProvidedDetails{PK: "LPA#123", SK: "#CERTIFICATE_PROVIDER#456", LpaID: "123", UpdatedAt: now, UID: uid, Email: "a@b.com"} + details := &actor.CertificateProviderProvidedDetails{PK: dynamo.LpaKey("123"), SK: dynamo.CertificateProviderKey("456"), LpaID: "123", UpdatedAt: now, UID: uid, Email: "a@b.com"} dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). Create(ctx, details). Return(nil) dynamoClient.EXPECT(). - Create(ctx, lpaLink{PK: "LPA#123", SK: "#SUB#456", DonorKey: "#DONOR#session-id", ActorType: actor.TypeCertificateProvider, UpdatedAt: now}). + Create(ctx, lpaLink{PK: dynamo.LpaKey("123"), SK: dynamo.SubKey("456"), DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey("session-id")), ActorType: actor.TypeCertificateProvider, UpdatedAt: now}). Return(nil) certificateProviderStore := &certificateProviderStore{dynamoClient: dynamoClient, now: func() time.Time { return now }} @@ -105,7 +106,7 @@ func TestCertificateProviderStoreGetAny(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectOneByPartialSK(ctx, "LPA#123", "#CERTIFICATE_PROVIDER#", &actor.CertificateProviderProvidedDetails{LpaID: "123"}, nil) + ExpectOneByPartialSK(ctx, dynamo.LpaKey("123"), dynamo.CertificateProviderKey(""), &actor.CertificateProviderProvidedDetails{LpaID: "123"}, nil) certificateProviderStore := &certificateProviderStore{dynamoClient: dynamoClient, now: nil} @@ -137,7 +138,7 @@ func TestCertificateProviderStoreGetAnyOnError(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectOneByPartialSK(ctx, "LPA#123", "#CERTIFICATE_PROVIDER#", &actor.CertificateProviderProvidedDetails{LpaID: "123"}, expectedError) + ExpectOneByPartialSK(ctx, dynamo.LpaKey("123"), dynamo.CertificateProviderKey(""), &actor.CertificateProviderProvidedDetails{LpaID: "123"}, expectedError) certificateProviderStore := &certificateProviderStore{dynamoClient: dynamoClient, now: nil} @@ -150,7 +151,7 @@ func TestCertificateProviderStoreGet(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectOne(ctx, "LPA#123", "#CERTIFICATE_PROVIDER#456", &actor.CertificateProviderProvidedDetails{LpaID: "123"}, nil) + ExpectOne(ctx, dynamo.LpaKey("123"), dynamo.CertificateProviderKey("456"), &actor.CertificateProviderProvidedDetails{LpaID: "123"}, nil) certificateProviderStore := &certificateProviderStore{dynamoClient: dynamoClient, now: nil} @@ -191,7 +192,7 @@ func TestCertificateProviderStoreGetOnError(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectOne(ctx, "LPA#123", "#CERTIFICATE_PROVIDER#456", &actor.CertificateProviderProvidedDetails{LpaID: "123"}, expectedError) + ExpectOne(ctx, dynamo.LpaKey("123"), dynamo.CertificateProviderKey("456"), &actor.CertificateProviderProvidedDetails{LpaID: "123"}, expectedError) certificateProviderStore := &certificateProviderStore{dynamoClient: dynamoClient, now: nil} @@ -205,7 +206,7 @@ func TestCertificateProviderStorePut(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). - Put(ctx, &actor.CertificateProviderProvidedDetails{PK: "LPA#123", SK: "#CERTIFICATE_PROVIDER#456", LpaID: "123", UpdatedAt: now}). + Put(ctx, &actor.CertificateProviderProvidedDetails{PK: dynamo.LpaKey("123"), SK: dynamo.CertificateProviderKey("456"), LpaID: "123", UpdatedAt: now}). Return(nil) certificateProviderStore := &certificateProviderStore{ @@ -213,7 +214,7 @@ func TestCertificateProviderStorePut(t *testing.T) { now: func() time.Time { return now }, } - err := certificateProviderStore.Put(ctx, &actor.CertificateProviderProvidedDetails{PK: "LPA#123", SK: "#CERTIFICATE_PROVIDER#456", LpaID: "123"}) + err := certificateProviderStore.Put(ctx, &actor.CertificateProviderProvidedDetails{PK: dynamo.LpaKey("123"), SK: dynamo.CertificateProviderKey("456"), LpaID: "123"}) assert.Nil(t, err) } @@ -223,7 +224,7 @@ func TestCertificateProviderStorePutOnError(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). - Put(ctx, &actor.CertificateProviderProvidedDetails{PK: "LPA#123", SK: "#CERTIFICATE_PROVIDER#456", LpaID: "123", UpdatedAt: now}). + Put(ctx, &actor.CertificateProviderProvidedDetails{PK: dynamo.LpaKey("123"), SK: dynamo.CertificateProviderKey("456"), LpaID: "123", UpdatedAt: now}). Return(expectedError) certificateProviderStore := &certificateProviderStore{ @@ -231,6 +232,6 @@ func TestCertificateProviderStorePutOnError(t *testing.T) { now: func() time.Time { return now }, } - err := certificateProviderStore.Put(ctx, &actor.CertificateProviderProvidedDetails{PK: "LPA#123", SK: "#CERTIFICATE_PROVIDER#456", LpaID: "123"}) + err := certificateProviderStore.Put(ctx, &actor.CertificateProviderProvidedDetails{PK: dynamo.LpaKey("123"), SK: dynamo.CertificateProviderKey("456"), LpaID: "123"}) assert.Equal(t, expectedError, err) } diff --git a/internal/app/dashboard_store.go b/internal/app/dashboard_store.go index c4d5f3120f..41336de62b 100644 --- a/internal/app/dashboard_store.go +++ b/internal/app/dashboard_store.go @@ -21,11 +21,11 @@ type LpaStoreResolvingService interface { // An lpaLink is used to join an actor to an LPA. type lpaLink struct { // PK is the same as the PK for the LPA - PK string + PK dynamo.LpaKeyType // SK is the subKey for the current user - SK string + SK dynamo.SubKeyType // DonorKey is the donorKey for the donor - DonorKey string + DonorKey dynamo.LpaOwnerKeyType // ActorType is the type for the current user ActorType actor.Type // UpdatedAt is set to allow this data to be queried from SKUpdatedAtIndex @@ -37,7 +37,7 @@ func (l lpaLink) UserSub() string { return "" } - return strings.Split(l.SK, dynamo.SubKey(""))[1] + return strings.Split(l.SK.SK(), dynamo.SubKey("").SK())[1] } type dashboardStore struct { @@ -50,15 +50,15 @@ type keys struct { } func (k keys) isLpa() bool { - return strings.HasPrefix(k.SK, dynamo.DonorKey("")) || strings.HasPrefix(k.SK, dynamo.OrganisationKey("")) + return strings.HasPrefix(k.SK, dynamo.DonorKey("").SK()) || strings.HasPrefix(k.SK, dynamo.OrganisationKey("").PK()) } func (k keys) isCertificateProviderDetails() bool { - return strings.HasPrefix(k.SK, dynamo.CertificateProviderKey("")) + return strings.HasPrefix(k.SK, dynamo.CertificateProviderKey("").SK()) } func (k keys) isAttorneyDetails() bool { - return strings.HasPrefix(k.SK, dynamo.AttorneyKey("")) + return strings.HasPrefix(k.SK, dynamo.AttorneyKey("").SK()) } func (s *dashboardStore) SubExistsForActorType(ctx context.Context, sub string, actorType actor.Type) (bool, error) { @@ -91,20 +91,20 @@ func (s *dashboardStore) GetAll(ctx context.Context) (donor, attorney, certifica return nil, nil, nil, err } - var searchKeys []dynamo.Key + var searchKeys []dynamo.Keys keyMap := map[string]actor.Type{} for _, key := range links { - searchKeys = append(searchKeys, dynamo.Key{PK: key.PK, SK: key.DonorKey}) + searchKeys = append(searchKeys, dynamo.Keys{PK: key.PK, SK: key.DonorKey}) if key.ActorType == actor.TypeAttorney { - searchKeys = append(searchKeys, dynamo.Key{PK: key.PK, SK: dynamo.AttorneyKey(data.SessionID)}) + searchKeys = append(searchKeys, dynamo.Keys{PK: key.PK, SK: dynamo.AttorneyKey(data.SessionID)}) } if key.ActorType == actor.TypeCertificateProvider { - searchKeys = append(searchKeys, dynamo.Key{PK: key.PK, SK: dynamo.CertificateProviderKey(data.SessionID)}) + searchKeys = append(searchKeys, dynamo.Keys{PK: key.PK, SK: dynamo.CertificateProviderKey(data.SessionID)}) } - _, id, _ := strings.Cut(key.PK, "#") + _, id, _ := strings.Cut(key.PK.PK(), "#") keyMap[id] = key.ActorType } diff --git a/internal/app/dashboard_store_test.go b/internal/app/dashboard_store_test.go index fff2a6be63..bcdd3784ff 100644 --- a/internal/app/dashboard_store_test.go +++ b/internal/app/dashboard_store_test.go @@ -20,22 +20,22 @@ func TestDashboardStoreGetAll(t *testing.T) { aTime := time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC) lpa0 := &lpastore.Lpa{LpaID: "0", LpaUID: "M", UpdatedAt: aTime} - lpa0Donor := &actor.DonorProvidedDetails{LpaID: "0", LpaUID: "M", UpdatedAt: aTime, SK: dynamo.DonorKey(sessionID), PK: dynamo.LpaKey("0")} + lpa0Donor := &actor.DonorProvidedDetails{LpaID: "0", LpaUID: "M", UpdatedAt: aTime, SK: dynamo.LpaOwnerKey(dynamo.DonorKey(sessionID)), PK: dynamo.LpaKey("0")} lpa123 := &lpastore.Lpa{LpaID: "123", LpaUID: "M", UpdatedAt: aTime} - lpa123Donor := &actor.DonorProvidedDetails{LpaID: "123", LpaUID: "M", UpdatedAt: aTime, SK: dynamo.DonorKey(sessionID), PK: dynamo.LpaKey("123")} + lpa123Donor := &actor.DonorProvidedDetails{LpaID: "123", LpaUID: "M", UpdatedAt: aTime, SK: dynamo.LpaOwnerKey(dynamo.DonorKey(sessionID)), PK: dynamo.LpaKey("123")} lpa456 := &lpastore.Lpa{LpaID: "456", LpaUID: "M"} - lpa456Donor := &actor.DonorProvidedDetails{LpaID: "456", LpaUID: "M", SK: dynamo.DonorKey("another-id"), PK: dynamo.LpaKey("456")} + lpa456Donor := &actor.DonorProvidedDetails{LpaID: "456", LpaUID: "M", SK: dynamo.LpaOwnerKey(dynamo.DonorKey("another-id")), PK: dynamo.LpaKey("456")} lpa456CertificateProvider := &actor.CertificateProviderProvidedDetails{ LpaID: "456", Tasks: actor.CertificateProviderTasks{ConfirmYourDetails: actor.TaskCompleted}, SK: dynamo.CertificateProviderKey(sessionID), } lpa789 := &lpastore.Lpa{LpaID: "789", LpaUID: "M"} - lpa789Donor := &actor.DonorProvidedDetails{LpaID: "789", LpaUID: "M", SK: dynamo.DonorKey("different-id"), PK: dynamo.LpaKey("789")} + lpa789Donor := &actor.DonorProvidedDetails{LpaID: "789", LpaUID: "M", SK: dynamo.LpaOwnerKey(dynamo.DonorKey("different-id")), PK: dynamo.LpaKey("789")} lpa789Attorney := &actor.AttorneyProvidedDetails{ LpaID: "789", Tasks: actor.AttorneyTasks{ConfirmYourDetails: actor.TaskInProgress}, SK: dynamo.AttorneyKey(sessionID), } - lpaNoUIDDonor := &actor.DonorProvidedDetails{LpaID: "999", UpdatedAt: aTime, SK: dynamo.DonorKey(sessionID), PK: dynamo.LpaKey("0")} + lpaNoUIDDonor := &actor.DonorProvidedDetails{LpaID: "999", UpdatedAt: aTime, SK: dynamo.LpaOwnerKey(dynamo.DonorKey(sessionID)), PK: dynamo.LpaKey("0")} lpaCertified := &lpastore.Lpa{LpaID: "signed-by-cp", LpaUID: "M"} - lpaCertifiedDonor := &actor.DonorProvidedDetails{LpaID: "signed-by-cp", LpaUID: "M", SK: dynamo.DonorKey("another-id"), PK: dynamo.LpaKey("signed-by-cp")} + lpaCertifiedDonor := &actor.DonorProvidedDetails{LpaID: "signed-by-cp", LpaUID: "M", SK: dynamo.LpaOwnerKey(dynamo.DonorKey("another-id")), PK: dynamo.LpaKey("signed-by-cp")} lpaCertifiedCertificateProvider := &actor.CertificateProviderProvidedDetails{ LpaID: "signed-by-cp", SK: dynamo.CertificateProviderKey(sessionID), Certificate: actor.Certificate{AgreeToStatement: true}, } @@ -69,25 +69,25 @@ func TestDashboardStoreGetAll(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: sessionID}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectAllBySK(ctx, "#SUB#an-id", + dynamoClient.ExpectAllBySK(ctx, dynamo.SubKey("an-id"), []lpaLink{ - {PK: "LPA#123", SK: "#SUB#an-id", DonorKey: "#DONOR#an-id", ActorType: actor.TypeDonor}, - {PK: "LPA#456", SK: "#SUB#an-id", DonorKey: "#DONOR#another-id", ActorType: actor.TypeCertificateProvider}, - {PK: "LPA#789", SK: "#SUB#an-id", DonorKey: "#DONOR#different-id", ActorType: actor.TypeAttorney}, - {PK: "LPA#0", SK: "#SUB#an-id", DonorKey: "#DONOR#an-id", ActorType: actor.TypeDonor}, - {PK: "LPA#999", SK: "#SUB#an-id", DonorKey: "#DONOR#an-id", ActorType: actor.TypeDonor}, - {PK: "LPA#signed-by-cp", SK: "#SUB#an-id", DonorKey: "#DONOR#another-id", ActorType: actor.TypeCertificateProvider}, + {PK: dynamo.LpaKey("123"), SK: dynamo.SubKey("an-id"), DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), ActorType: actor.TypeDonor}, + {PK: dynamo.LpaKey("456"), SK: dynamo.SubKey("an-id"), DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey("another-id")), ActorType: actor.TypeCertificateProvider}, + {PK: dynamo.LpaKey("789"), SK: dynamo.SubKey("an-id"), DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey("different-id")), ActorType: actor.TypeAttorney}, + {PK: dynamo.LpaKey("0"), SK: dynamo.SubKey("an-id"), DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), ActorType: actor.TypeDonor}, + {PK: dynamo.LpaKey("999"), SK: dynamo.SubKey("an-id"), DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), ActorType: actor.TypeDonor}, + {PK: dynamo.LpaKey("signed-by-cp"), SK: dynamo.SubKey("an-id"), DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey("another-id")), ActorType: actor.TypeCertificateProvider}, }, nil) - dynamoClient.ExpectAllByKeys(ctx, []dynamo.Key{ - {PK: "LPA#123", SK: "#DONOR#an-id"}, - {PK: "LPA#456", SK: "#DONOR#another-id"}, - {PK: "LPA#456", SK: "#CERTIFICATE_PROVIDER#an-id"}, - {PK: "LPA#789", SK: "#DONOR#different-id"}, - {PK: "LPA#789", SK: "#ATTORNEY#an-id"}, - {PK: "LPA#0", SK: "#DONOR#an-id"}, - {PK: "LPA#999", SK: "#DONOR#an-id"}, - {PK: "LPA#signed-by-cp", SK: "#DONOR#another-id"}, - {PK: "LPA#signed-by-cp", SK: "#CERTIFICATE_PROVIDER#an-id"}, + dynamoClient.ExpectAllByKeys(ctx, []dynamo.Keys{ + {PK: dynamo.LpaKey("123"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id"))}, + {PK: dynamo.LpaKey("456"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("another-id"))}, + {PK: dynamo.LpaKey("456"), SK: dynamo.CertificateProviderKey("an-id")}, + {PK: dynamo.LpaKey("789"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("different-id"))}, + {PK: dynamo.LpaKey("789"), SK: dynamo.AttorneyKey("an-id")}, + {PK: dynamo.LpaKey("0"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id"))}, + {PK: dynamo.LpaKey("999"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id"))}, + {PK: dynamo.LpaKey("signed-by-cp"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("another-id"))}, + {PK: dynamo.LpaKey("signed-by-cp"), SK: dynamo.CertificateProviderKey("an-id")}, }, attributeValues, nil) lpaStoreResolvingService := newMockLpaStoreResolvingService(t) @@ -112,24 +112,24 @@ func TestDashboardStoreGetAllSubmittedForAttorneys(t *testing.T) { aTime := time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC) lpaSubmitted := &lpastore.Lpa{LpaID: "submitted", LpaUID: "M", Submitted: true} - lpaSubmittedDonor := &actor.DonorProvidedDetails{LpaID: "submitted", LpaUID: "M", SK: dynamo.DonorKey("another-id"), PK: dynamo.LpaKey("submitted"), SubmittedAt: aTime} + lpaSubmittedDonor := &actor.DonorProvidedDetails{LpaID: "submitted", LpaUID: "M", SK: dynamo.LpaOwnerKey(dynamo.DonorKey("another-id")), PK: dynamo.LpaKey("submitted"), SubmittedAt: aTime} lpaSubmittedAttorney := &actor.AttorneyProvidedDetails{LpaID: "submitted", SK: dynamo.AttorneyKey(sessionID)} lpaSubmittedReplacement := &lpastore.Lpa{LpaID: "submitted-replacement", LpaUID: "M", Submitted: true} - lpaSubmittedReplacementDonor := &actor.DonorProvidedDetails{LpaID: "submitted-replacement", LpaUID: "M", SK: dynamo.DonorKey("another-id"), PK: dynamo.LpaKey("submitted-replacement"), SubmittedAt: aTime} + lpaSubmittedReplacementDonor := &actor.DonorProvidedDetails{LpaID: "submitted-replacement", LpaUID: "M", SK: dynamo.LpaOwnerKey(dynamo.DonorKey("another-id")), PK: dynamo.LpaKey("submitted-replacement"), SubmittedAt: aTime} lpaSubmittedReplacementAttorney := &actor.AttorneyProvidedDetails{LpaID: "submitted-replacement", SK: dynamo.AttorneyKey(sessionID), IsReplacement: true} ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: sessionID}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectAllBySK(ctx, "#SUB#an-id", + dynamoClient.ExpectAllBySK(ctx, dynamo.SubKey("an-id"), []lpaLink{ - {PK: "LPA#submitted", SK: "#SUB#an-id", DonorKey: "#DONOR#another-id", ActorType: actor.TypeAttorney}, - {PK: "LPA#submitted-replacement", SK: "#SUB#an-id", DonorKey: "#DONOR#another-id", ActorType: actor.TypeAttorney}, + {PK: dynamo.LpaKey("submitted"), SK: dynamo.SubKey("an-id"), DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey("another-id")), ActorType: actor.TypeAttorney}, + {PK: dynamo.LpaKey("submitted-replacement"), SK: dynamo.SubKey("an-id"), DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey("another-id")), ActorType: actor.TypeAttorney}, }, nil) - dynamoClient.ExpectAllByKeys(ctx, []dynamo.Key{ - {PK: "LPA#submitted", SK: "#DONOR#another-id"}, - {PK: "LPA#submitted", SK: "#ATTORNEY#an-id"}, - {PK: "LPA#submitted-replacement", SK: "#DONOR#another-id"}, - {PK: "LPA#submitted-replacement", SK: "#ATTORNEY#an-id"}, + dynamoClient.ExpectAllByKeys(ctx, []dynamo.Keys{ + {PK: dynamo.LpaKey("submitted"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("another-id"))}, + {PK: dynamo.LpaKey("submitted"), SK: dynamo.AttorneyKey("an-id")}, + {PK: dynamo.LpaKey("submitted-replacement"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("another-id"))}, + {PK: dynamo.LpaKey("submitted-replacement"), SK: dynamo.AttorneyKey("an-id")}, }, []map[string]types.AttributeValue{ makeAttributeValueMap(lpaSubmittedDonor), makeAttributeValueMap(lpaSubmittedAttorney), @@ -161,17 +161,17 @@ func TestDashboardStoreGetAllWhenResolveErrors(t *testing.T) { sessionID := "an-id" aTime := time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC) - donor := &actor.DonorProvidedDetails{LpaID: "0", LpaUID: "M", UpdatedAt: aTime, SK: dynamo.DonorKey(sessionID), PK: dynamo.LpaKey("0")} + donor := &actor.DonorProvidedDetails{LpaID: "0", LpaUID: "M", UpdatedAt: aTime, SK: dynamo.LpaOwnerKey(dynamo.DonorKey(sessionID)), PK: dynamo.LpaKey("0")} ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: sessionID}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectAllBySK(ctx, "#SUB#an-id", + dynamoClient.ExpectAllBySK(ctx, dynamo.SubKey("an-id"), []lpaLink{ - {PK: "LPA#0", SK: "#SUB#an-id", DonorKey: "#DONOR#an-id", ActorType: actor.TypeDonor}, + {PK: dynamo.LpaKey("0"), SK: dynamo.SubKey("an-id"), DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), ActorType: actor.TypeDonor}, }, nil) - dynamoClient.ExpectAllByKeys(ctx, []dynamo.Key{ - {PK: "LPA#0", SK: "#DONOR#an-id"}, + dynamoClient.ExpectAllByKeys(ctx, []dynamo.Keys{ + {PK: dynamo.LpaKey("0"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id"))}, }, []map[string]types.AttributeValue{makeAttributeValueMap(donor)}, nil) lpaStoreResolvingService := newMockLpaStoreResolvingService(t) @@ -180,14 +180,16 @@ func TestDashboardStoreGetAllWhenResolveErrors(t *testing.T) { dashboardStore := &dashboardStore{dynamoClient: dynamoClient, lpaStoreResolvingService: lpaStoreResolvingService} _, _, _, err := dashboardStore.GetAll(ctx) - assert.Equal(t, expectedError, err) + if !assert.Equal(t, expectedError, err) { + t.Log(err.Error()) + } } func TestDashboardStoreGetAllWhenNone(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: "an-id"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectAllBySK(ctx, "#SUB#an-id", + dynamoClient.ExpectAllBySK(ctx, dynamo.SubKey("an-id"), []map[string]any{}, nil) dashboardStore := &dashboardStore{dynamoClient: dynamoClient} @@ -203,7 +205,7 @@ func TestDashboardStoreGetAllWhenAllForActorErrors(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: "an-id"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectAllBySK(ctx, "#SUB#an-id", + dynamoClient.ExpectAllBySK(ctx, dynamo.SubKey("an-id"), []lpaLink{}, expectedError) dashboardStore := &dashboardStore{dynamoClient: dynamoClient} @@ -216,10 +218,10 @@ func TestDashboardStoreGetAllWhenAllByKeysErrors(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: "an-id"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectAllBySK(ctx, "#SUB#an-id", - []lpaLink{{PK: "LPA#123", SK: "#SUB#an-id", DonorKey: "#DONOR#an-id", ActorType: actor.TypeDonor}}, nil) - dynamoClient.ExpectAllByKeys(ctx, []dynamo.Key{ - {PK: "LPA#123", SK: "#DONOR#an-id"}, + dynamoClient.ExpectAllBySK(ctx, dynamo.SubKey("an-id"), + []lpaLink{{PK: dynamo.LpaKey("123"), SK: dynamo.SubKey("an-id"), DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), ActorType: actor.TypeDonor}}, nil) + dynamoClient.ExpectAllByKeys(ctx, []dynamo.Keys{ + {PK: dynamo.LpaKey("123"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id"))}, }, nil, expectedError) dashboardStore := &dashboardStore{dynamoClient: dynamoClient} @@ -235,12 +237,12 @@ func TestDashboardStoreSubExists(t *testing.T) { actorType actor.Type }{ "lpas exist - correct actor": { - lpas: []lpaLink{{PK: "LPA#123", SK: "#SUB#a-sub-id", DonorKey: "#DONOR#an-id", ActorType: actor.TypeDonor}}, + lpas: []lpaLink{{PK: dynamo.LpaKey("123"), SK: dynamo.SubKey("a-sub-id"), DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), ActorType: actor.TypeDonor}}, expectedExists: true, actorType: actor.TypeDonor, }, "lpas exist - incorrect actor": { - lpas: []lpaLink{{PK: "LPA#123", SK: "#SUB#a-sub-id", DonorKey: "#DONOR#an-id", ActorType: actor.TypeDonor}}, + lpas: []lpaLink{{PK: dynamo.LpaKey("123"), SK: dynamo.SubKey("a-sub-id"), DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), ActorType: actor.TypeDonor}}, expectedExists: false, actorType: actor.TypeAttorney, }, @@ -252,7 +254,7 @@ func TestDashboardStoreSubExists(t *testing.T) { for name, tc := range testCases { t.Run(name, func(t *testing.T) { dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectAllBySK(context.Background(), "#SUB#a-sub-id", + dynamoClient.ExpectAllBySK(context.Background(), dynamo.SubKey("a-sub-id"), tc.lpas, nil) dashboardStore := &dashboardStore{dynamoClient: dynamoClient} @@ -266,7 +268,7 @@ func TestDashboardStoreSubExists(t *testing.T) { func TestDashboardStoreSubExistsWhenDynamoError(t *testing.T) { dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectAllBySK(context.Background(), "#SUB#a-sub-id", + dynamoClient.ExpectAllBySK(context.Background(), dynamo.SubKey("a-sub-id"), []lpaLink{}, expectedError) dashboardStore := &dashboardStore{dynamoClient: dynamoClient} diff --git a/internal/app/document_store.go b/internal/app/document_store.go index 7888185daf..439316f260 100644 --- a/internal/app/document_store.go +++ b/internal/app/document_store.go @@ -86,11 +86,11 @@ func (s *documentStore) Put(ctx context.Context, document page.Document) error { } func (s *documentStore) DeleteInfectedDocuments(ctx context.Context, documents page.Documents) error { - var dynamoKeys []dynamo.Key + var dynamoKeys []dynamo.Keys for _, d := range documents { if d.VirusDetected { - dynamoKeys = append(dynamoKeys, dynamo.Key{PK: d.PK, SK: d.SK}) + dynamoKeys = append(dynamoKeys, dynamo.Keys{PK: d.PK, SK: d.SK}) } } diff --git a/internal/app/document_store_test.go b/internal/app/document_store_test.go index 47131720ca..6a70b2626f 100644 --- a/internal/app/document_store_test.go +++ b/internal/app/document_store_test.go @@ -31,9 +31,9 @@ func TestDocumentStoreGetAll(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - On("AllByPartialSK", ctx, "LPA#123", "#DOCUMENT#", mock.Anything). - Return(func(ctx context.Context, pk, partialSk string, v interface{}) error { - b, _ := json.Marshal(page.Documents{{PK: "LPA#123"}}) + On("AllByPartialSK", ctx, dynamo.LpaKey("123"), dynamo.DocumentKey(""), mock.Anything). + Return(func(ctx context.Context, pk dynamo.PK, partialSk dynamo.SK, v interface{}) error { + b, _ := json.Marshal(page.Documents{{PK: dynamo.LpaKey("123")}}) json.Unmarshal(b, v) return nil }) @@ -43,7 +43,7 @@ func TestDocumentStoreGetAll(t *testing.T) { documents, err := documentStore.GetAll(ctx) assert.Nil(t, err) - assert.Equal(t, page.Documents{{PK: "LPA#123"}}, documents) + assert.Equal(t, page.Documents{{PK: dynamo.LpaKey("123")}}, documents) } func TestDocumentStoreGetAllMissingSessionData(t *testing.T) { @@ -67,9 +67,9 @@ func TestDocumentStoreGetAllWhenDynamoClientAllByPartialSKError(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - On("AllByPartialSK", ctx, "LPA#123", "#DOCUMENT#", mock.Anything). - Return(func(ctx context.Context, pk, partialSk string, v interface{}) error { - b, _ := json.Marshal(page.Documents{{PK: "LPA#123"}}) + On("AllByPartialSK", ctx, dynamo.LpaKey("123"), dynamo.DocumentKey(""), mock.Anything). + Return(func(ctx context.Context, pk dynamo.PK, partialSk dynamo.SK, v interface{}) error { + b, _ := json.Marshal(page.Documents{{PK: dynamo.LpaKey("123")}}) json.Unmarshal(b, v) return expectedError }) @@ -85,8 +85,8 @@ func TestDocumentStoreGetAllWhenNoResults(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - On("AllByPartialSK", ctx, "LPA#123", "#DOCUMENT#", mock.Anything). - Return(func(ctx context.Context, pk, partialSk string, v interface{}) error { + On("AllByPartialSK", ctx, dynamo.LpaKey("123"), dynamo.DocumentKey(""), mock.Anything). + Return(func(ctx context.Context, pk dynamo.PK, partialSk dynamo.SK, v interface{}) error { b, _ := json.Marshal(page.Documents{}) json.Unmarshal(b, v) return dynamo.NotFoundError{} @@ -105,8 +105,8 @@ func TestDocumentStoreUpdateScanResults(t *testing.T) { dynamoClient.EXPECT(). Update( ctx, - "LPA#123", - "#DOCUMENT#object/key", + dynamo.LpaKey("123"), + dynamo.DocumentKey("object/key"), map[string]types.AttributeValue{ ":virusDetected": &types.AttributeValueMemberBOOL{Value: true}, ":scanned": &types.AttributeValueMemberBOOL{Value: true}, @@ -126,8 +126,8 @@ func TestDocumentStoreUpdateScanResultsWhenUpdateError(t *testing.T) { dynamoClient.EXPECT(). Update( ctx, - "LPA#123", - "#DOCUMENT#object/key", + dynamo.LpaKey("123"), + dynamo.DocumentKey("object/key"), map[string]types.AttributeValue{ ":virusDetected": &types.AttributeValueMemberBOOL{Value: true}, ":scanned": &types.AttributeValueMemberBOOL{Value: true}, @@ -176,17 +176,17 @@ func TestDeleteInfectedDocuments(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). - DeleteKeys(ctx, []dynamo.Key{ - {PK: "a-pk", SK: "a-sk"}, - {PK: "another-pk", SK: "another-sk"}, + DeleteKeys(ctx, []dynamo.Keys{ + {PK: dynamo.LpaKey("a-pk"), SK: dynamo.DocumentKey("a-sk")}, + {PK: dynamo.LpaKey("another-pk"), SK: dynamo.DocumentKey("another-sk")}, }). Return(nil) documentStore := documentStore{dynamoClient: dynamoClient} err := documentStore.DeleteInfectedDocuments(ctx, page.Documents{ - {PK: "a-pk", SK: "a-sk", Key: "a-key", VirusDetected: true}, - {PK: "another-pk", SK: "another-sk", Key: "another-key", VirusDetected: true}, + {PK: dynamo.LpaKey("a-pk"), SK: dynamo.DocumentKey("a-sk"), Key: "a-key", VirusDetected: true}, + {PK: dynamo.LpaKey("another-pk"), SK: dynamo.DocumentKey("another-sk"), Key: "another-key", VirusDetected: true}, }) assert.Nil(t, err) @@ -197,17 +197,17 @@ func TestDeleteInfectedDocumentsWhenDynamoClientError(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). - DeleteKeys(ctx, []dynamo.Key{ - {PK: "a-pk", SK: "a-sk"}, - {PK: "another-pk", SK: "another-sk"}, + DeleteKeys(ctx, []dynamo.Keys{ + {PK: dynamo.LpaKey("a-pk"), SK: dynamo.DocumentKey("a-sk")}, + {PK: dynamo.LpaKey("another-pk"), SK: dynamo.DocumentKey("another-sk")}, }). Return(expectedError) documentStore := documentStore{dynamoClient: dynamoClient} err := documentStore.DeleteInfectedDocuments(ctx, page.Documents{ - {PK: "a-pk", SK: "a-sk", Key: "a-key", VirusDetected: true}, - {PK: "another-pk", SK: "another-sk", Key: "another-key", VirusDetected: true}, + {PK: dynamo.LpaKey("a-pk"), SK: dynamo.DocumentKey("a-sk"), Key: "a-key", VirusDetected: true}, + {PK: dynamo.LpaKey("another-pk"), SK: dynamo.DocumentKey("another-sk"), Key: "another-key", VirusDetected: true}, }) assert.Equal(t, expectedError, err) @@ -236,12 +236,12 @@ func TestDelete(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). - DeleteOne(ctx, "a-pk", "a-sk"). + DeleteOne(ctx, dynamo.LpaKey("a-pk"), dynamo.DocumentKey("a-sk")). Return(nil) documentStore := documentStore{s3Client: s3Client, dynamoClient: dynamoClient} - err := documentStore.Delete(ctx, page.Document{PK: "a-pk", SK: "a-sk", Key: "a-key", VirusDetected: true}) + err := documentStore.Delete(ctx, page.Document{PK: dynamo.LpaKey("a-pk"), SK: dynamo.DocumentKey("a-sk"), Key: "a-key", VirusDetected: true}) assert.Nil(t, err) } @@ -271,12 +271,12 @@ func TestDeleteWhenDynamoClientError(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). - DeleteOne(ctx, "a-pk", "a-sk"). + DeleteOne(ctx, dynamo.LpaKey("a-pk"), dynamo.DocumentKey("a-sk")). Return(expectedError) documentStore := documentStore{s3Client: s3Client, dynamoClient: dynamoClient} - err := documentStore.Delete(ctx, page.Document{PK: "a-pk", SK: "a-sk", Key: "a-key", VirusDetected: true}) + err := documentStore.Delete(ctx, page.Document{PK: dynamo.LpaKey("a-pk"), SK: dynamo.DocumentKey("a-sk"), Key: "a-key", VirusDetected: true}) assert.Equal(t, expectedError, err) } @@ -438,8 +438,8 @@ func TestDocumentCreate(t *testing.T) { Return(nil) expectedDocument := page.Document{ - PK: "LPA#lpa-id", - SK: "#DOCUMENT#lpa-uid/evidence/a-uuid", + PK: dynamo.LpaKey("lpa-id"), + SK: dynamo.DocumentKey("lpa-uid/evidence/a-uuid"), Filename: "a-filename", Key: "lpa-uid/evidence/a-uuid", Uploaded: now, diff --git a/internal/app/donor_store.go b/internal/app/donor_store.go index 18b7a9902e..11123a8d0b 100644 --- a/internal/app/donor_store.go +++ b/internal/app/donor_store.go @@ -61,7 +61,7 @@ func (s *donorStore) Create(ctx context.Context) (*actor.DonorProvidedDetails, e donor := &actor.DonorProvidedDetails{ PK: dynamo.LpaKey(lpaID), - SK: dynamo.DonorKey(data.SessionID), + SK: dynamo.LpaOwnerKey(dynamo.DonorKey(data.SessionID)), LpaID: lpaID, CreatedAt: s.now(), Version: 1, @@ -95,7 +95,7 @@ func (s *donorStore) Create(ctx context.Context) (*actor.DonorProvidedDetails, e if err := s.dynamoClient.Create(ctx, lpaLink{ PK: dynamo.LpaKey(lpaID), SK: dynamo.SubKey(data.SessionID), - DonorKey: dynamo.DonorKey(data.SessionID), + DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey(data.SessionID)), ActorType: actor.TypeDonor, UpdatedAt: s.now(), }); err != nil { @@ -109,8 +109,9 @@ func (s *donorStore) Create(ctx context.Context) (*actor.DonorProvidedDetails, e // expected donor owned the LPA. This contains the actual SK containing the LPA // data. type lpaReference struct { - PK, SK string - ReferencedSK string + PK dynamo.LpaKeyType + SK dynamo.DonorKeyType + ReferencedSK dynamo.OrganisationKeyType } // Link allows a donor to access an Lpa created by a supporter. It creates two @@ -148,7 +149,7 @@ func (s *donorStore) Link(ctx context.Context, shareCode actor.ShareCodeData) er return s.dynamoClient.Create(ctx, lpaLink{ PK: dynamo.LpaKey(shareCode.LpaID), SK: dynamo.SubKey(data.SessionID), - DonorKey: dynamo.OrganisationKey(shareCode.SessionID), + DonorKey: dynamo.LpaOwnerKey(dynamo.OrganisationKey(shareCode.SessionID)), ActorType: actor.TypeDonor, UpdatedAt: s.now(), }) @@ -164,7 +165,7 @@ func (s *donorStore) GetAny(ctx context.Context) (*actor.DonorProvidedDetails, e return nil, errors.New("donorStore.Get requires LpaID") } - sk := dynamo.DonorKey("") + var sk dynamo.SK = dynamo.DonorKey("") if data.OrganisationID != "" { sk = dynamo.OrganisationKey("") } @@ -187,14 +188,14 @@ func (s *donorStore) Get(ctx context.Context) (*actor.DonorProvidedDetails, erro return nil, errors.New("donorStore.Get requires LpaID and SessionID") } - sk := dynamo.DonorKey(data.SessionID) + var sk dynamo.SK = dynamo.DonorKey(data.SessionID) if data.OrganisationID != "" { sk = dynamo.OrganisationKey(data.OrganisationID) } var donor struct { actor.DonorProvidedDetails - ReferencedSK string + ReferencedSK dynamo.OrganisationKeyType } if err := s.dynamoClient.One(ctx, dynamo.LpaKey(data.LpaID), sk, &donor); err != nil { return nil, err @@ -225,7 +226,7 @@ func (s *donorStore) Latest(ctx context.Context) (*actor.DonorProvidedDetails, e return donor, nil } -func (s *donorStore) GetByKeys(ctx context.Context, keys []dynamo.Key) ([]actor.DonorProvidedDetails, error) { +func (s *donorStore) GetByKeys(ctx context.Context, keys []dynamo.Keys) ([]actor.DonorProvidedDetails, error) { if len(keys) == 0 { return nil, nil } @@ -249,14 +250,14 @@ func (s *donorStore) Put(ctx context.Context, donor *actor.DonorProvidedDetails) donor.Hash = newHash - // By not setting UpdatedAt until a UID exists, queries for SK=#DONOR#xyz on + // By not setting UpdatedAt until a UID exists, queries for SK=DONOR#xyz on // SKUpdatedAtIndex will not return UID-less LPAs. if donor.LpaUID != "" { donor.UpdatedAt = s.now() if err := s.searchClient.Index(ctx, search.Lpa{ - PK: donor.PK, - SK: donor.SK, + PK: donor.PK.PK(), + SK: donor.SK.SK(), DonorFullName: donor.Donor.FullName(), }); err != nil { return fmt.Errorf("donorStore index failed: %w", err) diff --git a/internal/app/donor_store_test.go b/internal/app/donor_store_test.go index 711a5de298..b7f6bbd32e 100644 --- a/internal/app/donor_store_test.go +++ b/internal/app/donor_store_test.go @@ -35,7 +35,7 @@ var ( func (m *mockDynamoClient) ExpectOne(ctx, pk, sk, data interface{}, err error) { m. On("One", ctx, pk, sk, mock.Anything). - Return(func(ctx context.Context, pk, partialSk string, v interface{}) error { + Return(func(ctx context.Context, pk dynamo.PK, partialSk dynamo.SK, v interface{}) error { b, _ := json.Marshal(data) json.Unmarshal(b, v) return err @@ -46,7 +46,7 @@ func (m *mockDynamoClient) ExpectOne(ctx, pk, sk, data interface{}, err error) { func (m *mockDynamoClient) ExpectOneByPK(ctx, pk, data interface{}, err error) { m. On("OneByPK", ctx, pk, mock.Anything). - Return(func(ctx context.Context, pk string, v interface{}) error { + Return(func(ctx context.Context, pk dynamo.PK, v interface{}) error { b, _ := json.Marshal(data) json.Unmarshal(b, v) return err @@ -57,7 +57,7 @@ func (m *mockDynamoClient) ExpectOneByPK(ctx, pk, data interface{}, err error) { func (m *mockDynamoClient) ExpectOneByPartialSK(ctx, pk, partialSk, data interface{}, err error) { m. On("OneByPartialSK", ctx, pk, partialSk, mock.Anything). - Return(func(ctx context.Context, pk, partialSk string, v interface{}) error { + Return(func(ctx context.Context, pk dynamo.PK, partialSk dynamo.SK, v interface{}) error { b, _ := json.Marshal(data) json.Unmarshal(b, v) return err @@ -67,7 +67,7 @@ func (m *mockDynamoClient) ExpectOneByPartialSK(ctx, pk, partialSk, data interfa func (m *mockDynamoClient) ExpectAllByPartialSK(ctx, pk, partialSk, data interface{}, err error) { m. On("AllByPartialSK", ctx, pk, partialSk, mock.Anything). - Return(func(ctx context.Context, pk, partialSk string, v interface{}) error { + Return(func(ctx context.Context, pk dynamo.PK, partialSk dynamo.SK, v interface{}) error { b, _ := json.Marshal(data) json.Unmarshal(b, v) return err @@ -77,7 +77,7 @@ func (m *mockDynamoClient) ExpectAllByPartialSK(ctx, pk, partialSk, data interfa func (m *mockDynamoClient) ExpectAllBySK(ctx, sk, data interface{}, err error) { m. On("AllBySK", ctx, sk, mock.Anything). - Return(func(ctx context.Context, pk string, v interface{}) error { + Return(func(ctx context.Context, sk dynamo.SK, v interface{}) error { b, _ := json.Marshal(data) json.Unmarshal(b, v) return err @@ -87,23 +87,23 @@ func (m *mockDynamoClient) ExpectAllBySK(ctx, sk, data interface{}, err error) { func (m *mockDynamoClient) ExpectLatestForActor(ctx, sk, data interface{}, err error) { m. On("LatestForActor", ctx, sk, mock.Anything). - Return(func(ctx context.Context, sk string, v interface{}) error { + Return(func(ctx context.Context, sk dynamo.SK, v interface{}) error { b, _ := json.Marshal(data) json.Unmarshal(b, v) return err }) } -func (m *mockDynamoClient) ExpectAllByKeys(ctx context.Context, keys []dynamo.Key, data interface{}, err error) { - m. - On("AllByKeys", ctx, keys, mock.Anything). +func (m *mockDynamoClient) ExpectAllByKeys(ctx context.Context, keys []dynamo.Keys, data []map[string]types.AttributeValue, err error) { + m.EXPECT(). + AllByKeys(ctx, keys). Return(data, err) } func (m *mockDynamoClient) ExpectOneBySK(ctx, sk, data interface{}, err error) { m. On("OneBySK", ctx, sk, mock.Anything). - Return(func(ctx context.Context, sk string, v interface{}) error { + Return(func(ctx context.Context, sk dynamo.SK, v interface{}) error { b, _ := json.Marshal(data) json.Unmarshal(b, v) return err @@ -114,7 +114,7 @@ func TestDonorStoreGetAny(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{LpaID: "an-id"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectOneByPartialSK(ctx, "LPA#an-id", "#DONOR#", &actor.DonorProvidedDetails{LpaID: "an-id"}, nil) + dynamoClient.ExpectOneByPartialSK(ctx, dynamo.LpaKey("an-id"), dynamo.DonorKey(""), &actor.DonorProvidedDetails{LpaID: "an-id"}, nil) donorStore := &donorStore{dynamoClient: dynamoClient, uuidString: func() string { return "10100000" }} @@ -127,7 +127,7 @@ func TestDonorStoreGetAnyWhenOrganisation(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{LpaID: "an-id", OrganisationID: "x"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectOneByPartialSK(ctx, "LPA#an-id", "ORGANISATION#", &actor.DonorProvidedDetails{LpaID: "an-id"}, nil) + dynamoClient.ExpectOneByPartialSK(ctx, dynamo.LpaKey("an-id"), dynamo.OrganisationKey(""), &actor.DonorProvidedDetails{LpaID: "an-id"}, nil) donorStore := &donorStore{dynamoClient: dynamoClient, uuidString: func() string { return "10100000" }} @@ -147,7 +147,7 @@ func TestDonorStoreGetAnyWhenDataStoreError(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{LpaID: "an-id"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectOneByPartialSK(ctx, "LPA#an-id", "#DONOR#", &actor.DonorProvidedDetails{LpaID: "an-id"}, expectedError) + dynamoClient.ExpectOneByPartialSK(ctx, dynamo.LpaKey("an-id"), dynamo.DonorKey(""), &actor.DonorProvidedDetails{LpaID: "an-id"}, expectedError) donorStore := &donorStore{dynamoClient: dynamoClient, uuidString: func() string { return "10100000" }} @@ -158,15 +158,15 @@ func TestDonorStoreGetAnyWhenDataStoreError(t *testing.T) { func TestDonorStoreGet(t *testing.T) { testCases := map[string]struct { sessionData *page.SessionData - expectedSK string + expectedSK dynamo.SK }{ "donor": { sessionData: &page.SessionData{LpaID: "an-id", SessionID: "456"}, - expectedSK: "#DONOR#456", + expectedSK: dynamo.DonorKey("456"), }, "organisation": { sessionData: &page.SessionData{LpaID: "an-id", SessionID: "456", OrganisationID: "789"}, - expectedSK: "ORGANISATION#789", + expectedSK: dynamo.OrganisationKey("789"), }, } @@ -175,7 +175,7 @@ func TestDonorStoreGet(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), tc.sessionData) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectOne(ctx, "LPA#an-id", tc.expectedSK, &actor.DonorProvidedDetails{LpaID: "an-id"}, nil) + dynamoClient.ExpectOne(ctx, dynamo.LpaKey("an-id"), tc.expectedSK, &actor.DonorProvidedDetails{LpaID: "an-id"}, nil) donorStore := &donorStore{dynamoClient: dynamoClient, uuidString: func() string { return "10100000" }} @@ -190,8 +190,8 @@ func TestDonorStoreGetWhenReferenced(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{LpaID: "an-id", SessionID: "456"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectOne(ctx, "LPA#an-id", "#DONOR#456", lpaReference{ReferencedSK: "ORGANISATION#789"}, nil) - dynamoClient.ExpectOne(ctx, "LPA#an-id", "ORGANISATION#789", &actor.DonorProvidedDetails{LpaID: "an-id"}, expectedError) + dynamoClient.ExpectOne(ctx, dynamo.LpaKey("an-id"), dynamo.DonorKey("456"), lpaReference{ReferencedSK: dynamo.OrganisationKey("789")}, nil) + dynamoClient.ExpectOne(ctx, dynamo.LpaKey("an-id"), dynamo.OrganisationKey("789"), &actor.DonorProvidedDetails{LpaID: "an-id"}, expectedError) donorStore := &donorStore{dynamoClient: dynamoClient, uuidString: func() string { return "10100000" }} @@ -211,7 +211,7 @@ func TestDonorStoreGetWhenDataStoreError(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{LpaID: "an-id", SessionID: "456"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectOne(ctx, "LPA#an-id", "#DONOR#456", lpaReference{ReferencedSK: "ref"}, expectedError) + dynamoClient.ExpectOne(ctx, dynamo.LpaKey("an-id"), dynamo.DonorKey("456"), lpaReference{ReferencedSK: "ref"}, expectedError) donorStore := &donorStore{dynamoClient: dynamoClient, uuidString: func() string { return "10100000" }} @@ -223,7 +223,7 @@ func TestDonorStoreLatest(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{LpaID: "an-id", SessionID: "456"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectLatestForActor(ctx, "#DONOR#456", &actor.DonorProvidedDetails{LpaID: "an-id"}, nil) + dynamoClient.ExpectLatestForActor(ctx, dynamo.DonorKey("456"), &actor.DonorProvidedDetails{LpaID: "an-id"}, nil) donorStore := &donorStore{dynamoClient: dynamoClient, uuidString: func() string { return "10100000" }} @@ -243,7 +243,7 @@ func TestDonorStoreLatestWhenDataStoreError(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{LpaID: "an-id", SessionID: "456"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectLatestForActor(ctx, "#DONOR#456", &actor.DonorProvidedDetails{LpaID: "an-id"}, expectedError) + dynamoClient.ExpectLatestForActor(ctx, dynamo.DonorKey("456"), &actor.DonorProvidedDetails{LpaID: "an-id"}, expectedError) donorStore := &donorStore{dynamoClient: dynamoClient, uuidString: func() string { return "10100000" }} @@ -252,7 +252,7 @@ func TestDonorStoreLatestWhenDataStoreError(t *testing.T) { } func TestDonorStoreGetByKeys(t *testing.T) { - keys := []dynamo.Key{{}} + keys := []dynamo.Keys{{}} donors := []actor.DonorProvidedDetails{{LpaID: "1"}, {LpaID: "2"}} av0, _ := attributevalue.MarshalMap(donors[0]) av1, _ := attributevalue.MarshalMap(donors[1]) @@ -269,7 +269,7 @@ func TestDonorStoreGetByKeys(t *testing.T) { } func TestDonorStoreGetByKeysWhenNoKeys(t *testing.T) { - keys := []dynamo.Key{} + keys := []dynamo.Keys{} donorStore := &donorStore{} @@ -279,7 +279,7 @@ func TestDonorStoreGetByKeysWhenNoKeys(t *testing.T) { } func TestDonorStoreGetByKeysWhenDynamoErrors(t *testing.T) { - keys := []dynamo.Key{{}} + keys := []dynamo.Keys{{}} dynamoClient := newMockDynamoClient(t) dynamoClient.ExpectAllByKeys(ctx, keys, @@ -292,7 +292,7 @@ func TestDonorStoreGetByKeysWhenDynamoErrors(t *testing.T) { } func TestDonorStorePut(t *testing.T) { - saved := &actor.DonorProvidedDetails{PK: "LPA#5", SK: "#DONOR#an-id", LpaID: "5", HasSentApplicationUpdatedEvent: true, Donor: actor.Donor{FirstNames: "x", LastName: "y"}} + saved := &actor.DonorProvidedDetails{PK: dynamo.LpaKey("5"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), LpaID: "5", HasSentApplicationUpdatedEvent: true, Donor: actor.Donor{FirstNames: "x", LastName: "y"}} saved.Hash, _ = saved.GenerateHash() dynamoClient := newMockDynamoClient(t) @@ -302,12 +302,12 @@ func TestDonorStorePut(t *testing.T) { donorStore := &donorStore{dynamoClient: dynamoClient, now: testNowFn} - err := donorStore.Put(ctx, &actor.DonorProvidedDetails{PK: "LPA#5", Hash: 5, SK: "#DONOR#an-id", LpaID: "5", HasSentApplicationUpdatedEvent: true, Donor: actor.Donor{FirstNames: "x", LastName: "y"}}) + err := donorStore.Put(ctx, &actor.DonorProvidedDetails{PK: dynamo.LpaKey("5"), Hash: 5, SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), LpaID: "5", HasSentApplicationUpdatedEvent: true, Donor: actor.Donor{FirstNames: "x", LastName: "y"}}) assert.Nil(t, err) } func TestDonorStorePutWhenUIDSet(t *testing.T) { - saved := &actor.DonorProvidedDetails{PK: "LPA#5", SK: "#DONOR#an-id", LpaID: "5", HasSentApplicationUpdatedEvent: true, LpaUID: "M", UpdatedAt: testNow, Donor: actor.Donor{FirstNames: "x", LastName: "y"}} + saved := &actor.DonorProvidedDetails{PK: dynamo.LpaKey("5"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), LpaID: "5", HasSentApplicationUpdatedEvent: true, LpaUID: "M", UpdatedAt: testNow, Donor: actor.Donor{FirstNames: "x", LastName: "y"}} saved.Hash, _ = saved.GenerateHash() dynamoClient := newMockDynamoClient(t) @@ -317,12 +317,12 @@ func TestDonorStorePutWhenUIDSet(t *testing.T) { searchClient := newMockSearchClient(t) searchClient.EXPECT(). - Index(ctx, search.Lpa{PK: "LPA#5", SK: "#DONOR#an-id", DonorFullName: "x y"}). + Index(ctx, search.Lpa{PK: dynamo.LpaKey("5").PK(), SK: dynamo.DonorKey("an-id").SK(), DonorFullName: "x y"}). Return(nil) donorStore := &donorStore{dynamoClient: dynamoClient, searchClient: searchClient, now: testNowFn} - err := donorStore.Put(ctx, &actor.DonorProvidedDetails{PK: "LPA#5", Hash: 5, SK: "#DONOR#an-id", LpaID: "5", HasSentApplicationUpdatedEvent: true, LpaUID: "M", Donor: actor.Donor{FirstNames: "x", LastName: "y"}}) + err := donorStore.Put(ctx, &actor.DonorProvidedDetails{PK: dynamo.LpaKey("5"), Hash: 5, SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), LpaID: "5", HasSentApplicationUpdatedEvent: true, LpaUID: "M", Donor: actor.Donor{FirstNames: "x", LastName: "y"}}) assert.Nil(t, err) } @@ -334,7 +334,7 @@ func TestDonorStorePutWhenUIDSetIndexErrors(t *testing.T) { donorStore := &donorStore{searchClient: searchClient, now: testNowFn} - err := donorStore.Put(ctx, &actor.DonorProvidedDetails{PK: "LPA#5", Hash: 5, SK: "#DONOR#an-id", LpaID: "5", HasSentApplicationUpdatedEvent: true, LpaUID: "M", Donor: actor.Donor{FirstNames: "x", LastName: "y"}}) + err := donorStore.Put(ctx, &actor.DonorProvidedDetails{PK: dynamo.LpaKey("5"), Hash: 5, SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), LpaID: "5", HasSentApplicationUpdatedEvent: true, LpaUID: "M", Donor: actor.Donor{FirstNames: "x", LastName: "y"}}) assert.ErrorIs(t, err, expectedError) } @@ -354,7 +354,7 @@ func TestDonorStorePutWhenError(t *testing.T) { donorStore := &donorStore{dynamoClient: dynamoClient, now: time.Now} - err := donorStore.Put(ctx, &actor.DonorProvidedDetails{PK: "LPA#5", SK: "#DONOR#an-id", LpaID: "5"}) + err := donorStore.Put(ctx, &actor.DonorProvidedDetails{PK: dynamo.LpaKey("5"), SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), LpaID: "5"}) assert.Equal(t, expectedError, err) } @@ -376,8 +376,8 @@ func TestDonorStorePutWhenUIDNeeded(t *testing.T) { Return(nil) updatedDonor := &actor.DonorProvidedDetails{ - PK: "LPA#5", - SK: "#DONOR#an-id", + PK: dynamo.LpaKey("5"), + SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), LpaID: "5", Donor: actor.Donor{ FirstNames: "John", @@ -401,8 +401,8 @@ func TestDonorStorePutWhenUIDNeeded(t *testing.T) { donorStore := &donorStore{dynamoClient: dynamoClient, eventClient: eventClient} err := donorStore.Put(ctx, &actor.DonorProvidedDetails{ - PK: "LPA#5", - SK: "#DONOR#an-id", + PK: dynamo.LpaKey("5"), + SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), LpaID: "5", Donor: actor.Donor{ FirstNames: "John", @@ -423,8 +423,8 @@ func TestDonorStorePutWhenUIDNeededMissingSessionData(t *testing.T) { donorStore := &donorStore{} err := donorStore.Put(ctx, &actor.DonorProvidedDetails{ - PK: "LPA#5", - SK: "#DONOR#an-id", + PK: dynamo.LpaKey("5"), + SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), LpaID: "5", Donor: actor.Donor{ FirstNames: "John", @@ -452,8 +452,8 @@ func TestDonorStorePutWhenUIDFails(t *testing.T) { donorStore := &donorStore{eventClient: eventClient, now: time.Now} err := donorStore.Put(ctx, &actor.DonorProvidedDetails{ - PK: "LPA#5", - SK: "#DONOR#an-id", + PK: dynamo.LpaKey("5"), + SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), LpaID: "5", Donor: actor.Donor{ FirstNames: "John", @@ -483,8 +483,8 @@ func TestDonorStorePutWhenApplicationUpdatedWhenError(t *testing.T) { donorStore := &donorStore{eventClient: eventClient, searchClient: searchClient, now: testNowFn} err := donorStore.Put(ctx, &actor.DonorProvidedDetails{ - PK: "LPA#5", - SK: "#DONOR#an-id", + PK: dynamo.LpaKey("5"), + SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), LpaID: "5", LpaUID: "M-1111", Donor: actor.Donor{ @@ -511,8 +511,8 @@ func TestDonorStorePutWhenPreviousApplicationLinked(t *testing.T) { Return(nil) updatedDonor := &actor.DonorProvidedDetails{ - PK: "LPA#5", - SK: "#DONOR#an-id", + PK: dynamo.LpaKey("5"), + SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), LpaID: "5", LpaUID: "M-1111", UpdatedAt: testNow, @@ -535,8 +535,8 @@ func TestDonorStorePutWhenPreviousApplicationLinked(t *testing.T) { donorStore := &donorStore{dynamoClient: dynamoClient, eventClient: eventClient, searchClient: searchClient, now: testNowFn} err := donorStore.Put(ctx, &actor.DonorProvidedDetails{ - PK: "LPA#5", - SK: "#DONOR#an-id", + PK: dynamo.LpaKey("5"), + SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), LpaID: "5", LpaUID: "M-1111", PreviousApplicationNumber: "5555", @@ -560,8 +560,8 @@ func TestDonorStorePutWhenPreviousApplicationLinkedWontResend(t *testing.T) { donorStore := &donorStore{dynamoClient: dynamoClient, searchClient: searchClient, now: testNowFn} err := donorStore.Put(ctx, &actor.DonorProvidedDetails{ - PK: "LPA#5", - SK: "#DONOR#an-id", + PK: dynamo.LpaKey("5"), + SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), LpaID: "5", LpaUID: "M-1111", PreviousApplicationNumber: "5555", @@ -586,8 +586,8 @@ func TestDonorStorePutWhenPreviousApplicationLinkedWhenError(t *testing.T) { donorStore := &donorStore{eventClient: eventClient, searchClient: searchClient, now: testNowFn} err := donorStore.Put(ctx, &actor.DonorProvidedDetails{ - PK: "LPA#5", - SK: "#DONOR#an-id", + PK: dynamo.LpaKey("5"), + SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), LpaID: "5", LpaUID: "M-1111", PreviousApplicationNumber: "5555", @@ -615,8 +615,8 @@ func TestDonorStoreCreate(t *testing.T) { t.Run(name, func(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: "an-id"}) donor := &actor.DonorProvidedDetails{ - PK: "LPA#10100000", - SK: "#DONOR#an-id", + PK: dynamo.LpaKey("10100000"), + SK: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), LpaID: "10100000", CreatedAt: testNow, Version: 1, @@ -634,12 +634,12 @@ func TestDonorStoreCreate(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectLatestForActor(ctx, "#DONOR#an-id", previousDetails, nil) + ExpectLatestForActor(ctx, dynamo.DonorKey("an-id"), previousDetails, nil) dynamoClient.EXPECT(). Create(ctx, donor). Return(nil) dynamoClient.EXPECT(). - Create(ctx, lpaLink{PK: "LPA#10100000", SK: "#SUB#an-id", DonorKey: "#DONOR#an-id", ActorType: actor.TypeDonor, UpdatedAt: testNow}). + Create(ctx, lpaLink{PK: dynamo.LpaKey("10100000"), SK: dynamo.SubKey("an-id"), DonorKey: dynamo.LpaOwnerKey(dynamo.DonorKey("an-id")), ActorType: actor.TypeDonor, UpdatedAt: testNow}). Return(nil) donorStore := &donorStore{dynamoClient: dynamoClient, uuidString: func() string { return "10100000" }, now: testNowFn, newUID: testUIDFn} @@ -665,14 +665,14 @@ func TestDonorStoreCreateWhenError(t *testing.T) { "latest": func(t *testing.T) *mockDynamoClient { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectLatestForActor(ctx, "#DONOR#an-id", actor.DonorProvidedDetails{}, expectedError) + ExpectLatestForActor(ctx, dynamo.DonorKey("an-id"), actor.DonorProvidedDetails{}, expectedError) return dynamoClient }, "donor record": func(t *testing.T) *mockDynamoClient { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectLatestForActor(ctx, "#DONOR#an-id", actor.DonorProvidedDetails{}, nil) + ExpectLatestForActor(ctx, dynamo.DonorKey("an-id"), actor.DonorProvidedDetails{}, nil) dynamoClient.EXPECT(). Create(ctx, mock.Anything). Return(expectedError) @@ -682,7 +682,7 @@ func TestDonorStoreCreateWhenError(t *testing.T) { "link record": func(t *testing.T) *mockDynamoClient { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectLatestForActor(ctx, "#DONOR#an-id", actor.DonorProvidedDetails{}, nil) + ExpectLatestForActor(ctx, dynamo.DonorKey("an-id"), actor.DonorProvidedDetails{}, nil) dynamoClient.EXPECT(). Create(ctx, mock.Anything). Return(nil). @@ -722,8 +722,8 @@ func TestDonorStoreLink(t *testing.T) { }, "not a donor link": { link: lpaLink{ - PK: "LPA#", - SK: "#SUB#a-sub", + PK: dynamo.LpaKey(""), + SK: dynamo.SubKey("a-sub"), ActorType: actor.TypeCertificateProvider, }, }, @@ -739,20 +739,20 @@ func TestDonorStoreLink(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectOneByPartialSK(ctx, "LPA#lpa-id", "#SUB#", tc.link, tc.oneByPartialSKError) + ExpectOneByPartialSK(ctx, dynamo.LpaKey("lpa-id"), dynamo.SubKey(""), tc.link, tc.oneByPartialSKError) dynamoClient.EXPECT(). Create(ctx, lpaReference{ - PK: "LPA#lpa-id", - SK: "#DONOR#an-id", - ReferencedSK: "ORGANISATION#org-id", + PK: dynamo.LpaKey("lpa-id"), + SK: dynamo.DonorKey("an-id"), + ReferencedSK: dynamo.OrganisationKey("org-id"), }). Return(nil). Once() dynamoClient.EXPECT(). Create(ctx, lpaLink{ - PK: "LPA#lpa-id", - SK: "#SUB#an-id", - DonorKey: "ORGANISATION#org-id", + PK: dynamo.LpaKey("lpa-id"), + SK: dynamo.SubKey("an-id"), + DonorKey: dynamo.LpaOwnerKey(dynamo.OrganisationKey("org-id")), ActorType: actor.TypeDonor, UpdatedAt: testNow, }). @@ -787,7 +787,7 @@ func TestDonorStoreLinkWhenDonorLinkAlreadyExists(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectOneByPartialSK(ctx, "LPA#lpa-id", "#SUB#", lpaLink{PK: "LPA#lpa-id", SK: "#SUB#a-sub", ActorType: actor.TypeDonor}, nil) + ExpectOneByPartialSK(ctx, dynamo.LpaKey("lpa-id"), dynamo.SubKey(""), lpaLink{PK: dynamo.LpaKey("lpa-id"), SK: dynamo.SubKey("a-sub"), ActorType: actor.TypeDonor}, nil) donorStore := &donorStore{dynamoClient: dynamoClient} @@ -799,18 +799,18 @@ func TestDonorStoreLinkWhenError(t *testing.T) { testcases := map[string]func(*mockDynamoClient){ "OneByPartialSK errors": func(dynamoClient *mockDynamoClient) { dynamoClient. - ExpectOneByPartialSK(mock.Anything, "LPA#lpa-id", "#SUB#", lpaLink{PK: "LPA#lpa-id", SK: "#SUB#a-sub", ActorType: actor.TypeAttorney}, expectedError) + ExpectOneByPartialSK(mock.Anything, dynamo.LpaKey("lpa-id"), dynamo.SubKey(""), lpaLink{PK: dynamo.LpaKey("lpa-id"), SK: dynamo.SubKey("a-sub"), ActorType: actor.TypeAttorney}, expectedError) }, "lpaReference errors": func(dynamoClient *mockDynamoClient) { dynamoClient. - ExpectOneByPartialSK(mock.Anything, "LPA#lpa-id", "#SUB#", lpaLink{PK: "LPA#lpa-id", SK: "#SUB#a-sub", ActorType: actor.TypeAttorney}, nil) + ExpectOneByPartialSK(mock.Anything, dynamo.LpaKey("lpa-id"), dynamo.SubKey(""), lpaLink{PK: dynamo.LpaKey("lpa-id"), SK: dynamo.SubKey("a-sub"), ActorType: actor.TypeAttorney}, nil) dynamoClient.EXPECT(). Create(mock.Anything, mock.Anything). Return(expectedError) }, "lpaLink errors": func(dynamoClient *mockDynamoClient) { dynamoClient. - ExpectOneByPartialSK(mock.Anything, "LPA#lpa-id", "#SUB#", lpaLink{PK: "LPA#lpa-id", SK: "#SUB#a-sub", ActorType: actor.TypeAttorney}, nil) + ExpectOneByPartialSK(mock.Anything, dynamo.LpaKey("lpa-id"), dynamo.SubKey(""), lpaLink{PK: dynamo.LpaKey("lpa-id"), SK: dynamo.SubKey("a-sub"), ActorType: actor.TypeAttorney}, nil) dynamoClient.EXPECT(). Create(mock.Anything, mock.Anything). Return(nil). @@ -844,15 +844,15 @@ func TestDonorStoreLinkWhenError(t *testing.T) { func TestDonorStoreDelete(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: "an-id", LpaID: "123"}) - keys := []dynamo.Key{ - {PK: "LPA#123", SK: "sk1"}, - {PK: "LPA#123", SK: "sk2"}, - {PK: "LPA#123", SK: "#DONOR#an-id"}, + keys := []dynamo.Keys{ + {PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("sk1")}, + {PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("sk2")}, + {PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("an-id")}, } dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). - AllKeysByPK(ctx, "LPA#123"). + AllKeysByPK(ctx, dynamo.LpaKey("123")). Return(keys, nil) dynamoClient.EXPECT(). DeleteKeys(ctx, keys). @@ -867,15 +867,15 @@ func TestDonorStoreDelete(t *testing.T) { func TestDonorStoreDeleteWhenOtherDonor(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: "an-id", LpaID: "123"}) - keys := []dynamo.Key{ - {PK: "LPA#123", SK: "sk1"}, - {PK: "LPA#123", SK: "sk2"}, - {PK: "LPA#123", SK: "#DONOR#another-id"}, + keys := []dynamo.Keys{ + {PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("sk1")}, + {PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("sk2")}, + {PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("another-id")}, } dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). - AllKeysByPK(ctx, "LPA#123"). + AllKeysByPK(ctx, dynamo.LpaKey("123")). Return(keys, nil) donorStore := &donorStore{dynamoClient: dynamoClient} @@ -889,7 +889,7 @@ func TestDonorStoreDeleteWhenAllKeysByPKErrors(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). - AllKeysByPK(ctx, "LPA#123"). + AllKeysByPK(ctx, dynamo.LpaKey("123")). Return(nil, expectedError) donorStore := &donorStore{dynamoClient: dynamoClient} @@ -903,8 +903,8 @@ func TestDonorStoreDeleteWhenDeleteKeysErrors(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). - AllKeysByPK(ctx, "LPA#123"). - Return([]dynamo.Key{{PK: "LPA#123", SK: "#DONOR#an-id"}}, nil) + AllKeysByPK(ctx, dynamo.LpaKey("123")). + Return([]dynamo.Keys{{PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("an-id")}}, nil) dynamoClient.EXPECT(). DeleteKeys(ctx, mock.Anything). Return(expectedError) @@ -936,13 +936,13 @@ func TestDonorStoreDeleteLink(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: "an-id", OrganisationID: "org-id"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectOneByPartialSK(ctx, "LPA#lpa-id", "#SUB#", lpaLink{PK: "#LPA#lpa-id", SK: "#SUB#donor-sub"}, nil) + dynamoClient.ExpectOneByPartialSK(ctx, dynamo.LpaKey("lpa-id"), dynamo.SubKey(""), lpaLink{PK: dynamo.LpaKey("lpa-id"), SK: dynamo.SubKey("donor-sub")}, nil) dynamoClient.EXPECT(). - DeleteOne(ctx, "#LPA#lpa-id", "#SUB#donor-sub"). + DeleteOne(ctx, dynamo.LpaKey("lpa-id"), dynamo.SubKey("donor-sub")). Return(nil). Once() dynamoClient.EXPECT(). - DeleteOne(ctx, "LPA#lpa-id", "#DONOR#donor-sub"). + DeleteOne(ctx, dynamo.LpaKey("lpa-id"), dynamo.DonorKey("donor-sub")). Return(nil). Once() @@ -981,7 +981,7 @@ func TestDonorStoreDeleteLinkWhenOneByPartialSKError(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: "an-id", OrganisationID: "org-id"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectOneByPartialSK(ctx, "LPA#lpa-id", "#SUB#", lpaLink{PK: "#LPA#lpa-id", SK: "#SUB#donor-sub"}, expectedError) + dynamoClient.ExpectOneByPartialSK(ctx, dynamo.LpaKey("lpa-id"), dynamo.SubKey(""), lpaLink{PK: dynamo.LpaKey("lpa-id"), SK: dynamo.SubKey("donor-sub")}, expectedError) donorStore := &donorStore{dynamoClient: dynamoClient} @@ -993,7 +993,7 @@ func TestDonorStoreDeleteLinkWhenDeleteOneLinkError(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: "an-id", OrganisationID: "org-id"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectOneByPartialSK(ctx, "LPA#lpa-id", "#SUB#", lpaLink{PK: "#LPA#lpa-id", SK: "#SUB#donor-sub"}, nil) + dynamoClient.ExpectOneByPartialSK(ctx, dynamo.LpaKey("lpa-id"), dynamo.SubKey(""), lpaLink{PK: dynamo.LpaKey("lpa-id"), SK: dynamo.SubKey("donor-sub")}, nil) dynamoClient.EXPECT(). DeleteOne(mock.Anything, mock.Anything, mock.Anything). Return(expectedError) @@ -1008,13 +1008,13 @@ func TestDonorStoreDeleteLinkWhenDeleteOneLPAReferenceError(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: "an-id", OrganisationID: "org-id"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectOneByPartialSK(ctx, "LPA#lpa-id", "#SUB#", lpaLink{PK: "#LPA#lpa-id", SK: "#SUB#donor-sub"}, nil) + dynamoClient.ExpectOneByPartialSK(ctx, dynamo.LpaKey("lpa-id"), dynamo.SubKey(""), lpaLink{PK: dynamo.LpaKey("lpa-id"), SK: dynamo.SubKey("donor-sub")}, nil) dynamoClient.EXPECT(). DeleteOne(mock.Anything, mock.Anything, mock.Anything). Return(nil). Once() dynamoClient.EXPECT(). - DeleteOne(ctx, "LPA#lpa-id", "#DONOR#donor-sub"). + DeleteOne(ctx, dynamo.LpaKey("lpa-id"), dynamo.DonorKey("donor-sub")). Return(expectedError). Once() diff --git a/internal/app/evidence_received_store_test.go b/internal/app/evidence_received_store_test.go index 744325535f..533d8c5ebf 100644 --- a/internal/app/evidence_received_store_test.go +++ b/internal/app/evidence_received_store_test.go @@ -14,7 +14,7 @@ func TestEvidenceReceivedStoreGet(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{LpaID: "an-id", SessionID: "456"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectOne(ctx, "LPA#an-id", "#EVIDENCE_RECEIVED", nil, nil) + dynamoClient.ExpectOne(ctx, dynamo.LpaKey("an-id"), dynamo.EvidenceReceivedKey(), nil, nil) evidenceReceivedStore := &evidenceReceivedStore{dynamoClient: dynamoClient} @@ -27,7 +27,7 @@ func TestEvidenceReceivedStoreGetWhenFalse(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{LpaID: "an-id", SessionID: "456"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectOne(ctx, "LPA#an-id", "#EVIDENCE_RECEIVED", nil, dynamo.NotFoundError{}) + dynamoClient.ExpectOne(ctx, dynamo.LpaKey("an-id"), dynamo.EvidenceReceivedKey(), nil, dynamo.NotFoundError{}) evidenceReceivedStore := &evidenceReceivedStore{dynamoClient: dynamoClient} @@ -49,7 +49,7 @@ func TestEvidenceReceivedStoreGetWhenDataStoreError(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{LpaID: "an-id", SessionID: "456"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectOne(ctx, "LPA#an-id", "#EVIDENCE_RECEIVED", &actor.DonorProvidedDetails{LpaID: "an-id"}, expectedError) + dynamoClient.ExpectOne(ctx, dynamo.LpaKey("an-id"), dynamo.EvidenceReceivedKey(), &actor.DonorProvidedDetails{LpaID: "an-id"}, expectedError) evidenceReceivedStore := &evidenceReceivedStore{dynamoClient: dynamoClient} diff --git a/internal/app/member_store_test.go b/internal/app/member_store_test.go index 3989db27fe..a95566c1e2 100644 --- a/internal/app/member_store_test.go +++ b/internal/app/member_store_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" + "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" "github.com/ministryofjustice/opg-modernising-lpa/internal/page" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -16,8 +17,8 @@ func TestMemberStoreCreateMemberInvite(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). Create(ctx, &actor.MemberInvite{ - PK: "ORGANISATION#an-id", - SK: "MEMBERINVITE#ZW1haWxAZXhhbXBsZS5jb20=", + PK: dynamo.OrganisationKey("an-id"), + SK: dynamo.MemberInviteKey("email@example.com"), CreatedAt: testNow, OrganisationID: "a-uuid", OrganisationName: "org name", @@ -57,7 +58,7 @@ func TestMemberStoreInvitedMember(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{Email: "a@example.org"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectOneBySK(ctx, "MEMBERINVITE#YUBleGFtcGxlLm9yZw==", &actor.MemberInvite{OrganisationID: "an-id"}, nil) + dynamoClient.ExpectOneBySK(ctx, dynamo.MemberInviteKey("a@example.org"), &actor.MemberInvite{OrganisationID: "an-id"}, nil) memberStore := &memberStore{dynamoClient: dynamoClient, now: testNowFn, uuidString: func() string { return "a-uuid" }} @@ -115,8 +116,8 @@ func TestMemberStoreInvitedMembers(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{OrganisationID: "an-id"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectAllByPartialSK(ctx, "ORGANISATION#an-id", - "MEMBERINVITE#", []*actor.MemberInvite{{OrganisationID: "an-id"}, {OrganisationID: "an-id"}}, nil) + dynamoClient.ExpectAllByPartialSK(ctx, dynamo.OrganisationKey("an-id"), + dynamo.MemberInviteKey(""), []*actor.MemberInvite{{OrganisationID: "an-id"}, {OrganisationID: "an-id"}}, nil) memberStore := &memberStore{dynamoClient: dynamoClient, now: testNowFn, uuidString: func() string { return "a-uuid" }} @@ -147,8 +148,8 @@ func TestMemberStoreInvitedMembersWhenDynamoClientError(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{OrganisationID: "an-id"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectAllByPartialSK(ctx, "ORGANISATION#an-id", - "MEMBERINVITE#", []*actor.MemberInvite{}, expectedError) + dynamoClient.ExpectAllByPartialSK(ctx, dynamo.OrganisationKey("an-id"), + dynamo.MemberInviteKey(""), []*actor.MemberInvite{}, expectedError) memberStore := &memberStore{dynamoClient: dynamoClient, now: testNowFn, uuidString: func() string { return "a-uuid" }} @@ -161,7 +162,7 @@ func TestMemberStoreInvitedMembersByEmail(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{Email: "a@example.org"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectAllBySK(ctx, "MEMBERINVITE#YUBleGFtcGxlLm9yZw==", []*actor.MemberInvite{ + dynamoClient.ExpectAllBySK(ctx, dynamo.MemberInviteKey("a@example.org"), []*actor.MemberInvite{ {OrganisationID: "an-id", Email: "a@example.org"}, {OrganisationID: "another-id", Email: "a@example.org"}, }, nil) @@ -212,7 +213,7 @@ func TestPut(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). - Put(ctx, &actor.Member{PK: "ORGANISATION#123", SK: "ORGANISATION#456", UpdatedAt: testNow}). + Put(ctx, &actor.Member{PK: dynamo.OrganisationKey("123"), SK: dynamo.MemberKey("456"), UpdatedAt: testNow}). Return(nil) store := &memberStore{ @@ -220,7 +221,7 @@ func TestPut(t *testing.T) { now: testNowFn, } - err := store.Put(ctx, &actor.Member{PK: "ORGANISATION#123", SK: "ORGANISATION#456"}) + err := store.Put(ctx, &actor.Member{PK: dynamo.OrganisationKey("123"), SK: dynamo.MemberKey("456")}) assert.Nil(t, err) } @@ -237,7 +238,7 @@ func TestPutWhenDynamoError(t *testing.T) { now: testNowFn, } - err := store.Put(ctx, &actor.Member{PK: "ORGANISATION#123", SK: "ORGANISATION#456"}) + err := store.Put(ctx, &actor.Member{PK: dynamo.OrganisationKey("123"), SK: dynamo.MemberKey("456")}) assert.Equal(t, expectedError, err) } @@ -245,8 +246,8 @@ func TestMemberStoreGetAll(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{OrganisationID: "an-id"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectAllByPartialSK(ctx, "ORGANISATION#an-id", - "MEMBER#", []*actor.Member{{FirstNames: "a"}, {FirstNames: "c"}, {FirstNames: "b"}}, nil) + dynamoClient.ExpectAllByPartialSK(ctx, dynamo.OrganisationKey("an-id"), + dynamo.MemberKey(""), []*actor.Member{{FirstNames: "a"}, {FirstNames: "c"}, {FirstNames: "b"}}, nil) memberStore := &memberStore{dynamoClient: dynamoClient, now: testNowFn, uuidString: func() string { return "a-uuid" }} @@ -277,8 +278,8 @@ func TestMemberStoreGetAllWhenDynamoClientError(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{OrganisationID: "an-id"}) dynamoClient := newMockDynamoClient(t) - dynamoClient.ExpectAllByPartialSK(ctx, "ORGANISATION#an-id", - "MEMBER#", []*actor.MemberInvite{}, expectedError) + dynamoClient.ExpectAllByPartialSK(ctx, dynamo.OrganisationKey("an-id"), + dynamo.MemberKey(""), []*actor.MemberInvite{}, expectedError) memberStore := &memberStore{dynamoClient: dynamoClient, now: testNowFn, uuidString: func() string { return "a-uuid" }} @@ -296,7 +297,7 @@ func TestMemberStoreGet(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectOne(ctx, "ORGANISATION#a-uuid", "MEMBER#session-id", + ExpectOne(ctx, dynamo.OrganisationKey("a-uuid"), dynamo.MemberKey("session-id"), member, nil) memberStore := &memberStore{dynamoClient: dynamoClient, now: testNowFn, uuidString: func() string { return "a-uuid" }} @@ -328,7 +329,7 @@ func TestMemberStoreGetWhenErrors(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectOne(ctx, "ORGANISATION#a-uuid", "MEMBER#session-id", + ExpectOne(ctx, dynamo.OrganisationKey("a-uuid"), dynamo.MemberKey("session-id"), nil, expectedError) memberStore := &memberStore{dynamoClient: dynamoClient, now: testNowFn, uuidString: func() string { return "a-uuid" }} @@ -339,8 +340,8 @@ func TestMemberStoreGetWhenErrors(t *testing.T) { func TestMemberStoreCreate(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: "session-id", Email: "email@example.com"}) expectedMember := &actor.Member{ - PK: "ORGANISATION#a-uuid", - SK: "MEMBER#session-id", + PK: dynamo.OrganisationKey("a-uuid"), + SK: dynamo.MemberKey("session-id"), CreatedAt: testNow, UpdatedAt: testNow, ID: "a-uuid", @@ -360,9 +361,9 @@ func TestMemberStoreCreate(t *testing.T) { Once() dynamoClient.EXPECT(). Create(ctx, &organisationLink{ - PK: "ORGANISATION#a-uuid", - SK: "MEMBERID#a-uuid", - MemberSK: "MEMBER#session-id", + PK: dynamo.OrganisationKey("a-uuid"), + SK: dynamo.MemberIDKey("a-uuid"), + MemberSK: dynamo.MemberKey("session-id"), }). Return(nil). Once() @@ -448,8 +449,8 @@ func TestMemberStoreCreateFromInvite(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). Create(ctx, &actor.Member{ - PK: "ORGANISATION#org-id", - SK: "MEMBER#session-id", + PK: dynamo.OrganisationKey("org-id"), + SK: dynamo.MemberKey("session-id"), CreatedAt: testNow, UpdatedAt: testNow, ID: "a-uuid", @@ -464,14 +465,14 @@ func TestMemberStoreCreateFromInvite(t *testing.T) { Return(nil) dynamoClient.EXPECT(). - DeleteOne(ctx, "ORGANISATION#org-id", "MEMBERINVITE#YWJAZXhhbXBsZS5vcmc="). + DeleteOne(ctx, dynamo.OrganisationKey("org-id"), dynamo.MemberInviteKey(invite.Email)). Return(nil) dynamoClient.EXPECT(). Create(ctx, &organisationLink{ - PK: "ORGANISATION#org-id", - SK: "MEMBERID#a-uuid", - MemberSK: "MEMBER#session-id", + PK: dynamo.OrganisationKey("org-id"), + SK: dynamo.MemberIDKey("a-uuid"), + MemberSK: dynamo.MemberKey("session-id"), }). Return(nil) @@ -549,21 +550,21 @@ func TestMemberStoreGetByID(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectOne(ctx, "ORGANISATION#org-id", "MEMBERID#1", + ExpectOne(ctx, dynamo.OrganisationKey("org-id"), dynamo.MemberIDKey("1"), &organisationLink{ - PK: "ORGANISATION#org-id", - SK: "MEMBERID#1", - MemberSK: "MEMBER#a-uuid", + PK: dynamo.OrganisationKey("org-id"), + SK: dynamo.MemberIDKey("1"), + MemberSK: dynamo.MemberKey("a-uuid"), }, nil) expectedMember := &actor.Member{ - PK: "ORGANISATION#org-id", - SK: "MEMBER#a-uuid", + PK: dynamo.OrganisationKey("org-id"), + SK: dynamo.MemberKey("a-uuid"), ID: "1", } dynamoClient. - ExpectOne(ctx, "ORGANISATION#org-id", "MEMBER#a-uuid", + ExpectOne(ctx, dynamo.OrganisationKey("org-id"), dynamo.MemberKey("a-uuid"), expectedMember, nil) memberStore := &memberStore{dynamoClient: dynamoClient, now: testNowFn, uuidString: func() string { return "a-uuid" }} @@ -635,7 +636,7 @@ func TestMemberStoreGetAny(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectOneBySK(ctx, "MEMBER#session-id", + ExpectOneBySK(ctx, dynamo.MemberKey("session-id"), expectedMember, nil) memberStore := &memberStore{dynamoClient: dynamoClient, now: testNowFn, uuidString: func() string { return "a-uuid" }} @@ -667,7 +668,7 @@ func TestMemberStoreGetAnyWhenDynamoClientErrors(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectOneBySK(ctx, "MEMBER#session-id", + ExpectOneBySK(ctx, dynamo.MemberKey("session-id"), nil, expectedError) memberStore := &memberStore{dynamoClient: dynamoClient, now: testNowFn, uuidString: func() string { return "a-uuid" }} diff --git a/internal/app/mock_DynamoClient_test.go b/internal/app/mock_DynamoClient_test.go index 695f09a830..49452ec563 100644 --- a/internal/app/mock_DynamoClient_test.go +++ b/internal/app/mock_DynamoClient_test.go @@ -25,7 +25,7 @@ func (_m *mockDynamoClient) EXPECT() *mockDynamoClient_Expecter { } // AllByKeys provides a mock function with given fields: ctx, keys -func (_m *mockDynamoClient) AllByKeys(ctx context.Context, keys []dynamo.Key) ([]map[string]types.AttributeValue, error) { +func (_m *mockDynamoClient) AllByKeys(ctx context.Context, keys []dynamo.Keys) ([]map[string]types.AttributeValue, error) { ret := _m.Called(ctx, keys) if len(ret) == 0 { @@ -34,10 +34,10 @@ func (_m *mockDynamoClient) AllByKeys(ctx context.Context, keys []dynamo.Key) ([ var r0 []map[string]types.AttributeValue var r1 error - if rf, ok := ret.Get(0).(func(context.Context, []dynamo.Key) ([]map[string]types.AttributeValue, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, []dynamo.Keys) ([]map[string]types.AttributeValue, error)); ok { return rf(ctx, keys) } - if rf, ok := ret.Get(0).(func(context.Context, []dynamo.Key) []map[string]types.AttributeValue); ok { + if rf, ok := ret.Get(0).(func(context.Context, []dynamo.Keys) []map[string]types.AttributeValue); ok { r0 = rf(ctx, keys) } else { if ret.Get(0) != nil { @@ -45,7 +45,7 @@ func (_m *mockDynamoClient) AllByKeys(ctx context.Context, keys []dynamo.Key) ([ } } - if rf, ok := ret.Get(1).(func(context.Context, []dynamo.Key) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, []dynamo.Keys) error); ok { r1 = rf(ctx, keys) } else { r1 = ret.Error(1) @@ -61,14 +61,14 @@ type mockDynamoClient_AllByKeys_Call struct { // AllByKeys is a helper method to define mock.On call // - ctx context.Context -// - keys []dynamo.Key +// - keys []dynamo.Keys func (_e *mockDynamoClient_Expecter) AllByKeys(ctx interface{}, keys interface{}) *mockDynamoClient_AllByKeys_Call { return &mockDynamoClient_AllByKeys_Call{Call: _e.mock.On("AllByKeys", ctx, keys)} } -func (_c *mockDynamoClient_AllByKeys_Call) Run(run func(ctx context.Context, keys []dynamo.Key)) *mockDynamoClient_AllByKeys_Call { +func (_c *mockDynamoClient_AllByKeys_Call) Run(run func(ctx context.Context, keys []dynamo.Keys)) *mockDynamoClient_AllByKeys_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]dynamo.Key)) + run(args[0].(context.Context), args[1].([]dynamo.Keys)) }) return _c } @@ -78,13 +78,13 @@ func (_c *mockDynamoClient_AllByKeys_Call) Return(_a0 []map[string]types.Attribu return _c } -func (_c *mockDynamoClient_AllByKeys_Call) RunAndReturn(run func(context.Context, []dynamo.Key) ([]map[string]types.AttributeValue, error)) *mockDynamoClient_AllByKeys_Call { +func (_c *mockDynamoClient_AllByKeys_Call) RunAndReturn(run func(context.Context, []dynamo.Keys) ([]map[string]types.AttributeValue, error)) *mockDynamoClient_AllByKeys_Call { _c.Call.Return(run) return _c } // AllByPartialSK provides a mock function with given fields: ctx, pk, partialSK, v -func (_m *mockDynamoClient) AllByPartialSK(ctx context.Context, pk string, partialSK string, v interface{}) error { +func (_m *mockDynamoClient) AllByPartialSK(ctx context.Context, pk dynamo.PK, partialSK dynamo.SK, v interface{}) error { ret := _m.Called(ctx, pk, partialSK, v) if len(ret) == 0 { @@ -92,7 +92,7 @@ func (_m *mockDynamoClient) AllByPartialSK(ctx context.Context, pk string, parti } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, interface{}) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.PK, dynamo.SK, interface{}) error); ok { r0 = rf(ctx, pk, partialSK, v) } else { r0 = ret.Error(0) @@ -108,16 +108,16 @@ type mockDynamoClient_AllByPartialSK_Call struct { // AllByPartialSK is a helper method to define mock.On call // - ctx context.Context -// - pk string -// - partialSK string +// - pk dynamo.PK +// - partialSK dynamo.SK // - v interface{} func (_e *mockDynamoClient_Expecter) AllByPartialSK(ctx interface{}, pk interface{}, partialSK interface{}, v interface{}) *mockDynamoClient_AllByPartialSK_Call { return &mockDynamoClient_AllByPartialSK_Call{Call: _e.mock.On("AllByPartialSK", ctx, pk, partialSK, v)} } -func (_c *mockDynamoClient_AllByPartialSK_Call) Run(run func(ctx context.Context, pk string, partialSK string, v interface{})) *mockDynamoClient_AllByPartialSK_Call { +func (_c *mockDynamoClient_AllByPartialSK_Call) Run(run func(ctx context.Context, pk dynamo.PK, partialSK dynamo.SK, v interface{})) *mockDynamoClient_AllByPartialSK_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(interface{})) + run(args[0].(context.Context), args[1].(dynamo.PK), args[2].(dynamo.SK), args[3].(interface{})) }) return _c } @@ -127,13 +127,13 @@ func (_c *mockDynamoClient_AllByPartialSK_Call) Return(_a0 error) *mockDynamoCli return _c } -func (_c *mockDynamoClient_AllByPartialSK_Call) RunAndReturn(run func(context.Context, string, string, interface{}) error) *mockDynamoClient_AllByPartialSK_Call { +func (_c *mockDynamoClient_AllByPartialSK_Call) RunAndReturn(run func(context.Context, dynamo.PK, dynamo.SK, interface{}) error) *mockDynamoClient_AllByPartialSK_Call { _c.Call.Return(run) return _c } // AllBySK provides a mock function with given fields: ctx, sk, v -func (_m *mockDynamoClient) AllBySK(ctx context.Context, sk string, v interface{}) error { +func (_m *mockDynamoClient) AllBySK(ctx context.Context, sk dynamo.SK, v interface{}) error { ret := _m.Called(ctx, sk, v) if len(ret) == 0 { @@ -141,7 +141,7 @@ func (_m *mockDynamoClient) AllBySK(ctx context.Context, sk string, v interface{ } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, interface{}) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.SK, interface{}) error); ok { r0 = rf(ctx, sk, v) } else { r0 = ret.Error(0) @@ -157,15 +157,15 @@ type mockDynamoClient_AllBySK_Call struct { // AllBySK is a helper method to define mock.On call // - ctx context.Context -// - sk string +// - sk dynamo.SK // - v interface{} func (_e *mockDynamoClient_Expecter) AllBySK(ctx interface{}, sk interface{}, v interface{}) *mockDynamoClient_AllBySK_Call { return &mockDynamoClient_AllBySK_Call{Call: _e.mock.On("AllBySK", ctx, sk, v)} } -func (_c *mockDynamoClient_AllBySK_Call) Run(run func(ctx context.Context, sk string, v interface{})) *mockDynamoClient_AllBySK_Call { +func (_c *mockDynamoClient_AllBySK_Call) Run(run func(ctx context.Context, sk dynamo.SK, v interface{})) *mockDynamoClient_AllBySK_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(interface{})) + run(args[0].(context.Context), args[1].(dynamo.SK), args[2].(interface{})) }) return _c } @@ -175,33 +175,33 @@ func (_c *mockDynamoClient_AllBySK_Call) Return(_a0 error) *mockDynamoClient_All return _c } -func (_c *mockDynamoClient_AllBySK_Call) RunAndReturn(run func(context.Context, string, interface{}) error) *mockDynamoClient_AllBySK_Call { +func (_c *mockDynamoClient_AllBySK_Call) RunAndReturn(run func(context.Context, dynamo.SK, interface{}) error) *mockDynamoClient_AllBySK_Call { _c.Call.Return(run) return _c } // AllKeysByPK provides a mock function with given fields: ctx, pk -func (_m *mockDynamoClient) AllKeysByPK(ctx context.Context, pk string) ([]dynamo.Key, error) { +func (_m *mockDynamoClient) AllKeysByPK(ctx context.Context, pk dynamo.PK) ([]dynamo.Keys, error) { ret := _m.Called(ctx, pk) if len(ret) == 0 { panic("no return value specified for AllKeysByPK") } - var r0 []dynamo.Key + var r0 []dynamo.Keys var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string) ([]dynamo.Key, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.PK) ([]dynamo.Keys, error)); ok { return rf(ctx, pk) } - if rf, ok := ret.Get(0).(func(context.Context, string) []dynamo.Key); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.PK) []dynamo.Keys); ok { r0 = rf(ctx, pk) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).([]dynamo.Key) + r0 = ret.Get(0).([]dynamo.Keys) } } - if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, dynamo.PK) error); ok { r1 = rf(ctx, pk) } else { r1 = ret.Error(1) @@ -217,24 +217,24 @@ type mockDynamoClient_AllKeysByPK_Call struct { // AllKeysByPK is a helper method to define mock.On call // - ctx context.Context -// - pk string +// - pk dynamo.PK func (_e *mockDynamoClient_Expecter) AllKeysByPK(ctx interface{}, pk interface{}) *mockDynamoClient_AllKeysByPK_Call { return &mockDynamoClient_AllKeysByPK_Call{Call: _e.mock.On("AllKeysByPK", ctx, pk)} } -func (_c *mockDynamoClient_AllKeysByPK_Call) Run(run func(ctx context.Context, pk string)) *mockDynamoClient_AllKeysByPK_Call { +func (_c *mockDynamoClient_AllKeysByPK_Call) Run(run func(ctx context.Context, pk dynamo.PK)) *mockDynamoClient_AllKeysByPK_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string)) + run(args[0].(context.Context), args[1].(dynamo.PK)) }) return _c } -func (_c *mockDynamoClient_AllKeysByPK_Call) Return(_a0 []dynamo.Key, _a1 error) *mockDynamoClient_AllKeysByPK_Call { +func (_c *mockDynamoClient_AllKeysByPK_Call) Return(_a0 []dynamo.Keys, _a1 error) *mockDynamoClient_AllKeysByPK_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *mockDynamoClient_AllKeysByPK_Call) RunAndReturn(run func(context.Context, string) ([]dynamo.Key, error)) *mockDynamoClient_AllKeysByPK_Call { +func (_c *mockDynamoClient_AllKeysByPK_Call) RunAndReturn(run func(context.Context, dynamo.PK) ([]dynamo.Keys, error)) *mockDynamoClient_AllKeysByPK_Call { _c.Call.Return(run) return _c } @@ -334,7 +334,7 @@ func (_c *mockDynamoClient_Create_Call) RunAndReturn(run func(context.Context, i } // DeleteKeys provides a mock function with given fields: ctx, keys -func (_m *mockDynamoClient) DeleteKeys(ctx context.Context, keys []dynamo.Key) error { +func (_m *mockDynamoClient) DeleteKeys(ctx context.Context, keys []dynamo.Keys) error { ret := _m.Called(ctx, keys) if len(ret) == 0 { @@ -342,7 +342,7 @@ func (_m *mockDynamoClient) DeleteKeys(ctx context.Context, keys []dynamo.Key) e } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, []dynamo.Key) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, []dynamo.Keys) error); ok { r0 = rf(ctx, keys) } else { r0 = ret.Error(0) @@ -358,14 +358,14 @@ type mockDynamoClient_DeleteKeys_Call struct { // DeleteKeys is a helper method to define mock.On call // - ctx context.Context -// - keys []dynamo.Key +// - keys []dynamo.Keys func (_e *mockDynamoClient_Expecter) DeleteKeys(ctx interface{}, keys interface{}) *mockDynamoClient_DeleteKeys_Call { return &mockDynamoClient_DeleteKeys_Call{Call: _e.mock.On("DeleteKeys", ctx, keys)} } -func (_c *mockDynamoClient_DeleteKeys_Call) Run(run func(ctx context.Context, keys []dynamo.Key)) *mockDynamoClient_DeleteKeys_Call { +func (_c *mockDynamoClient_DeleteKeys_Call) Run(run func(ctx context.Context, keys []dynamo.Keys)) *mockDynamoClient_DeleteKeys_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]dynamo.Key)) + run(args[0].(context.Context), args[1].([]dynamo.Keys)) }) return _c } @@ -375,13 +375,13 @@ func (_c *mockDynamoClient_DeleteKeys_Call) Return(_a0 error) *mockDynamoClient_ return _c } -func (_c *mockDynamoClient_DeleteKeys_Call) RunAndReturn(run func(context.Context, []dynamo.Key) error) *mockDynamoClient_DeleteKeys_Call { +func (_c *mockDynamoClient_DeleteKeys_Call) RunAndReturn(run func(context.Context, []dynamo.Keys) error) *mockDynamoClient_DeleteKeys_Call { _c.Call.Return(run) return _c } // DeleteOne provides a mock function with given fields: ctx, pk, sk -func (_m *mockDynamoClient) DeleteOne(ctx context.Context, pk string, sk string) error { +func (_m *mockDynamoClient) DeleteOne(ctx context.Context, pk dynamo.PK, sk dynamo.SK) error { ret := _m.Called(ctx, pk, sk) if len(ret) == 0 { @@ -389,7 +389,7 @@ func (_m *mockDynamoClient) DeleteOne(ctx context.Context, pk string, sk string) } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.PK, dynamo.SK) error); ok { r0 = rf(ctx, pk, sk) } else { r0 = ret.Error(0) @@ -405,15 +405,15 @@ type mockDynamoClient_DeleteOne_Call struct { // DeleteOne is a helper method to define mock.On call // - ctx context.Context -// - pk string -// - sk string +// - pk dynamo.PK +// - sk dynamo.SK func (_e *mockDynamoClient_Expecter) DeleteOne(ctx interface{}, pk interface{}, sk interface{}) *mockDynamoClient_DeleteOne_Call { return &mockDynamoClient_DeleteOne_Call{Call: _e.mock.On("DeleteOne", ctx, pk, sk)} } -func (_c *mockDynamoClient_DeleteOne_Call) Run(run func(ctx context.Context, pk string, sk string)) *mockDynamoClient_DeleteOne_Call { +func (_c *mockDynamoClient_DeleteOne_Call) Run(run func(ctx context.Context, pk dynamo.PK, sk dynamo.SK)) *mockDynamoClient_DeleteOne_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(string)) + run(args[0].(context.Context), args[1].(dynamo.PK), args[2].(dynamo.SK)) }) return _c } @@ -423,13 +423,13 @@ func (_c *mockDynamoClient_DeleteOne_Call) Return(_a0 error) *mockDynamoClient_D return _c } -func (_c *mockDynamoClient_DeleteOne_Call) RunAndReturn(run func(context.Context, string, string) error) *mockDynamoClient_DeleteOne_Call { +func (_c *mockDynamoClient_DeleteOne_Call) RunAndReturn(run func(context.Context, dynamo.PK, dynamo.SK) error) *mockDynamoClient_DeleteOne_Call { _c.Call.Return(run) return _c } // LatestForActor provides a mock function with given fields: ctx, sk, v -func (_m *mockDynamoClient) LatestForActor(ctx context.Context, sk string, v interface{}) error { +func (_m *mockDynamoClient) LatestForActor(ctx context.Context, sk dynamo.SK, v interface{}) error { ret := _m.Called(ctx, sk, v) if len(ret) == 0 { @@ -437,7 +437,7 @@ func (_m *mockDynamoClient) LatestForActor(ctx context.Context, sk string, v int } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, interface{}) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.SK, interface{}) error); ok { r0 = rf(ctx, sk, v) } else { r0 = ret.Error(0) @@ -453,15 +453,15 @@ type mockDynamoClient_LatestForActor_Call struct { // LatestForActor is a helper method to define mock.On call // - ctx context.Context -// - sk string +// - sk dynamo.SK // - v interface{} func (_e *mockDynamoClient_Expecter) LatestForActor(ctx interface{}, sk interface{}, v interface{}) *mockDynamoClient_LatestForActor_Call { return &mockDynamoClient_LatestForActor_Call{Call: _e.mock.On("LatestForActor", ctx, sk, v)} } -func (_c *mockDynamoClient_LatestForActor_Call) Run(run func(ctx context.Context, sk string, v interface{})) *mockDynamoClient_LatestForActor_Call { +func (_c *mockDynamoClient_LatestForActor_Call) Run(run func(ctx context.Context, sk dynamo.SK, v interface{})) *mockDynamoClient_LatestForActor_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(interface{})) + run(args[0].(context.Context), args[1].(dynamo.SK), args[2].(interface{})) }) return _c } @@ -471,13 +471,13 @@ func (_c *mockDynamoClient_LatestForActor_Call) Return(_a0 error) *mockDynamoCli return _c } -func (_c *mockDynamoClient_LatestForActor_Call) RunAndReturn(run func(context.Context, string, interface{}) error) *mockDynamoClient_LatestForActor_Call { +func (_c *mockDynamoClient_LatestForActor_Call) RunAndReturn(run func(context.Context, dynamo.SK, interface{}) error) *mockDynamoClient_LatestForActor_Call { _c.Call.Return(run) return _c } // One provides a mock function with given fields: ctx, pk, sk, v -func (_m *mockDynamoClient) One(ctx context.Context, pk string, sk string, v interface{}) error { +func (_m *mockDynamoClient) One(ctx context.Context, pk dynamo.PK, sk dynamo.SK, v interface{}) error { ret := _m.Called(ctx, pk, sk, v) if len(ret) == 0 { @@ -485,7 +485,7 @@ func (_m *mockDynamoClient) One(ctx context.Context, pk string, sk string, v int } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, interface{}) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.PK, dynamo.SK, interface{}) error); ok { r0 = rf(ctx, pk, sk, v) } else { r0 = ret.Error(0) @@ -501,16 +501,16 @@ type mockDynamoClient_One_Call struct { // One is a helper method to define mock.On call // - ctx context.Context -// - pk string -// - sk string +// - pk dynamo.PK +// - sk dynamo.SK // - v interface{} func (_e *mockDynamoClient_Expecter) One(ctx interface{}, pk interface{}, sk interface{}, v interface{}) *mockDynamoClient_One_Call { return &mockDynamoClient_One_Call{Call: _e.mock.On("One", ctx, pk, sk, v)} } -func (_c *mockDynamoClient_One_Call) Run(run func(ctx context.Context, pk string, sk string, v interface{})) *mockDynamoClient_One_Call { +func (_c *mockDynamoClient_One_Call) Run(run func(ctx context.Context, pk dynamo.PK, sk dynamo.SK, v interface{})) *mockDynamoClient_One_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(interface{})) + run(args[0].(context.Context), args[1].(dynamo.PK), args[2].(dynamo.SK), args[3].(interface{})) }) return _c } @@ -520,13 +520,13 @@ func (_c *mockDynamoClient_One_Call) Return(_a0 error) *mockDynamoClient_One_Cal return _c } -func (_c *mockDynamoClient_One_Call) RunAndReturn(run func(context.Context, string, string, interface{}) error) *mockDynamoClient_One_Call { +func (_c *mockDynamoClient_One_Call) RunAndReturn(run func(context.Context, dynamo.PK, dynamo.SK, interface{}) error) *mockDynamoClient_One_Call { _c.Call.Return(run) return _c } // OneByPK provides a mock function with given fields: ctx, pk, v -func (_m *mockDynamoClient) OneByPK(ctx context.Context, pk string, v interface{}) error { +func (_m *mockDynamoClient) OneByPK(ctx context.Context, pk dynamo.PK, v interface{}) error { ret := _m.Called(ctx, pk, v) if len(ret) == 0 { @@ -534,7 +534,7 @@ func (_m *mockDynamoClient) OneByPK(ctx context.Context, pk string, v interface{ } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, interface{}) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.PK, interface{}) error); ok { r0 = rf(ctx, pk, v) } else { r0 = ret.Error(0) @@ -550,15 +550,15 @@ type mockDynamoClient_OneByPK_Call struct { // OneByPK is a helper method to define mock.On call // - ctx context.Context -// - pk string +// - pk dynamo.PK // - v interface{} func (_e *mockDynamoClient_Expecter) OneByPK(ctx interface{}, pk interface{}, v interface{}) *mockDynamoClient_OneByPK_Call { return &mockDynamoClient_OneByPK_Call{Call: _e.mock.On("OneByPK", ctx, pk, v)} } -func (_c *mockDynamoClient_OneByPK_Call) Run(run func(ctx context.Context, pk string, v interface{})) *mockDynamoClient_OneByPK_Call { +func (_c *mockDynamoClient_OneByPK_Call) Run(run func(ctx context.Context, pk dynamo.PK, v interface{})) *mockDynamoClient_OneByPK_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(interface{})) + run(args[0].(context.Context), args[1].(dynamo.PK), args[2].(interface{})) }) return _c } @@ -568,13 +568,13 @@ func (_c *mockDynamoClient_OneByPK_Call) Return(_a0 error) *mockDynamoClient_One return _c } -func (_c *mockDynamoClient_OneByPK_Call) RunAndReturn(run func(context.Context, string, interface{}) error) *mockDynamoClient_OneByPK_Call { +func (_c *mockDynamoClient_OneByPK_Call) RunAndReturn(run func(context.Context, dynamo.PK, interface{}) error) *mockDynamoClient_OneByPK_Call { _c.Call.Return(run) return _c } // OneByPartialSK provides a mock function with given fields: ctx, pk, partialSK, v -func (_m *mockDynamoClient) OneByPartialSK(ctx context.Context, pk string, partialSK string, v interface{}) error { +func (_m *mockDynamoClient) OneByPartialSK(ctx context.Context, pk dynamo.PK, partialSK dynamo.SK, v interface{}) error { ret := _m.Called(ctx, pk, partialSK, v) if len(ret) == 0 { @@ -582,7 +582,7 @@ func (_m *mockDynamoClient) OneByPartialSK(ctx context.Context, pk string, parti } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, interface{}) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.PK, dynamo.SK, interface{}) error); ok { r0 = rf(ctx, pk, partialSK, v) } else { r0 = ret.Error(0) @@ -598,16 +598,16 @@ type mockDynamoClient_OneByPartialSK_Call struct { // OneByPartialSK is a helper method to define mock.On call // - ctx context.Context -// - pk string -// - partialSK string +// - pk dynamo.PK +// - partialSK dynamo.SK // - v interface{} func (_e *mockDynamoClient_Expecter) OneByPartialSK(ctx interface{}, pk interface{}, partialSK interface{}, v interface{}) *mockDynamoClient_OneByPartialSK_Call { return &mockDynamoClient_OneByPartialSK_Call{Call: _e.mock.On("OneByPartialSK", ctx, pk, partialSK, v)} } -func (_c *mockDynamoClient_OneByPartialSK_Call) Run(run func(ctx context.Context, pk string, partialSK string, v interface{})) *mockDynamoClient_OneByPartialSK_Call { +func (_c *mockDynamoClient_OneByPartialSK_Call) Run(run func(ctx context.Context, pk dynamo.PK, partialSK dynamo.SK, v interface{})) *mockDynamoClient_OneByPartialSK_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(interface{})) + run(args[0].(context.Context), args[1].(dynamo.PK), args[2].(dynamo.SK), args[3].(interface{})) }) return _c } @@ -617,13 +617,13 @@ func (_c *mockDynamoClient_OneByPartialSK_Call) Return(_a0 error) *mockDynamoCli return _c } -func (_c *mockDynamoClient_OneByPartialSK_Call) RunAndReturn(run func(context.Context, string, string, interface{}) error) *mockDynamoClient_OneByPartialSK_Call { +func (_c *mockDynamoClient_OneByPartialSK_Call) RunAndReturn(run func(context.Context, dynamo.PK, dynamo.SK, interface{}) error) *mockDynamoClient_OneByPartialSK_Call { _c.Call.Return(run) return _c } // OneBySK provides a mock function with given fields: ctx, sk, v -func (_m *mockDynamoClient) OneBySK(ctx context.Context, sk string, v interface{}) error { +func (_m *mockDynamoClient) OneBySK(ctx context.Context, sk dynamo.SK, v interface{}) error { ret := _m.Called(ctx, sk, v) if len(ret) == 0 { @@ -631,7 +631,7 @@ func (_m *mockDynamoClient) OneBySK(ctx context.Context, sk string, v interface{ } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, interface{}) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.SK, interface{}) error); ok { r0 = rf(ctx, sk, v) } else { r0 = ret.Error(0) @@ -647,15 +647,15 @@ type mockDynamoClient_OneBySK_Call struct { // OneBySK is a helper method to define mock.On call // - ctx context.Context -// - sk string +// - sk dynamo.SK // - v interface{} func (_e *mockDynamoClient_Expecter) OneBySK(ctx interface{}, sk interface{}, v interface{}) *mockDynamoClient_OneBySK_Call { return &mockDynamoClient_OneBySK_Call{Call: _e.mock.On("OneBySK", ctx, sk, v)} } -func (_c *mockDynamoClient_OneBySK_Call) Run(run func(ctx context.Context, sk string, v interface{})) *mockDynamoClient_OneBySK_Call { +func (_c *mockDynamoClient_OneBySK_Call) Run(run func(ctx context.Context, sk dynamo.SK, v interface{})) *mockDynamoClient_OneBySK_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(interface{})) + run(args[0].(context.Context), args[1].(dynamo.SK), args[2].(interface{})) }) return _c } @@ -665,7 +665,7 @@ func (_c *mockDynamoClient_OneBySK_Call) Return(_a0 error) *mockDynamoClient_One return _c } -func (_c *mockDynamoClient_OneBySK_Call) RunAndReturn(run func(context.Context, string, interface{}) error) *mockDynamoClient_OneBySK_Call { +func (_c *mockDynamoClient_OneBySK_Call) RunAndReturn(run func(context.Context, dynamo.SK, interface{}) error) *mockDynamoClient_OneBySK_Call { _c.Call.Return(run) return _c } @@ -766,7 +766,7 @@ func (_c *mockDynamoClient_Put_Call) RunAndReturn(run func(context.Context, inte } // Update provides a mock function with given fields: ctx, pk, sk, values, expression -func (_m *mockDynamoClient) Update(ctx context.Context, pk string, sk string, values map[string]types.AttributeValue, expression string) error { +func (_m *mockDynamoClient) Update(ctx context.Context, pk dynamo.PK, sk dynamo.SK, values map[string]types.AttributeValue, expression string) error { ret := _m.Called(ctx, pk, sk, values, expression) if len(ret) == 0 { @@ -774,7 +774,7 @@ func (_m *mockDynamoClient) Update(ctx context.Context, pk string, sk string, va } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, map[string]types.AttributeValue, string) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.PK, dynamo.SK, map[string]types.AttributeValue, string) error); ok { r0 = rf(ctx, pk, sk, values, expression) } else { r0 = ret.Error(0) @@ -790,17 +790,17 @@ type mockDynamoClient_Update_Call struct { // Update is a helper method to define mock.On call // - ctx context.Context -// - pk string -// - sk string +// - pk dynamo.PK +// - sk dynamo.SK // - values map[string]types.AttributeValue // - expression string func (_e *mockDynamoClient_Expecter) Update(ctx interface{}, pk interface{}, sk interface{}, values interface{}, expression interface{}) *mockDynamoClient_Update_Call { return &mockDynamoClient_Update_Call{Call: _e.mock.On("Update", ctx, pk, sk, values, expression)} } -func (_c *mockDynamoClient_Update_Call) Run(run func(ctx context.Context, pk string, sk string, values map[string]types.AttributeValue, expression string)) *mockDynamoClient_Update_Call { +func (_c *mockDynamoClient_Update_Call) Run(run func(ctx context.Context, pk dynamo.PK, sk dynamo.SK, values map[string]types.AttributeValue, expression string)) *mockDynamoClient_Update_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(map[string]types.AttributeValue), args[4].(string)) + run(args[0].(context.Context), args[1].(dynamo.PK), args[2].(dynamo.SK), args[3].(map[string]types.AttributeValue), args[4].(string)) }) return _c } @@ -810,7 +810,7 @@ func (_c *mockDynamoClient_Update_Call) Return(_a0 error) *mockDynamoClient_Upda return _c } -func (_c *mockDynamoClient_Update_Call) RunAndReturn(run func(context.Context, string, string, map[string]types.AttributeValue, string) error) *mockDynamoClient_Update_Call { +func (_c *mockDynamoClient_Update_Call) RunAndReturn(run func(context.Context, dynamo.PK, dynamo.SK, map[string]types.AttributeValue, string) error) *mockDynamoClient_Update_Call { _c.Call.Return(run) return _c } diff --git a/internal/app/mock_DynamoUpdateClient_test.go b/internal/app/mock_DynamoUpdateClient_test.go index 42301e303d..4feb4b46a3 100644 --- a/internal/app/mock_DynamoUpdateClient_test.go +++ b/internal/app/mock_DynamoUpdateClient_test.go @@ -5,8 +5,10 @@ package app import ( context "context" - types "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" + dynamo "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" mock "github.com/stretchr/testify/mock" + + types "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" ) // mockDynamoUpdateClient is an autogenerated mock type for the DynamoUpdateClient type @@ -23,7 +25,7 @@ func (_m *mockDynamoUpdateClient) EXPECT() *mockDynamoUpdateClient_Expecter { } // UpdateReturn provides a mock function with given fields: ctx, pk, sk, values, expression -func (_m *mockDynamoUpdateClient) UpdateReturn(ctx context.Context, pk string, sk string, values map[string]types.AttributeValue, expression string) (map[string]types.AttributeValue, error) { +func (_m *mockDynamoUpdateClient) UpdateReturn(ctx context.Context, pk dynamo.PK, sk dynamo.SK, values map[string]types.AttributeValue, expression string) (map[string]types.AttributeValue, error) { ret := _m.Called(ctx, pk, sk, values, expression) if len(ret) == 0 { @@ -32,10 +34,10 @@ func (_m *mockDynamoUpdateClient) UpdateReturn(ctx context.Context, pk string, s var r0 map[string]types.AttributeValue var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, map[string]types.AttributeValue, string) (map[string]types.AttributeValue, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.PK, dynamo.SK, map[string]types.AttributeValue, string) (map[string]types.AttributeValue, error)); ok { return rf(ctx, pk, sk, values, expression) } - if rf, ok := ret.Get(0).(func(context.Context, string, string, map[string]types.AttributeValue, string) map[string]types.AttributeValue); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.PK, dynamo.SK, map[string]types.AttributeValue, string) map[string]types.AttributeValue); ok { r0 = rf(ctx, pk, sk, values, expression) } else { if ret.Get(0) != nil { @@ -43,7 +45,7 @@ func (_m *mockDynamoUpdateClient) UpdateReturn(ctx context.Context, pk string, s } } - if rf, ok := ret.Get(1).(func(context.Context, string, string, map[string]types.AttributeValue, string) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, dynamo.PK, dynamo.SK, map[string]types.AttributeValue, string) error); ok { r1 = rf(ctx, pk, sk, values, expression) } else { r1 = ret.Error(1) @@ -59,17 +61,17 @@ type mockDynamoUpdateClient_UpdateReturn_Call struct { // UpdateReturn is a helper method to define mock.On call // - ctx context.Context -// - pk string -// - sk string +// - pk dynamo.PK +// - sk dynamo.SK // - values map[string]types.AttributeValue // - expression string func (_e *mockDynamoUpdateClient_Expecter) UpdateReturn(ctx interface{}, pk interface{}, sk interface{}, values interface{}, expression interface{}) *mockDynamoUpdateClient_UpdateReturn_Call { return &mockDynamoUpdateClient_UpdateReturn_Call{Call: _e.mock.On("UpdateReturn", ctx, pk, sk, values, expression)} } -func (_c *mockDynamoUpdateClient_UpdateReturn_Call) Run(run func(ctx context.Context, pk string, sk string, values map[string]types.AttributeValue, expression string)) *mockDynamoUpdateClient_UpdateReturn_Call { +func (_c *mockDynamoUpdateClient_UpdateReturn_Call) Run(run func(ctx context.Context, pk dynamo.PK, sk dynamo.SK, values map[string]types.AttributeValue, expression string)) *mockDynamoUpdateClient_UpdateReturn_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(map[string]types.AttributeValue), args[4].(string)) + run(args[0].(context.Context), args[1].(dynamo.PK), args[2].(dynamo.SK), args[3].(map[string]types.AttributeValue), args[4].(string)) }) return _c } @@ -79,7 +81,7 @@ func (_c *mockDynamoUpdateClient_UpdateReturn_Call) Return(_a0 map[string]types. return _c } -func (_c *mockDynamoUpdateClient_UpdateReturn_Call) RunAndReturn(run func(context.Context, string, string, map[string]types.AttributeValue, string) (map[string]types.AttributeValue, error)) *mockDynamoUpdateClient_UpdateReturn_Call { +func (_c *mockDynamoUpdateClient_UpdateReturn_Call) RunAndReturn(run func(context.Context, dynamo.PK, dynamo.SK, map[string]types.AttributeValue, string) (map[string]types.AttributeValue, error)) *mockDynamoUpdateClient_UpdateReturn_Call { _c.Call.Return(run) return _c } diff --git a/internal/app/mock_ShareCodeStoreDynamoClient_test.go b/internal/app/mock_ShareCodeStoreDynamoClient_test.go index 9f75b853f7..b2bf5c535d 100644 --- a/internal/app/mock_ShareCodeStoreDynamoClient_test.go +++ b/internal/app/mock_ShareCodeStoreDynamoClient_test.go @@ -5,6 +5,7 @@ package app import ( context "context" + dynamo "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" mock "github.com/stretchr/testify/mock" ) @@ -22,7 +23,7 @@ func (_m *mockShareCodeStoreDynamoClient) EXPECT() *mockShareCodeStoreDynamoClie } // DeleteOne provides a mock function with given fields: ctx, pk, sk -func (_m *mockShareCodeStoreDynamoClient) DeleteOne(ctx context.Context, pk string, sk string) error { +func (_m *mockShareCodeStoreDynamoClient) DeleteOne(ctx context.Context, pk dynamo.PK, sk dynamo.SK) error { ret := _m.Called(ctx, pk, sk) if len(ret) == 0 { @@ -30,7 +31,7 @@ func (_m *mockShareCodeStoreDynamoClient) DeleteOne(ctx context.Context, pk stri } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.PK, dynamo.SK) error); ok { r0 = rf(ctx, pk, sk) } else { r0 = ret.Error(0) @@ -46,15 +47,15 @@ type mockShareCodeStoreDynamoClient_DeleteOne_Call struct { // DeleteOne is a helper method to define mock.On call // - ctx context.Context -// - pk string -// - sk string +// - pk dynamo.PK +// - sk dynamo.SK func (_e *mockShareCodeStoreDynamoClient_Expecter) DeleteOne(ctx interface{}, pk interface{}, sk interface{}) *mockShareCodeStoreDynamoClient_DeleteOne_Call { return &mockShareCodeStoreDynamoClient_DeleteOne_Call{Call: _e.mock.On("DeleteOne", ctx, pk, sk)} } -func (_c *mockShareCodeStoreDynamoClient_DeleteOne_Call) Run(run func(ctx context.Context, pk string, sk string)) *mockShareCodeStoreDynamoClient_DeleteOne_Call { +func (_c *mockShareCodeStoreDynamoClient_DeleteOne_Call) Run(run func(ctx context.Context, pk dynamo.PK, sk dynamo.SK)) *mockShareCodeStoreDynamoClient_DeleteOne_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(string)) + run(args[0].(context.Context), args[1].(dynamo.PK), args[2].(dynamo.SK)) }) return _c } @@ -64,13 +65,13 @@ func (_c *mockShareCodeStoreDynamoClient_DeleteOne_Call) Return(_a0 error) *mock return _c } -func (_c *mockShareCodeStoreDynamoClient_DeleteOne_Call) RunAndReturn(run func(context.Context, string, string) error) *mockShareCodeStoreDynamoClient_DeleteOne_Call { +func (_c *mockShareCodeStoreDynamoClient_DeleteOne_Call) RunAndReturn(run func(context.Context, dynamo.PK, dynamo.SK) error) *mockShareCodeStoreDynamoClient_DeleteOne_Call { _c.Call.Return(run) return _c } // One provides a mock function with given fields: ctx, pk, sk, v -func (_m *mockShareCodeStoreDynamoClient) One(ctx context.Context, pk string, sk string, v interface{}) error { +func (_m *mockShareCodeStoreDynamoClient) One(ctx context.Context, pk dynamo.PK, sk dynamo.SK, v interface{}) error { ret := _m.Called(ctx, pk, sk, v) if len(ret) == 0 { @@ -78,7 +79,7 @@ func (_m *mockShareCodeStoreDynamoClient) One(ctx context.Context, pk string, sk } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, interface{}) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.PK, dynamo.SK, interface{}) error); ok { r0 = rf(ctx, pk, sk, v) } else { r0 = ret.Error(0) @@ -94,16 +95,16 @@ type mockShareCodeStoreDynamoClient_One_Call struct { // One is a helper method to define mock.On call // - ctx context.Context -// - pk string -// - sk string +// - pk dynamo.PK +// - sk dynamo.SK // - v interface{} func (_e *mockShareCodeStoreDynamoClient_Expecter) One(ctx interface{}, pk interface{}, sk interface{}, v interface{}) *mockShareCodeStoreDynamoClient_One_Call { return &mockShareCodeStoreDynamoClient_One_Call{Call: _e.mock.On("One", ctx, pk, sk, v)} } -func (_c *mockShareCodeStoreDynamoClient_One_Call) Run(run func(ctx context.Context, pk string, sk string, v interface{})) *mockShareCodeStoreDynamoClient_One_Call { +func (_c *mockShareCodeStoreDynamoClient_One_Call) Run(run func(ctx context.Context, pk dynamo.PK, sk dynamo.SK, v interface{})) *mockShareCodeStoreDynamoClient_One_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(interface{})) + run(args[0].(context.Context), args[1].(dynamo.PK), args[2].(dynamo.SK), args[3].(interface{})) }) return _c } @@ -113,13 +114,13 @@ func (_c *mockShareCodeStoreDynamoClient_One_Call) Return(_a0 error) *mockShareC return _c } -func (_c *mockShareCodeStoreDynamoClient_One_Call) RunAndReturn(run func(context.Context, string, string, interface{}) error) *mockShareCodeStoreDynamoClient_One_Call { +func (_c *mockShareCodeStoreDynamoClient_One_Call) RunAndReturn(run func(context.Context, dynamo.PK, dynamo.SK, interface{}) error) *mockShareCodeStoreDynamoClient_One_Call { _c.Call.Return(run) return _c } // OneByPK provides a mock function with given fields: ctx, pk, v -func (_m *mockShareCodeStoreDynamoClient) OneByPK(ctx context.Context, pk string, v interface{}) error { +func (_m *mockShareCodeStoreDynamoClient) OneByPK(ctx context.Context, pk dynamo.PK, v interface{}) error { ret := _m.Called(ctx, pk, v) if len(ret) == 0 { @@ -127,7 +128,7 @@ func (_m *mockShareCodeStoreDynamoClient) OneByPK(ctx context.Context, pk string } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, interface{}) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.PK, interface{}) error); ok { r0 = rf(ctx, pk, v) } else { r0 = ret.Error(0) @@ -143,15 +144,15 @@ type mockShareCodeStoreDynamoClient_OneByPK_Call struct { // OneByPK is a helper method to define mock.On call // - ctx context.Context -// - pk string +// - pk dynamo.PK // - v interface{} func (_e *mockShareCodeStoreDynamoClient_Expecter) OneByPK(ctx interface{}, pk interface{}, v interface{}) *mockShareCodeStoreDynamoClient_OneByPK_Call { return &mockShareCodeStoreDynamoClient_OneByPK_Call{Call: _e.mock.On("OneByPK", ctx, pk, v)} } -func (_c *mockShareCodeStoreDynamoClient_OneByPK_Call) Run(run func(ctx context.Context, pk string, v interface{})) *mockShareCodeStoreDynamoClient_OneByPK_Call { +func (_c *mockShareCodeStoreDynamoClient_OneByPK_Call) Run(run func(ctx context.Context, pk dynamo.PK, v interface{})) *mockShareCodeStoreDynamoClient_OneByPK_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(interface{})) + run(args[0].(context.Context), args[1].(dynamo.PK), args[2].(interface{})) }) return _c } @@ -161,13 +162,13 @@ func (_c *mockShareCodeStoreDynamoClient_OneByPK_Call) Return(_a0 error) *mockSh return _c } -func (_c *mockShareCodeStoreDynamoClient_OneByPK_Call) RunAndReturn(run func(context.Context, string, interface{}) error) *mockShareCodeStoreDynamoClient_OneByPK_Call { +func (_c *mockShareCodeStoreDynamoClient_OneByPK_Call) RunAndReturn(run func(context.Context, dynamo.PK, interface{}) error) *mockShareCodeStoreDynamoClient_OneByPK_Call { _c.Call.Return(run) return _c } // OneBySK provides a mock function with given fields: ctx, sk, v -func (_m *mockShareCodeStoreDynamoClient) OneBySK(ctx context.Context, sk string, v interface{}) error { +func (_m *mockShareCodeStoreDynamoClient) OneBySK(ctx context.Context, sk dynamo.SK, v interface{}) error { ret := _m.Called(ctx, sk, v) if len(ret) == 0 { @@ -175,7 +176,7 @@ func (_m *mockShareCodeStoreDynamoClient) OneBySK(ctx context.Context, sk string } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, interface{}) error); ok { + if rf, ok := ret.Get(0).(func(context.Context, dynamo.SK, interface{}) error); ok { r0 = rf(ctx, sk, v) } else { r0 = ret.Error(0) @@ -191,15 +192,15 @@ type mockShareCodeStoreDynamoClient_OneBySK_Call struct { // OneBySK is a helper method to define mock.On call // - ctx context.Context -// - sk string +// - sk dynamo.SK // - v interface{} func (_e *mockShareCodeStoreDynamoClient_Expecter) OneBySK(ctx interface{}, sk interface{}, v interface{}) *mockShareCodeStoreDynamoClient_OneBySK_Call { return &mockShareCodeStoreDynamoClient_OneBySK_Call{Call: _e.mock.On("OneBySK", ctx, sk, v)} } -func (_c *mockShareCodeStoreDynamoClient_OneBySK_Call) Run(run func(ctx context.Context, sk string, v interface{})) *mockShareCodeStoreDynamoClient_OneBySK_Call { +func (_c *mockShareCodeStoreDynamoClient_OneBySK_Call) Run(run func(ctx context.Context, sk dynamo.SK, v interface{})) *mockShareCodeStoreDynamoClient_OneBySK_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(interface{})) + run(args[0].(context.Context), args[1].(dynamo.SK), args[2].(interface{})) }) return _c } @@ -209,7 +210,7 @@ func (_c *mockShareCodeStoreDynamoClient_OneBySK_Call) Return(_a0 error) *mockSh return _c } -func (_c *mockShareCodeStoreDynamoClient_OneBySK_Call) RunAndReturn(run func(context.Context, string, interface{}) error) *mockShareCodeStoreDynamoClient_OneBySK_Call { +func (_c *mockShareCodeStoreDynamoClient_OneBySK_Call) RunAndReturn(run func(context.Context, dynamo.SK, interface{}) error) *mockShareCodeStoreDynamoClient_OneBySK_Call { _c.Call.Return(run) return _c } diff --git a/internal/app/organisation_store.go b/internal/app/organisation_store.go index bb1c3890a2..23d2a41f27 100644 --- a/internal/app/organisation_store.go +++ b/internal/app/organisation_store.go @@ -23,10 +23,10 @@ type organisationStore struct { // An organisationLink is used to join a Member to an Organisation to be accessed by MemberID. type organisationLink struct { // PK is the same as the PK for the Member - PK string + PK dynamo.OrganisationKeyType // SK is the Member ID for the Member - SK string - MemberSK string + SK dynamo.MemberIDKeyType + MemberSK dynamo.MemberKeyType } func (s *organisationStore) Create(ctx context.Context, member *actor.Member, name string) (*actor.Organisation, error) { @@ -101,7 +101,7 @@ func (s *organisationStore) CreateLPA(ctx context.Context) (*actor.DonorProvided donor := &actor.DonorProvidedDetails{ PK: dynamo.LpaKey(lpaID), - SK: dynamo.OrganisationKey(data.OrganisationID), + SK: dynamo.LpaOwnerKey(dynamo.OrganisationKey(data.OrganisationID)), LpaID: lpaID, CreatedAt: s.now(), Version: 1, diff --git a/internal/app/organisation_store_test.go b/internal/app/organisation_store_test.go index 0f264bfa5c..f098647d12 100644 --- a/internal/app/organisation_store_test.go +++ b/internal/app/organisation_store_test.go @@ -17,8 +17,8 @@ func TestOrganisationStoreCreate(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). Create(ctx, &actor.Organisation{ - PK: "ORGANISATION#a-uuid", - SK: "ORGANISATION#a-uuid", + PK: dynamo.OrganisationKey("a-uuid"), + SK: dynamo.OrganisationKey("a-uuid"), ID: "a-uuid", CreatedAt: testNow, Name: "A name", @@ -30,8 +30,8 @@ func TestOrganisationStoreCreate(t *testing.T) { organisation, err := organisationStore.Create(ctx, &actor.Member{OrganisationID: "a-uuid"}, "A name") assert.Nil(t, err) assert.Equal(t, &actor.Organisation{ - PK: "ORGANISATION#a-uuid", - SK: "ORGANISATION#a-uuid", + PK: dynamo.OrganisationKey("a-uuid"), + SK: dynamo.OrganisationKey("a-uuid"), ID: "a-uuid", CreatedAt: testNow, Name: "A name", @@ -74,12 +74,12 @@ func TestOrganisationStoreGet(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: "session-id"}) organisation := &actor.Organisation{Name: "A name"} - member := actor.Member{PK: "ORGANISATION#a-uuid"} + member := actor.Member{PK: dynamo.OrganisationKey("a-uuid")} dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectOneBySK(ctx, "MEMBER#session-id", member, nil) + ExpectOneBySK(ctx, dynamo.MemberKey("session-id"), member, nil) dynamoClient. - ExpectOne(ctx, "ORGANISATION#a-uuid", "ORGANISATION#a-uuid", organisation, nil) + ExpectOne(ctx, dynamo.OrganisationKey("a-uuid"), dynamo.OrganisationKey("a-uuid"), organisation, nil) organisationStore := &organisationStore{dynamoClient: dynamoClient, now: testNowFn, uuidString: func() string { return "a-uuid" }} @@ -92,12 +92,12 @@ func TestOrganisationStoreGetWhenOrganisationDeleted(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: "session-id"}) organisation := &actor.Organisation{Name: "A name", DeletedAt: testNow} - member := actor.Member{PK: "ORGANISATION#a-uuid"} + member := actor.Member{PK: dynamo.OrganisationKey("a-uuid")} dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectOneBySK(ctx, "MEMBER#session-id", member, nil) + ExpectOneBySK(ctx, dynamo.MemberKey("session-id"), member, nil) dynamoClient. - ExpectOne(ctx, "ORGANISATION#a-uuid", "ORGANISATION#a-uuid", organisation, nil) + ExpectOne(ctx, dynamo.OrganisationKey("a-uuid"), dynamo.OrganisationKey("a-uuid"), organisation, nil) organisationStore := &organisationStore{dynamoClient: dynamoClient, now: testNowFn, uuidString: func() string { return "a-uuid" }} @@ -137,17 +137,17 @@ func TestOrganisationStoreGetWhenErrors(t *testing.T) { } ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{SessionID: "session-id"}) - member := actor.Member{PK: "ORGANISATION#a-uuid"} + member := actor.Member{PK: dynamo.OrganisationKey("a-uuid")} for name, tc := range testcases { t.Run(name, func(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectOneBySK(ctx, "MEMBER#session-id", member, tc.oneBySKError) + ExpectOneBySK(ctx, dynamo.MemberKey("session-id"), member, tc.oneBySKError) if tc.oneError != nil { dynamoClient. - ExpectOne(ctx, "ORGANISATION#a-uuid", "ORGANISATION#a-uuid", nil, tc.oneError) + ExpectOne(ctx, dynamo.OrganisationKey("a-uuid"), dynamo.OrganisationKey("a-uuid"), nil, tc.oneError) } organisationStore := &organisationStore{dynamoClient: dynamoClient, now: testNowFn, uuidString: func() string { return "a-uuid" }} @@ -163,7 +163,7 @@ func TestOrganisationStorePut(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). - Put(ctx, &actor.Organisation{PK: "ORGANISATION#123", SK: "ORGANISATION#456", Name: "Hey", UpdatedAt: testNow}). + Put(ctx, &actor.Organisation{PK: dynamo.OrganisationKey("123"), SK: dynamo.OrganisationKey("456"), Name: "Hey", UpdatedAt: testNow}). Return(expectedError) store := &organisationStore{ @@ -171,15 +171,15 @@ func TestOrganisationStorePut(t *testing.T) { now: testNowFn, } - err := store.Put(ctx, &actor.Organisation{PK: "ORGANISATION#123", SK: "ORGANISATION#456", Name: "Hey"}) + err := store.Put(ctx, &actor.Organisation{PK: dynamo.OrganisationKey("123"), SK: dynamo.OrganisationKey("456"), Name: "Hey"}) assert.Equal(t, expectedError, err) } func TestOrganisationStoreCreateLPA(t *testing.T) { ctx := page.ContextWithSessionData(context.Background(), &page.SessionData{OrganisationID: "an-id"}) expectedDonor := &actor.DonorProvidedDetails{ - PK: "LPA#a-uuid", - SK: "ORGANISATION#an-id", + PK: dynamo.LpaKey("a-uuid"), + SK: dynamo.LpaOwnerKey(dynamo.OrganisationKey("an-id")), LpaID: "a-uuid", CreatedAt: testNow, Version: 1, diff --git a/internal/app/share_code_store.go b/internal/app/share_code_store.go index 5a82270328..089f2c14ae 100644 --- a/internal/app/share_code_store.go +++ b/internal/app/share_code_store.go @@ -2,6 +2,7 @@ package app import ( "context" + "fmt" "time" "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" @@ -10,11 +11,11 @@ import ( ) type ShareCodeStoreDynamoClient interface { - One(ctx context.Context, pk, sk string, v interface{}) error - OneByPK(ctx context.Context, pk string, v interface{}) error - OneBySK(ctx context.Context, sk string, v interface{}) error + One(ctx context.Context, pk dynamo.PK, sk dynamo.SK, v interface{}) error + OneByPK(ctx context.Context, pk dynamo.PK, v interface{}) error + OneBySK(ctx context.Context, sk dynamo.SK, v interface{}) error Put(ctx context.Context, v interface{}) error - DeleteOne(ctx context.Context, pk, sk string) error + DeleteOne(ctx context.Context, pk dynamo.PK, sk dynamo.SK) error } type shareCodeStore struct { @@ -29,7 +30,7 @@ func NewShareCodeStore(dynamoClient ShareCodeStoreDynamoClient) *shareCodeStore func (s *shareCodeStore) Get(ctx context.Context, actorType actor.Type, shareCode string) (actor.ShareCodeData, error) { var data actor.ShareCodeData - pk, err := dynamo.ShareCodeKey(actorType, shareCode) + pk, err := shareCodeKey(actorType, shareCode) if err != nil { return data, err } @@ -53,20 +54,20 @@ func (s *shareCodeStore) Linked(ctx context.Context, data actor.ShareCodeData, e } func (s *shareCodeStore) Put(ctx context.Context, actorType actor.Type, shareCode string, data actor.ShareCodeData) error { - pk, err := dynamo.ShareCodeKey(actorType, shareCode) + pk, err := shareCodeKey(actorType, shareCode) if err != nil { return err } data.PK = pk - data.SK = dynamo.MetadataKey(shareCode) + data.SK = dynamo.ShareSortKey(dynamo.MetadataKey(shareCode)) return s.dynamoClient.Put(ctx, data) } func (s *shareCodeStore) PutDonor(ctx context.Context, shareCode string, data actor.ShareCodeData) error { - data.PK = dynamo.DonorShareKey(shareCode) - data.SK = dynamo.DonorInviteKey(data.SessionID, data.LpaID) + data.PK = dynamo.ShareKey(dynamo.DonorShareKey(shareCode)) + data.SK = dynamo.ShareSortKey(dynamo.DonorInviteKey(data.SessionID, data.LpaID)) data.UpdatedAt = s.now() return s.dynamoClient.Put(ctx, data) @@ -89,3 +90,18 @@ func (s *shareCodeStore) GetDonor(ctx context.Context) (actor.ShareCodeData, err func (s *shareCodeStore) Delete(ctx context.Context, shareCode actor.ShareCodeData) error { return s.dynamoClient.DeleteOne(ctx, shareCode.PK, shareCode.SK) } + +func shareCodeKey(actorType actor.Type, shareCode string) (pk dynamo.ShareKeyType, err error) { + switch actorType { + case actor.TypeDonor: + return dynamo.ShareKey(dynamo.DonorShareKey(shareCode)), nil + // As attorneys and replacement attorneys share the same landing page we can't + // differentiate between them + case actor.TypeAttorney, actor.TypeReplacementAttorney, actor.TypeTrustCorporation, actor.TypeReplacementTrustCorporation: + return dynamo.ShareKey(dynamo.AttorneyShareKey(shareCode)), nil + case actor.TypeCertificateProvider: + return dynamo.ShareKey(dynamo.CertificateProviderShareKey(shareCode)), nil + default: + return dynamo.ShareKey(nil), fmt.Errorf("cannot have share code for actorType=%v", actorType) + } +} diff --git a/internal/app/share_code_store_test.go b/internal/app/share_code_store_test.go index d7dd1f9ecd..952539248d 100644 --- a/internal/app/share_code_store_test.go +++ b/internal/app/share_code_store_test.go @@ -15,19 +15,19 @@ import ( func TestShareCodeStoreGet(t *testing.T) { testcases := map[string]struct { t actor.Type - pk string + pk dynamo.ShareKeyType }{ "attorney": { t: actor.TypeAttorney, - pk: "ATTORNEYSHARE#123", + pk: dynamo.ShareKey(dynamo.AttorneyShareKey("123")), }, "replacement attorney": { t: actor.TypeReplacementAttorney, - pk: "ATTORNEYSHARE#123", + pk: dynamo.ShareKey(dynamo.AttorneyShareKey("123")), }, "certificate provider": { t: actor.TypeCertificateProvider, - pk: "CERTIFICATEPROVIDERSHARE#123", + pk: dynamo.ShareKey(dynamo.CertificateProviderShareKey("123")), }, } @@ -55,7 +55,7 @@ func TestShareCodeStoreGetWhenLinked(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectOneByPK(ctx, "DONORSHARE#123", + ExpectOneByPK(ctx, dynamo.ShareKey(dynamo.DonorShareKey("123")), actor.ShareCodeData{LpaLinkedAt: time.Now()}, nil) shareCodeStore := &shareCodeStore{dynamoClient: dynamoClient} @@ -79,7 +79,7 @@ func TestShareCodeStoreGetOnError(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectOneByPK(ctx, "ATTORNEYSHARE#123", + ExpectOneByPK(ctx, dynamo.ShareKey(dynamo.AttorneyShareKey("123")), data, expectedError) shareCodeStore := &shareCodeStore{dynamoClient: dynamoClient} @@ -108,26 +108,26 @@ func TestShareCodeStoreLinked(t *testing.T) { func TestShareCodeStorePut(t *testing.T) { testcases := map[string]struct { actor actor.Type - pk string + pk dynamo.ShareKeyType }{ "attorney": { actor: actor.TypeAttorney, - pk: "ATTORNEYSHARE#123", + pk: dynamo.ShareKey(dynamo.AttorneyShareKey("123")), }, "replacement attorney": { actor: actor.TypeReplacementAttorney, - pk: "ATTORNEYSHARE#123", + pk: dynamo.ShareKey(dynamo.AttorneyShareKey("123")), }, "certificate provider": { actor: actor.TypeCertificateProvider, - pk: "CERTIFICATEPROVIDERSHARE#123", + pk: dynamo.ShareKey(dynamo.CertificateProviderShareKey("123")), }, } for name, tc := range testcases { t.Run(name, func(t *testing.T) { ctx := context.Background() - data := actor.ShareCodeData{PK: tc.pk, SK: "#METADATA#123", LpaID: "lpa-id"} + data := actor.ShareCodeData{PK: tc.pk, SK: dynamo.ShareSortKey(dynamo.MetadataKey("123")), LpaID: "lpa-id"} dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). @@ -181,7 +181,7 @@ func TestShareCodeStoreGetDonor(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient. - ExpectOneBySK(ctx, "DONORINVITE#org-id#lpa-id", + ExpectOneBySK(ctx, dynamo.DonorInviteKey("org-id", "lpa-id"), data, expectedError) shareCodeStore := &shareCodeStore{dynamoClient: dynamoClient} @@ -205,8 +205,8 @@ func TestShareCodeStorePutDonor(t *testing.T) { dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). Put(ctx, actor.ShareCodeData{ - PK: "DONORSHARE#123", - SK: "DONORINVITE#org-id#lpa-id", + PK: dynamo.ShareKey(dynamo.DonorShareKey("123")), + SK: dynamo.ShareSortKey(dynamo.DonorInviteKey("org-id", "lpa-id")), SessionID: "org-id", LpaID: "lpa-id", UpdatedAt: testNow, @@ -221,15 +221,17 @@ func TestShareCodeStorePutDonor(t *testing.T) { func TestShareCodeStoreDelete(t *testing.T) { ctx := context.Background() + pk := dynamo.ShareKey(dynamo.AttorneyShareKey("a-pk")) + sk := dynamo.ShareSortKey(dynamo.MetadataKey("a-sk")) dynamoClient := newMockDynamoClient(t) dynamoClient.EXPECT(). - DeleteOne(ctx, "a-pk", "a-sk"). + DeleteOne(ctx, pk, sk). Return(nil) shareCodeStore := &shareCodeStore{dynamoClient: dynamoClient} - err := shareCodeStore.Delete(ctx, actor.ShareCodeData{LpaID: "123", PK: "a-pk", SK: "a-sk"}) + err := shareCodeStore.Delete(ctx, actor.ShareCodeData{LpaID: "123", PK: pk, SK: sk}) assert.Nil(t, err) } @@ -246,3 +248,27 @@ func TestShareCodeStoreDeleteOnError(t *testing.T) { err := shareCodeStore.Delete(ctx, actor.ShareCodeData{}) assert.Equal(t, expectedError, err) } + +func TestShareCodeKey(t *testing.T) { + testcases := map[actor.Type]dynamo.PK{ + actor.TypeDonor: dynamo.ShareKey(dynamo.DonorShareKey("S")), + actor.TypeAttorney: dynamo.ShareKey(dynamo.AttorneyShareKey("S")), + actor.TypeReplacementAttorney: dynamo.ShareKey(dynamo.AttorneyShareKey("S")), + actor.TypeTrustCorporation: dynamo.ShareKey(dynamo.AttorneyShareKey("S")), + actor.TypeReplacementTrustCorporation: dynamo.ShareKey(dynamo.AttorneyShareKey("S")), + actor.TypeCertificateProvider: dynamo.ShareKey(dynamo.CertificateProviderShareKey("S")), + } + + for actorType, prefix := range testcases { + t.Run(actorType.String(), func(t *testing.T) { + pk, err := shareCodeKey(actorType, "S") + assert.Nil(t, err) + assert.Equal(t, prefix, pk) + }) + } +} + +func TestShareCodeKeyWhenUnknownType(t *testing.T) { + _, err := shareCodeKey(actor.TypeAuthorisedSignatory, "S") + assert.NotNil(t, err) +} diff --git a/internal/app/uid_store.go b/internal/app/uid_store.go index a04bc60d3f..d6bc8e7adf 100644 --- a/internal/app/uid_store.go +++ b/internal/app/uid_store.go @@ -13,7 +13,7 @@ import ( ) type DynamoUpdateClient interface { - UpdateReturn(ctx context.Context, pk, sk string, values map[string]types.AttributeValue, expression string) (map[string]types.AttributeValue, error) + UpdateReturn(ctx context.Context, pk dynamo.PK, sk dynamo.SK, values map[string]types.AttributeValue, expression string) (map[string]types.AttributeValue, error) } type SearchClient interface { @@ -39,7 +39,7 @@ func (s *uidStore) Set(ctx context.Context, lpaID, sessionID, organisationID, ui return err } - sk := dynamo.DonorKey(sessionID) + var sk dynamo.SK = dynamo.DonorKey(sessionID) if organisationID != "" { sk = dynamo.OrganisationKey(organisationID) } @@ -56,8 +56,8 @@ func (s *uidStore) Set(ctx context.Context, lpaID, sessionID, organisationID, ui } if err := s.searchClient.Index(ctx, search.Lpa{ - PK: dynamo.LpaKey(lpaID), - SK: sk, + PK: dynamo.LpaKey(lpaID).PK(), + SK: sk.SK(), DonorFullName: donor.Donor.FullName(), }); err != nil { return fmt.Errorf("uidStore index failed: %w", err) diff --git a/internal/app/uid_store_test.go b/internal/app/uid_store_test.go index d75ddf624b..22f0ce9dbd 100644 --- a/internal/app/uid_store_test.go +++ b/internal/app/uid_store_test.go @@ -5,22 +5,23 @@ import ( "github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue" "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" + "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" "github.com/ministryofjustice/opg-modernising-lpa/internal/search" "github.com/stretchr/testify/assert" - mock "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/mock" ) func TestUidStoreSet(t *testing.T) { testcases := map[string]struct { organisationID string - sk string + sk dynamo.SK }{ "donor": { - sk: "#DONOR#session-id", + sk: dynamo.DonorKey("session-id"), }, "organisation": { organisationID: "org-id", - sk: "ORGANISATION#org-id", + sk: dynamo.OrganisationKey("org-id"), }, } @@ -40,15 +41,15 @@ func TestUidStoreSet(t *testing.T) { dynamoClient := newMockDynamoUpdateClient(t) dynamoClient.EXPECT(). - UpdateReturn(ctx, "LPA#lpa-id", tc.sk, values, + UpdateReturn(ctx, dynamo.LpaKey("lpa-id"), tc.sk, values, "set LpaUID = :uid, UpdatedAt = :now"). Return(returnValues, nil) searchClient := newMockSearchClient(t) searchClient.EXPECT(). Index(ctx, search.Lpa{ - PK: "LPA#lpa-id", - SK: tc.sk, + PK: dynamo.LpaKey("lpa-id").PK(), + SK: tc.sk.SK(), DonorFullName: "x y", }). Return(nil) @@ -68,7 +69,7 @@ func TestUidStoreSetWhenDynamoClientError(t *testing.T) { dynamoClient := newMockDynamoUpdateClient(t) dynamoClient.EXPECT(). - UpdateReturn(ctx, "LPA#lpa-id", "#DONOR#session-id", values, + UpdateReturn(ctx, dynamo.LpaKey("lpa-id"), dynamo.DonorKey("session-id"), values, "set LpaUID = :uid, UpdatedAt = :now"). Return(nil, expectedError) diff --git a/internal/dynamo/client.go b/internal/dynamo/client.go index 92511bffc1..0acf1e9934 100644 --- a/internal/dynamo/client.go +++ b/internal/dynamo/client.go @@ -59,12 +59,12 @@ func NewClient(cfg aws.Config, tableName string) (*Client, error) { return &Client{table: tableName, svc: dynamodb.NewFromConfig(cfg)}, nil } -func (c *Client) One(ctx context.Context, pk, sk string, v interface{}) error { +func (c *Client) One(ctx context.Context, pk PK, sk SK, v interface{}) error { result, err := c.svc.GetItem(ctx, &dynamodb.GetItemInput{ TableName: aws.String(c.table), Key: map[string]types.AttributeValue{ - "PK": &types.AttributeValueMemberS{Value: pk}, - "SK": &types.AttributeValueMemberS{Value: sk}, + "PK": &types.AttributeValueMemberS{Value: pk.PK()}, + "SK": &types.AttributeValueMemberS{Value: sk.SK()}, }, }) if err != nil { @@ -99,13 +99,13 @@ func (c *Client) OneByUID(ctx context.Context, uid string, v interface{}) error return attributevalue.UnmarshalMap(response.Items[0], v) } -func (c *Client) AllBySK(ctx context.Context, sk string, v interface{}) error { +func (c *Client) AllBySK(ctx context.Context, sk SK, v interface{}) error { response, err := c.svc.Query(ctx, &dynamodb.QueryInput{ TableName: aws.String(c.table), IndexName: aws.String(skUpdatedAtIndex), ExpressionAttributeNames: map[string]string{"#SK": "SK"}, ExpressionAttributeValues: map[string]types.AttributeValue{ - ":SK": &types.AttributeValueMemberS{Value: sk}, + ":SK": &types.AttributeValueMemberS{Value: sk.SK()}, }, KeyConditionExpression: aws.String("#SK = :SK"), }) @@ -116,13 +116,13 @@ func (c *Client) AllBySK(ctx context.Context, sk string, v interface{}) error { return attributevalue.UnmarshalListOfMaps(response.Items, v) } -func (c *Client) OneBySK(ctx context.Context, sk string, v interface{}) error { +func (c *Client) OneBySK(ctx context.Context, sk SK, v interface{}) error { response, err := c.svc.Query(ctx, &dynamodb.QueryInput{ TableName: aws.String(c.table), IndexName: aws.String(skUpdatedAtIndex), ExpressionAttributeNames: map[string]string{"#SK": "SK"}, ExpressionAttributeValues: map[string]types.AttributeValue{ - ":SK": &types.AttributeValueMemberS{Value: sk}, + ":SK": &types.AttributeValueMemberS{Value: sk.SK()}, }, KeyConditionExpression: aws.String("#SK = :SK"), }) @@ -141,13 +141,13 @@ func (c *Client) OneBySK(ctx context.Context, sk string, v interface{}) error { return attributevalue.UnmarshalMap(response.Items[0], v) } -func (c *Client) LatestForActor(ctx context.Context, sk string, v interface{}) error { +func (c *Client) LatestForActor(ctx context.Context, sk SK, v interface{}) error { response, err := c.svc.Query(ctx, &dynamodb.QueryInput{ TableName: aws.String(c.table), IndexName: aws.String(skUpdatedAtIndex), ExpressionAttributeNames: map[string]string{"#SK": "SK", "#UpdatedAt": "UpdatedAt"}, ExpressionAttributeValues: map[string]types.AttributeValue{ - ":SK": &types.AttributeValueMemberS{Value: sk}, + ":SK": &types.AttributeValueMemberS{Value: sk.SK()}, // Specifying the condition UpdatedAt>2 filters out zero-value timestamps ":UpdatedAt": &types.AttributeValueMemberS{Value: "2"}, }, @@ -167,17 +167,12 @@ func (c *Client) LatestForActor(ctx context.Context, sk string, v interface{}) e return attributevalue.UnmarshalMap(response.Items[0], v) } -type Key struct { - PK string - SK string -} - -func (c *Client) AllKeysByPK(ctx context.Context, pk string) ([]Key, error) { +func (c *Client) AllKeysByPK(ctx context.Context, pk PK) ([]Keys, error) { response, err := c.svc.Query(ctx, &dynamodb.QueryInput{ TableName: aws.String(c.table), ExpressionAttributeNames: map[string]string{"#PK": "PK"}, ExpressionAttributeValues: map[string]types.AttributeValue{ - ":PK": &types.AttributeValueMemberS{Value: pk}, + ":PK": &types.AttributeValueMemberS{Value: pk.PK()}, }, KeyConditionExpression: aws.String("#PK = :PK"), ProjectionExpression: aws.String("PK, SK"), @@ -187,18 +182,18 @@ func (c *Client) AllKeysByPK(ctx context.Context, pk string) ([]Key, error) { return nil, err } - var keys []Key + var keys []Keys err = attributevalue.UnmarshalListOfMaps(response.Items, &keys) return keys, err } -func (c *Client) AllByKeys(ctx context.Context, keys []Key) ([]map[string]types.AttributeValue, error) { +func (c *Client) AllByKeys(ctx context.Context, keys []Keys) ([]map[string]types.AttributeValue, error) { var keyAttrs []map[string]types.AttributeValue for _, key := range keys { keyAttrs = append(keyAttrs, map[string]types.AttributeValue{ - "PK": &types.AttributeValueMemberS{Value: key.PK}, - "SK": &types.AttributeValueMemberS{Value: key.SK}, + "PK": &types.AttributeValueMemberS{Value: key.PK.PK()}, + "SK": &types.AttributeValueMemberS{Value: key.SK.SK()}, }) } @@ -216,12 +211,12 @@ func (c *Client) AllByKeys(ctx context.Context, keys []Key) ([]map[string]types. return result.Responses[c.table], nil } -func (c *Client) OneByPK(ctx context.Context, pk string, v interface{}) error { +func (c *Client) OneByPK(ctx context.Context, pk PK, v interface{}) error { response, err := c.svc.Query(ctx, &dynamodb.QueryInput{ TableName: aws.String(c.table), ExpressionAttributeNames: map[string]string{"#PK": "PK"}, ExpressionAttributeValues: map[string]types.AttributeValue{ - ":PK": &types.AttributeValueMemberS{Value: pk}, + ":PK": &types.AttributeValueMemberS{Value: pk.PK()}, }, KeyConditionExpression: aws.String("#PK = :PK"), }) @@ -241,13 +236,13 @@ func (c *Client) OneByPK(ctx context.Context, pk string, v interface{}) error { return attributevalue.UnmarshalMap(response.Items[0], v) } -func (c *Client) OneByPartialSK(ctx context.Context, pk, partialSK string, v interface{}) error { +func (c *Client) OneByPartialSK(ctx context.Context, pk PK, partialSK SK, v interface{}) error { response, err := c.svc.Query(ctx, &dynamodb.QueryInput{ TableName: aws.String(c.table), ExpressionAttributeNames: map[string]string{"#PK": "PK", "#SK": "SK"}, ExpressionAttributeValues: map[string]types.AttributeValue{ - ":PK": &types.AttributeValueMemberS{Value: pk}, - ":SK": &types.AttributeValueMemberS{Value: partialSK}, + ":PK": &types.AttributeValueMemberS{Value: pk.PK()}, + ":SK": &types.AttributeValueMemberS{Value: partialSK.SK()}, }, KeyConditionExpression: aws.String("#PK = :PK and begins_with(#SK, :SK)"), }) @@ -267,13 +262,13 @@ func (c *Client) OneByPartialSK(ctx context.Context, pk, partialSK string, v int return attributevalue.UnmarshalMap(response.Items[0], v) } -func (c *Client) AllByPartialSK(ctx context.Context, pk, partialSk string, v interface{}) error { +func (c *Client) AllByPartialSK(ctx context.Context, pk PK, partialSk SK, v interface{}) error { response, err := c.svc.Query(ctx, &dynamodb.QueryInput{ TableName: aws.String(c.table), ExpressionAttributeNames: map[string]string{"#PK": "PK", "#SK": "SK"}, ExpressionAttributeValues: map[string]types.AttributeValue{ - ":PK": &types.AttributeValueMemberS{Value: pk}, - ":SK": &types.AttributeValueMemberS{Value: partialSk}, + ":PK": &types.AttributeValueMemberS{Value: pk.PK()}, + ":SK": &types.AttributeValueMemberS{Value: partialSk.SK()}, }, KeyConditionExpression: aws.String("#PK = :PK and begins_with(#SK, :SK)"), }) @@ -347,7 +342,7 @@ func (c *Client) Create(ctx context.Context, v interface{}) error { return err } -func (c *Client) DeleteKeys(ctx context.Context, keys []Key) error { +func (c *Client) DeleteKeys(ctx context.Context, keys []Keys) error { items := make([]types.TransactWriteItem, len(keys)) for i, key := range keys { @@ -355,8 +350,8 @@ func (c *Client) DeleteKeys(ctx context.Context, keys []Key) error { Delete: &types.Delete{ TableName: aws.String(c.table), Key: map[string]types.AttributeValue{ - "PK": &types.AttributeValueMemberS{Value: key.PK}, - "SK": &types.AttributeValueMemberS{Value: key.SK}, + "PK": &types.AttributeValueMemberS{Value: key.PK.PK()}, + "SK": &types.AttributeValueMemberS{Value: key.SK.SK()}, }, }, } @@ -369,24 +364,24 @@ func (c *Client) DeleteKeys(ctx context.Context, keys []Key) error { return err } -func (c *Client) DeleteOne(ctx context.Context, pk, sk string) error { +func (c *Client) DeleteOne(ctx context.Context, pk PK, sk SK) error { _, err := c.svc.DeleteItem(ctx, &dynamodb.DeleteItemInput{ TableName: aws.String(c.table), Key: map[string]types.AttributeValue{ - "PK": &types.AttributeValueMemberS{Value: pk}, - "SK": &types.AttributeValueMemberS{Value: sk}, + "PK": &types.AttributeValueMemberS{Value: pk.PK()}, + "SK": &types.AttributeValueMemberS{Value: sk.SK()}, }, }) return err } -func (c *Client) Update(ctx context.Context, pk, sk string, values map[string]types.AttributeValue, expression string) error { +func (c *Client) Update(ctx context.Context, pk PK, sk SK, values map[string]types.AttributeValue, expression string) error { _, err := c.svc.UpdateItem(ctx, &dynamodb.UpdateItemInput{ TableName: aws.String(c.table), Key: map[string]types.AttributeValue{ - "PK": &types.AttributeValueMemberS{Value: pk}, - "SK": &types.AttributeValueMemberS{Value: sk}, + "PK": &types.AttributeValueMemberS{Value: pk.PK()}, + "SK": &types.AttributeValueMemberS{Value: sk.SK()}, }, ExpressionAttributeValues: values, UpdateExpression: aws.String(expression), @@ -395,12 +390,12 @@ func (c *Client) Update(ctx context.Context, pk, sk string, values map[string]ty return err } -func (c *Client) UpdateReturn(ctx context.Context, pk, sk string, values map[string]types.AttributeValue, expression string) (map[string]types.AttributeValue, error) { +func (c *Client) UpdateReturn(ctx context.Context, pk PK, sk SK, values map[string]types.AttributeValue, expression string) (map[string]types.AttributeValue, error) { resp, err := c.svc.UpdateItem(ctx, &dynamodb.UpdateItemInput{ TableName: aws.String(c.table), Key: map[string]types.AttributeValue{ - "PK": &types.AttributeValueMemberS{Value: pk}, - "SK": &types.AttributeValueMemberS{Value: sk}, + "PK": &types.AttributeValueMemberS{Value: pk.PK()}, + "SK": &types.AttributeValueMemberS{Value: sk.SK()}, }, ExpressionAttributeValues: values, UpdateExpression: aws.String(expression), diff --git a/internal/dynamo/client_test.go b/internal/dynamo/client_test.go index fe49978698..eb0e4b872b 100644 --- a/internal/dynamo/client_test.go +++ b/internal/dynamo/client_test.go @@ -11,11 +11,18 @@ import ( "github.com/aws/aws-sdk-go-v2/service/dynamodb" "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" "github.com/aws/smithy-go" - "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" "github.com/stretchr/testify/assert" mock "github.com/stretchr/testify/mock" ) +type testPK string + +func (k testPK) PK() string { return string(k) } + +type testSK string + +func (k testSK) SK() string { return string(k) } + var expectedError = errors.New("err") func TestOne(t *testing.T) { @@ -37,7 +44,7 @@ func TestOne(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var actual map[string]string - err := c.One(ctx, "a-pk", "a-sk", &actual) + err := c.One(ctx, testPK("a-pk"), testSK("a-sk"), &actual) assert.Nil(t, err) assert.Equal(t, expected, actual) } @@ -58,7 +65,7 @@ func TestOneWhenError(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v string - err := c.One(ctx, "a-pk", "a-sk", &v) + err := c.One(ctx, testPK("a-pk"), testSK("a-sk"), &v) assert.Equal(t, expectedError, err) assert.Equal(t, "", v) } @@ -79,7 +86,7 @@ func TestOneWhenNotFound(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v string - err := c.One(ctx, "a-pk", "a-sk", &v) + err := c.One(ctx, testPK("a-pk"), testSK("a-sk"), &v) assert.Equal(t, NotFoundError{}, err) assert.Equal(t, "", v) } @@ -105,11 +112,11 @@ func TestOneByUID(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} - var v actor.DonorProvidedDetails + var v map[string]any err := c.OneByUID(ctx, "M-1111-2222-3333", &v) assert.Nil(t, err) - assert.Equal(t, actor.DonorProvidedDetails{PK: "LPA#123", LpaUID: "M-1111-2222-3333"}, v) + assert.Equal(t, map[string]any{"PK": "LPA#123", "LpaUID": "M-1111-2222-3333"}, v) } func TestOneByUIDWhenQueryError(t *testing.T) { @@ -213,7 +220,7 @@ func TestOneByPK(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v map[string]string - err := c.OneByPK(ctx, "a-pk", &v) + err := c.OneByPK(ctx, testPK("a-pk"), &v) assert.Nil(t, err) assert.Equal(t, expected, v) } @@ -229,7 +236,7 @@ func TestOneByPKOnQueryError(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v map[string]string - err := c.OneByPK(ctx, "a-pk", &v) + err := c.OneByPK(ctx, testPK("a-pk"), &v) assert.Equal(t, expectedError, err) } @@ -244,7 +251,7 @@ func TestOneByPKWhenNotFound(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v map[string]string - err := c.OneByPK(ctx, "a-pk", &v) + err := c.OneByPK(ctx, testPK("a-pk"), &v) assert.Equal(t, NotFoundError{}, err) } @@ -261,7 +268,7 @@ func TestOneByPKWhenMultipleResults(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v map[string]string - err := c.OneByPK(ctx, "a-pk", &v) + err := c.OneByPK(ctx, testPK("a-pk"), &v) assert.Equal(t, MultipleResultsError{}, err) } @@ -286,7 +293,7 @@ func TestOneByPartialSK(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v map[string]string - err := c.OneByPartialSK(ctx, "a-pk", "a-partial-sk", &v) + err := c.OneByPartialSK(ctx, testPK("a-pk"), testSK("a-partial-sk"), &v) assert.Nil(t, err) assert.Equal(t, expected, v) } @@ -302,7 +309,7 @@ func TestOneByPartialSKOnQueryError(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v map[string]string - err := c.OneByPartialSK(ctx, "a-pk", "a-partial-sk", &v) + err := c.OneByPartialSK(ctx, testPK("a-pk"), testSK("a-partial-sk"), &v) assert.Equal(t, expectedError, err) } @@ -317,7 +324,7 @@ func TestOneByPartialSKWhenNotFound(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v map[string]string - err := c.OneByPartialSK(ctx, "a-pk", "a-partial-sk", &v) + err := c.OneByPartialSK(ctx, testPK("a-pk"), testSK("a-partial-sk"), &v) assert.Equal(t, NotFoundError{}, err) } @@ -334,7 +341,7 @@ func TestOneByPartialSKWhenMultipleResults(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v map[string]string - err := c.OneByPartialSK(ctx, "a-pk", "a-partial-sk", &v) + err := c.OneByPartialSK(ctx, testPK("a-pk"), testSK("a-partial-sk"), &v) assert.Equal(t, MultipleResultsError{}, err) } @@ -360,7 +367,7 @@ func TestAllByPartialSK(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v []map[string]string - err := c.AllByPartialSK(ctx, "a-pk", "a-partial-sk", &v) + err := c.AllByPartialSK(ctx, testPK("a-pk"), testSK("a-partial-sk"), &v) assert.Nil(t, err) assert.Equal(t, expected, v) } @@ -376,7 +383,7 @@ func TestAllByPartialSKOnQueryError(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v map[string]string - err := c.AllByPartialSK(ctx, "a-pk", "a-partial-sk", &v) + err := c.AllByPartialSK(ctx, testPK("a-pk"), testSK("a-partial-sk"), &v) assert.Equal(t, expectedError, err) } @@ -401,7 +408,7 @@ func TestAllForActor(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v []map[string]string - err := c.AllBySK(ctx, "a-partial-sk", &v) + err := c.AllBySK(ctx, testSK("a-partial-sk"), &v) assert.Nil(t, err) assert.Equal(t, []map[string]string{expected, expected}, v) } @@ -417,7 +424,7 @@ func TestAllForActorWhenNotFound(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v []string - err := c.AllBySK(ctx, "a-partial-sk", &v) + err := c.AllBySK(ctx, testSK("a-partial-sk"), &v) assert.Nil(t, err) assert.Empty(t, v) } @@ -433,7 +440,7 @@ func TestAllForActorOnQueryError(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v []string - err := c.AllBySK(ctx, "a-partial-sk", &v) + err := c.AllBySK(ctx, testSK("a-partial-sk"), &v) assert.Equal(t, expectedError, err) } @@ -461,7 +468,7 @@ func TestLatestForActor(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v map[string]string - err := c.LatestForActor(ctx, "a-partial-sk", &v) + err := c.LatestForActor(ctx, testSK("a-partial-sk"), &v) assert.Nil(t, err) assert.Equal(t, expected, v) } @@ -477,7 +484,7 @@ func TestLatestForActorWhenNotFound(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v interface{} - err := c.LatestForActor(ctx, "a-partial-sk", &v) + err := c.LatestForActor(ctx, testSK("a-partial-sk"), &v) assert.Nil(t, err) assert.Nil(t, v) } @@ -493,16 +500,16 @@ func TestLatestForActorOnQueryError(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v []string - err := c.LatestForActor(ctx, "a-partial-sk", &v) + err := c.LatestForActor(ctx, testSK("a-partial-sk"), &v) assert.Equal(t, expectedError, err) } func TestAllKeysByPK(t *testing.T) { ctx := context.Background() - keys := []Key{ - {PK: "pk", SK: "sk1"}, - {PK: "pk", SK: "sk2"}, + keys := []Keys{ + {PK: LpaKey("pk"), SK: OrganisationKey("sk1")}, + {PK: LpaKey("pk"), SK: DonorKey("sk2")}, } item1, _ := attributevalue.MarshalMap(keys[0]) @@ -514,7 +521,7 @@ func TestAllKeysByPK(t *testing.T) { TableName: aws.String("this"), ExpressionAttributeNames: map[string]string{"#PK": "PK"}, ExpressionAttributeValues: map[string]types.AttributeValue{ - ":PK": &types.AttributeValueMemberS{Value: "pk"}, + ":PK": &types.AttributeValueMemberS{Value: "LPA#pk"}, }, KeyConditionExpression: aws.String("#PK = :PK"), ProjectionExpression: aws.String("PK, SK"), @@ -523,7 +530,7 @@ func TestAllKeysByPK(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} - result, err := c.AllKeysByPK(ctx, "pk") + result, err := c.AllKeysByPK(ctx, LpaKey("pk")) assert.Nil(t, err) assert.Equal(t, keys, result) } @@ -538,7 +545,7 @@ func TestAllKeysByPKWhenError(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} - _, err := c.AllKeysByPK(ctx, "pk") + _, err := c.AllKeysByPK(ctx, testPK("pk")) assert.Equal(t, expectedError, err) } @@ -568,7 +575,7 @@ func TestAllByKeys(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} - v, err := c.AllByKeys(ctx, []Key{{PK: "pk", SK: "sk"}}) + v, err := c.AllByKeys(ctx, []Keys{{PK: testPK("pk"), SK: testSK("sk")}}) assert.Nil(t, err) assert.Equal(t, []map[string]types.AttributeValue{data}, v) } @@ -583,7 +590,7 @@ func TestAllByKeysWhenQueryErrors(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} - _, err := c.AllByKeys(ctx, []Key{{PK: "pk", SK: "sk"}}) + _, err := c.AllByKeys(ctx, []Keys{{PK: testPK("pk"), SK: testSK("sk")}}) assert.Equal(t, expectedError, err) } @@ -741,7 +748,28 @@ func TestDeleteKeys(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} - err := c.DeleteKeys(ctx, []Key{{PK: "pk", SK: "sk1"}, {PK: "pk", SK: "sk2"}}) + err := c.DeleteKeys(ctx, []Keys{{PK: testPK("pk"), SK: testSK("sk1")}, {PK: testPK("pk"), SK: testSK("sk2")}}) + assert.Equal(t, expectedError, err) +} + +func TestDeleteOne(t *testing.T) { + ctx := context.Background() + + dynamoDB := newMockDynamoDB(t) + dynamoDB.EXPECT(). + DeleteItem(ctx, &dynamodb.DeleteItemInput{ + TableName: aws.String("table-name"), + Key: map[string]types.AttributeValue{ + "PK": &types.AttributeValueMemberS{Value: "a-pk"}, + "SK": &types.AttributeValueMemberS{Value: "a-sk"}, + }, + }). + Return(nil, expectedError) + + c := &Client{table: "table-name", svc: dynamoDB} + + err := c.DeleteOne(ctx, testPK("a-pk"), testSK("a-sk")) + assert.Equal(t, expectedError, err) } @@ -763,7 +791,7 @@ func TestUpdate(t *testing.T) { c := &Client{table: "table-name", svc: dynamoDB} - err := c.Update(ctx, "a-pk", "a-sk", map[string]types.AttributeValue{"prop": &types.AttributeValueMemberS{Value: "val"}}, "some = expression") + err := c.Update(ctx, testPK("a-pk"), testSK("a-sk"), map[string]types.AttributeValue{"prop": &types.AttributeValueMemberS{Value: "val"}}, "some = expression") assert.Nil(t, err) } @@ -786,7 +814,7 @@ func TestUpdateOnServiceError(t *testing.T) { c := &Client{table: "table-name", svc: dynamoDB} - err := c.Update(ctx, "a-pk", "a-sk", map[string]types.AttributeValue{"Col": &types.AttributeValueMemberS{Value: "Val"}}, "some = expression") + err := c.Update(ctx, testPK("a-pk"), testSK("a-sk"), map[string]types.AttributeValue{"Col": &types.AttributeValueMemberS{Value: "Val"}}, "some = expression") assert.Equal(t, expectedError, err) } @@ -812,7 +840,7 @@ func TestUpdateReturn(t *testing.T) { c := &Client{table: "table-name", svc: dynamoDB} - result, err := c.UpdateReturn(ctx, "a-pk", "a-sk", map[string]types.AttributeValue{"prop": &types.AttributeValueMemberS{Value: "val"}}, "some = expression") + result, err := c.UpdateReturn(ctx, testPK("a-pk"), testSK("a-sk"), map[string]types.AttributeValue{"prop": &types.AttributeValueMemberS{Value: "val"}}, "some = expression") assert.Nil(t, err) assert.Equal(t, returned, result) } @@ -827,7 +855,7 @@ func TestUpdateReturnOnServiceError(t *testing.T) { c := &Client{table: "table-name", svc: dynamoDB} - _, err := c.UpdateReturn(ctx, "a-pk", "a-sk", map[string]types.AttributeValue{"Col": &types.AttributeValueMemberS{Value: "Val"}}, "some = expression") + _, err := c.UpdateReturn(ctx, testPK("a-pk"), testSK("a-sk"), map[string]types.AttributeValue{"Col": &types.AttributeValueMemberS{Value: "Val"}}, "some = expression") assert.Equal(t, expectedError, err) } @@ -886,7 +914,7 @@ func TestOneBySk(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v map[string]string - err := c.OneBySK(ctx, "sk", &v) + err := c.OneBySK(ctx, testSK("sk"), &v) assert.Nil(t, err) assert.Equal(t, expected, v) } @@ -920,7 +948,7 @@ func TestOneBySKWhenNotOneResult(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v map[string]string - err := c.OneBySK(ctx, "sk", &v) + err := c.OneBySK(ctx, testSK("sk"), &v) assert.Equal(t, tc.expectedError, err) }) @@ -941,7 +969,7 @@ func TestOneBySkWhenQueryError(t *testing.T) { c := &Client{table: "this", svc: dynamoDB} var v map[string]string - err := c.OneBySK(ctx, "sk", &v) + err := c.OneBySK(ctx, testSK("sk"), &v) assert.Equal(t, expectedError, err) } diff --git a/internal/dynamo/complex_keys.go b/internal/dynamo/complex_keys.go new file mode 100644 index 0000000000..d00efedd48 --- /dev/null +++ b/internal/dynamo/complex_keys.go @@ -0,0 +1,254 @@ +package dynamo + +import ( + "encoding/json" + "errors" + + "github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue" + "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" +) + +type Keys struct { + PK PK + SK SK +} + +func (k *Keys) read(pkStr, skStr string) error { + v, err := readKey(pkStr) + if err != nil { + return err + } + + var ok bool + k.PK, ok = v.(PK) + if !ok { + return errors.New("newKeys pk not pk") + } + + v, err = readKey(skStr) + if err != nil { + return err + } + + k.SK, ok = v.(SK) + if !ok { + return errors.New("newKeys sk not sk") + } + + return nil +} + +func (k *Keys) UnmarshalDynamoDBAttributeValue(av types.AttributeValue) error { + var skeys struct{ PK, SK string } + if err := attributevalue.Unmarshal(av, &skeys); err != nil { + return err + } + + return k.read(skeys.PK, skeys.SK) +} + +func (k *Keys) UnmarshalJSON(text []byte) error { + var skeys struct{ PK, SK string } + if err := json.Unmarshal(text, &skeys); err != nil { + return err + } + + return k.read(skeys.PK, skeys.SK) +} + +type LpaOwnerKeyType struct{ sk SK } + +// LpaOwnerKey is used as the SK (with LpaKey as PK) to allow both donors and +// organisations to "own" LPAs. +func LpaOwnerKey(sk interface { + SK + lpaOwner() +}) LpaOwnerKeyType { + return LpaOwnerKeyType{sk: sk} +} + +func (k LpaOwnerKeyType) MarshalText() ([]byte, error) { + if k.sk == nil { + return []byte(nil), nil + } + + return []byte(k.sk.SK()), nil +} + +func (k *LpaOwnerKeyType) UnmarshalText(text []byte) error { + if len(text) == 0 { + return nil + } + + v, err := readKey(string(text)) + if err != nil { + return err + } + + sk, ok := v.(interface { + SK + lpaOwner() + }) + if !ok { + return errors.New("invalid key") + } + + k.sk = sk + return err +} + +func (k LpaOwnerKeyType) MarshalDynamoDBAttributeValue() (types.AttributeValue, error) { + text, _ := k.MarshalText() + + return attributevalue.Marshal(string(text)) +} + +func (k *LpaOwnerKeyType) UnmarshalDynamoDBAttributeValue(av types.AttributeValue) error { + var s string + if err := attributevalue.Unmarshal(av, &s); err != nil { + return err + } + + return k.UnmarshalText([]byte(s)) +} + +func (k LpaOwnerKeyType) Equals(sk SK) bool { + if k.sk == nil || sk == nil { + return false + } + + return k.SK() == sk.SK() +} + +func (k LpaOwnerKeyType) SK() string { + if k.sk == nil { + return "" + } + + return k.sk.SK() +} + +func (k LpaOwnerKeyType) IsOrganisation() bool { + _, ok := k.sk.(OrganisationKeyType) + return ok +} + +type ShareKeyType struct{ pk PK } + +// ShareKey is used as the PK (with ShareSortKey as SK) for sharing an LPA with +// another actor. +func ShareKey(pk interface { + PK + share() +}) ShareKeyType { + return ShareKeyType{pk: pk} +} + +func (k ShareKeyType) MarshalText() ([]byte, error) { + if k.pk == nil { + return []byte(nil), nil + } + + return []byte(k.pk.PK()), nil +} + +func (k *ShareKeyType) UnmarshalText(text []byte) error { + if len(text) == 0 { + return nil + } + + v, err := readKey(string(text)) + if err != nil { + return err + } + + pk, ok := v.(interface { + PK + share() + }) + if !ok { + return errors.New("invalid key") + } + + k.pk = pk + return err +} + +func (k ShareKeyType) MarshalDynamoDBAttributeValue() (types.AttributeValue, error) { + text, _ := k.MarshalText() + + return attributevalue.Marshal(string(text)) +} + +func (k *ShareKeyType) UnmarshalDynamoDBAttributeValue(av types.AttributeValue) error { + var s string + if err := attributevalue.Unmarshal(av, &s); err != nil { + return err + } + + return k.UnmarshalText([]byte(s)) +} + +func (k ShareKeyType) PK() string { + return k.pk.PK() +} + +type ShareSortKeyType struct{ sk SK } + +// ShareSortKey is used as the SK (with ShareKey as the PK) for sharing an LPA +// with another actor. +func ShareSortKey(sk interface { + SK + shareSort() +}) ShareSortKeyType { + return ShareSortKeyType{sk: sk} +} + +func (k ShareSortKeyType) MarshalText() ([]byte, error) { + if k.sk == nil { + return []byte(nil), nil + } + + return []byte(k.sk.SK()), nil +} + +func (k *ShareSortKeyType) UnmarshalText(text []byte) error { + if len(text) == 0 { + return nil + } + + v, err := readKey(string(text)) + if err != nil { + return err + } + + sk, ok := v.(interface { + SK + shareSort() + }) + if !ok { + return errors.New("invalid key") + } + + k.sk = sk + return nil +} + +func (k ShareSortKeyType) MarshalDynamoDBAttributeValue() (types.AttributeValue, error) { + text, _ := k.MarshalText() + + return attributevalue.Marshal(string(text)) +} + +func (k *ShareSortKeyType) UnmarshalDynamoDBAttributeValue(av types.AttributeValue) error { + var s string + if err := attributevalue.Unmarshal(av, &s); err != nil { + return err + } + + return k.UnmarshalText([]byte(s)) +} + +func (k ShareSortKeyType) SK() string { + return k.sk.SK() +} diff --git a/internal/dynamo/complex_keys_test.go b/internal/dynamo/complex_keys_test.go new file mode 100644 index 0000000000..10a2a92e87 --- /dev/null +++ b/internal/dynamo/complex_keys_test.go @@ -0,0 +1,297 @@ +package dynamo + +import ( + "encoding/json" + "testing" + + "github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue" + "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" + "github.com/stretchr/testify/assert" +) + +func TestKeysJSON(t *testing.T) { + keys := Keys{PK: LpaKey("abc"), SK: DonorKey("123")} + + data, err := json.Marshal(keys) + assert.Nil(t, err) + assert.Equal(t, `{"PK":"LPA#abc","SK":"DONOR#123"}`, string(data)) + + var v Keys + err = json.Unmarshal(data, &v) + assert.Nil(t, err) + assert.Equal(t, keys, v) +} + +func TestKeysUnmarshalJSONWhenError(t *testing.T) { + var v Keys + err := json.Unmarshal([]byte(`hey`), &v) + assert.Error(t, err) +} + +func TestKeysAttributeValue(t *testing.T) { + keys := Keys{PK: LpaKey("abc"), SK: DonorKey("123")} + + data, err := attributevalue.Marshal(keys) + assert.Nil(t, err) + assert.Equal(t, &types.AttributeValueMemberM{ + Value: map[string]types.AttributeValue{ + "PK": &types.AttributeValueMemberS{Value: "LPA#abc"}, + "SK": &types.AttributeValueMemberS{Value: "DONOR#123"}, + }, + }, data) + + var v Keys + err = attributevalue.Unmarshal(data, &v) + assert.Nil(t, err) + assert.Equal(t, keys, v) +} + +func TestKeysUnmarshalAttributeValueWhenError(t *testing.T) { + var v Keys + err := attributevalue.Unmarshal(&types.AttributeValueMemberS{Value: "hey"}, &v) + assert.Error(t, err) +} + +func TestKeysWhenMalformed(t *testing.T) { + testcases := map[string]string{ + "empty": `{}`, + "malformed pk": `{"PK":"WHAT","SK":"DONOR#123"}`, + "bad pk": `{"PK":"ATTORNEY#123","SK":"DONOR#123"}`, + "malformed sk": `{"PK":"LPA#123","SK":"WHAT"}`, + "bad sk": `{"PK":"LPA#123","SK":"LPA#123"}`, + } + + for name, str := range testcases { + t.Run(name, func(t *testing.T) { + var v Keys + err := json.Unmarshal([]byte(str), &v) + assert.Error(t, err) + }) + } +} + +func TestLpaOwnerKey(t *testing.T) { + for str, key := range map[string]LpaOwnerKeyType{ + "DONOR#123": LpaOwnerKey(DonorKey("123")), + "ORGANISATION#123": LpaOwnerKey(OrganisationKey("123")), + } { + t.Run(str+"/SK", func(t *testing.T) { + assert.Equal(t, str, key.SK()) + }) + + t.Run(str+"/json", func(t *testing.T) { + data, err := json.Marshal(key) + assert.Nil(t, err) + assert.Equal(t, `"`+str+`"`, string(data)) + + var v LpaOwnerKeyType + err = json.Unmarshal(data, &v) + assert.Nil(t, err) + assert.Equal(t, key, v) + }) + + t.Run(str+"/attributevalue", func(t *testing.T) { + data, err := attributevalue.Marshal(key) + assert.Nil(t, err) + assert.Equal(t, &types.AttributeValueMemberS{Value: str}, data) + + var v LpaOwnerKeyType + err = attributevalue.Unmarshal(data, &v) + assert.Nil(t, err) + assert.Equal(t, key, v) + }) + } + + t.Run("Equals", func(t *testing.T) { + x := LpaOwnerKey(DonorKey("abc")) + y := LpaOwnerKey(DonorKey("def")) + z := LpaOwnerKey(OrganisationKey("abc")) + + assert.True(t, x.Equals(x)) + assert.True(t, x.Equals(DonorKey("abc"))) + assert.True(t, x.Equals(LpaOwnerKey(DonorKey("abc")))) + assert.True(t, z.Equals(z)) + + assert.False(t, x.Equals(y)) + assert.False(t, x.Equals(z)) + + assert.False(t, LpaOwnerKey(nil).Equals(nil)) + assert.False(t, LpaOwnerKey(DonorKey("")).Equals(LpaOwnerKey(nil))) + assert.False(t, LpaOwnerKey(nil).Equals(DonorKey(""))) + }) + + t.Run("IsOrganisation", func(t *testing.T) { + assert.True(t, LpaOwnerKey(OrganisationKey("")).IsOrganisation()) + assert.False(t, LpaOwnerKey(DonorKey("")).IsOrganisation()) + assert.False(t, LpaOwnerKey(nil).IsOrganisation()) + }) + + t.Run("malformed", func(t *testing.T) { + var v LpaOwnerKeyType + err := json.Unmarshal([]byte(`"WHAT"`), &v) + assert.Error(t, err) + }) + + t.Run("invalid", func(t *testing.T) { + var v LpaOwnerKeyType + err := json.Unmarshal([]byte(`"ATTORNEY#123"`), &v) + assert.Error(t, err) + }) + + t.Run("empty json", func(t *testing.T) { + var v LpaOwnerKeyType + err := json.Unmarshal([]byte(`""`), &v) + assert.Nil(t, err) + assert.Equal(t, LpaOwnerKeyType{}, v) + + data, err := json.Marshal(v) + assert.Nil(t, err) + assert.Equal(t, `""`, string(data)) + }) + + t.Run("empty attributevalue", func(t *testing.T) { + var v LpaOwnerKeyType + err := attributevalue.Unmarshal(&types.AttributeValueMemberS{Value: ""}, &v) + assert.Nil(t, err) + assert.Equal(t, LpaOwnerKeyType{}, v) + + av, err := attributevalue.Marshal(v) + assert.Nil(t, err) + assert.Equal(t, &types.AttributeValueMemberS{}, av) + }) +} + +func TestShareKey(t *testing.T) { + for str, key := range map[string]ShareKeyType{ + "DONORSHARE#123": ShareKey(DonorShareKey("123")), + "CERTIFICATEPROVIDERSHARE#123": ShareKey(CertificateProviderShareKey("123")), + "ATTORNEYSHARE#123": ShareKey(AttorneyShareKey("123")), + } { + t.Run(str+"/PK", func(t *testing.T) { + assert.Equal(t, str, key.PK()) + }) + + t.Run(str+"/json", func(t *testing.T) { + data, err := json.Marshal(key) + assert.Nil(t, err) + assert.Equal(t, `"`+str+`"`, string(data)) + + var v ShareKeyType + err = json.Unmarshal(data, &v) + assert.Nil(t, err) + assert.Equal(t, key, v) + }) + + t.Run(str+"/attributevalue", func(t *testing.T) { + data, err := attributevalue.Marshal(key) + assert.Nil(t, err) + assert.Equal(t, &types.AttributeValueMemberS{Value: str}, data) + + var v ShareKeyType + err = attributevalue.Unmarshal(data, &v) + assert.Nil(t, err) + assert.Equal(t, key, v) + }) + } + + t.Run("malformed", func(t *testing.T) { + var v ShareKeyType + err := json.Unmarshal([]byte(`"WHAT"`), &v) + assert.Error(t, err) + }) + + t.Run("invalid", func(t *testing.T) { + var v ShareKeyType + err := json.Unmarshal([]byte(`"ATTORNEY#123"`), &v) + assert.Error(t, err) + }) + + t.Run("empty json", func(t *testing.T) { + var v ShareKeyType + err := json.Unmarshal([]byte(`""`), &v) + assert.Nil(t, err) + assert.Equal(t, ShareKeyType{}, v) + + data, err := json.Marshal(v) + assert.Nil(t, err) + assert.Equal(t, `""`, string(data)) + }) + + t.Run("empty attributevalue", func(t *testing.T) { + var v ShareKeyType + err := attributevalue.Unmarshal(&types.AttributeValueMemberS{Value: ""}, &v) + assert.Nil(t, err) + assert.Equal(t, ShareKeyType{}, v) + + av, err := attributevalue.Marshal(v) + assert.Nil(t, err) + assert.Equal(t, &types.AttributeValueMemberS{}, av) + }) +} + +func TestShareSortKey(t *testing.T) { + for str, key := range map[string]ShareSortKeyType{ + "DONORINVITE#123#abc": ShareSortKey(DonorInviteKey("123", "abc")), + "METADATA#123": ShareSortKey(MetadataKey("123")), + } { + t.Run(str+"/SK", func(t *testing.T) { + assert.Equal(t, str, key.SK()) + }) + + t.Run(str+"/json", func(t *testing.T) { + data, err := json.Marshal(key) + assert.Nil(t, err) + assert.Equal(t, `"`+str+`"`, string(data)) + + var v ShareSortKeyType + err = json.Unmarshal(data, &v) + assert.Nil(t, err) + assert.Equal(t, key, v) + }) + + t.Run(str+"/attributevalue", func(t *testing.T) { + data, err := attributevalue.Marshal(key) + assert.Nil(t, err) + assert.Equal(t, &types.AttributeValueMemberS{Value: str}, data) + + var v ShareSortKeyType + err = attributevalue.Unmarshal(data, &v) + assert.Nil(t, err) + assert.Equal(t, key, v) + }) + } + + t.Run("malformed", func(t *testing.T) { + var v ShareSortKeyType + err := json.Unmarshal([]byte(`"WHAT"`), &v) + assert.Error(t, err) + }) + + t.Run("invalid", func(t *testing.T) { + var v ShareSortKeyType + err := json.Unmarshal([]byte(`"ATTORNEY#123"`), &v) + assert.Error(t, err) + }) + + t.Run("empty json", func(t *testing.T) { + var v ShareSortKeyType + err := json.Unmarshal([]byte(`""`), &v) + assert.Nil(t, err) + assert.Equal(t, ShareSortKeyType{}, v) + + data, err := json.Marshal(v) + assert.Nil(t, err) + assert.Equal(t, `""`, string(data)) + }) + + t.Run("empty attributevalue", func(t *testing.T) { + var v ShareSortKeyType + err := attributevalue.Unmarshal(&types.AttributeValueMemberS{Value: ""}, &v) + assert.Nil(t, err) + assert.Equal(t, ShareSortKeyType{}, v) + + av, err := attributevalue.Marshal(v) + assert.Nil(t, err) + assert.Equal(t, &types.AttributeValueMemberS{}, av) + }) +} diff --git a/internal/dynamo/keys.go b/internal/dynamo/keys.go index f29569019d..8d00019a3e 100644 --- a/internal/dynamo/keys.go +++ b/internal/dynamo/keys.go @@ -2,107 +2,239 @@ package dynamo import ( "encoding/base64" - "fmt" + "errors" + "strings" +) - "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" +const ( + lpaPrefix = "LPA" + donorPrefix = "DONOR" + subPrefix = "SUB" + attorneyPrefix = "ATTORNEY" + certificateProviderPrefix = "CERTIFICATE_PROVIDER" + documentPrefix = "DOCUMENT" + evidenceReceivedPrefix = "EVIDENCE_RECEIVED" + organisationPrefix = "ORGANISATION" + memberPrefix = "MEMBER" + memberInvitePrefix = "MEMBERINVITE" + memberIDPrefix = "MEMBERID" + metadataPrefix = "METADATA" + donorSharePrefix = "DONORSHARE" + donorInvitePrefix = "DONORINVITE" + certificateProviderSharePrefix = "CERTIFICATEPROVIDERSHARE" + attorneySharePrefix = "ATTORNEYSHARE" ) +func readKey(s string) (any, error) { + prefix, _, ok := strings.Cut(s, "#") + if !ok { + return nil, errors.New("malformed key") + } + + switch prefix { + case lpaPrefix: + return LpaKeyType(s), nil + case donorSharePrefix: + return DonorShareKeyType(s), nil + case certificateProviderSharePrefix: + return CertificateProviderShareKeyType(s), nil + case attorneySharePrefix: + return AttorneyShareKeyType(s), nil + case donorPrefix: + return DonorKeyType(s), nil + case subPrefix: + return SubKeyType(s), nil + case attorneyPrefix: + return AttorneyKeyType(s), nil + case certificateProviderPrefix: + return CertificateProviderKeyType(s), nil + case documentPrefix: + return DocumentKeyType(s), nil + case evidenceReceivedPrefix: + return EvidenceReceivedKeyType(s), nil + case organisationPrefix: + return OrganisationKeyType(s), nil + case memberPrefix: + return MemberKeyType(s), nil + case memberInvitePrefix: + return MemberInviteKeyType(s), nil + case memberIDPrefix: + return MemberIDKeyType(s), nil + case metadataPrefix: + return MetadataKeyType(s), nil + case donorInvitePrefix: + return DonorInviteKeyType(s), nil + default: + return nil, errors.New("unknown key prefix") + } +} + +type PK interface{ PK() string } + +type SK interface{ SK() string } + +type LpaKeyType string + +func (t LpaKeyType) PK() string { return string(t) } + // LpaKey is used as the PK for all Lpa related information. -func LpaKey(s string) string { - return "LPA#" + s +func LpaKey(s string) LpaKeyType { + return LpaKeyType(lpaPrefix + "#" + s) } +type DonorKeyType string + +func (t DonorKeyType) SK() string { return string(t) } +func (t DonorKeyType) lpaOwner() {} // mark as usable with LpaOwnerKey + // DonorKey is used as the SK (with LpaKey as PK) for donor entered // information. It is set to PAPER when the donor information has been provided // from paper forms. -func DonorKey(s string) string { - return "#DONOR#" + s +func DonorKey(s string) DonorKeyType { + return DonorKeyType(donorPrefix + "#" + s) } +type SubKeyType string + +func (t SubKeyType) SK() string { return string(t) } + // SubKey is used as the SK (with LpaKey as PK) to allow queries on a OneLogin // sub against all Lpas an actor may have provided information on. -func SubKey(s string) string { - return "#SUB#" + s +func SubKey(s string) SubKeyType { + return SubKeyType(subPrefix + "#" + s) } +type AttorneyKeyType string + +func (t AttorneyKeyType) SK() string { return string(t) } + // AttorneyKey is used as the SK (with LpaKey as PK) for attorney entered // information. -func AttorneyKey(s string) string { - return "#ATTORNEY#" + s +func AttorneyKey(s string) AttorneyKeyType { + return AttorneyKeyType(attorneyPrefix + "#" + s) } +type CertificateProviderKeyType string + +func (t CertificateProviderKeyType) SK() string { return string(t) } + // CertificateProviderKey is used as the SK (with LpaKey as PK) for certificate // provider entered information. -func CertificateProviderKey(s string) string { - return "#CERTIFICATE_PROVIDER#" + s +func CertificateProviderKey(s string) CertificateProviderKeyType { + return CertificateProviderKeyType(certificateProviderPrefix + "#" + s) } +type DocumentKeyType string + +func (t DocumentKeyType) SK() string { return string(t) } + // DocumentKey is used as the SK (with LpaKey as PK) for any documents uploaded // as evidence for reduced fees. -func DocumentKey(s3Key string) string { - return "#DOCUMENT#" + s3Key +func DocumentKey(s3Key string) DocumentKeyType { + return DocumentKeyType(documentPrefix + "#" + s3Key) } +type EvidenceReceivedKeyType string + +func (t EvidenceReceivedKeyType) SK() string { return string(t) } + // EvidenceReceivedKey is used as the SK (with LpaKey as PK) to show that paper // evidence has been submitted for an Lpa. -func EvidenceReceivedKey() string { - return "#EVIDENCE_RECEIVED" +func EvidenceReceivedKey() EvidenceReceivedKeyType { + return EvidenceReceivedKeyType(evidenceReceivedPrefix + "#") } +type OrganisationKeyType string + +func (t OrganisationKeyType) PK() string { return string(t) } +func (t OrganisationKeyType) SK() string { return string(t) } +func (t OrganisationKeyType) lpaOwner() {} // mark as usable with LpaOwnerKey + // OrganisationKey is used as the PK to group organisation data; or as the SK // (with OrganisationKey as PK) for the organisation itself; or as the SK (with // LpaKey as PK) for the donor information entered by a member of an // organisation. -func OrganisationKey(organisationID string) string { - return "ORGANISATION#" + organisationID +func OrganisationKey(organisationID string) OrganisationKeyType { + return OrganisationKeyType(organisationPrefix + "#" + organisationID) } +type MemberKeyType string + +func (t MemberKeyType) SK() string { return string(t) } + // MemberKey is used as the SK (with OrganisationKey as PK) for a member of an // organisation. -func MemberKey(sessionID string) string { - return "MEMBER#" + sessionID +func MemberKey(sessionID string) MemberKeyType { + return MemberKeyType(memberPrefix + "#" + sessionID) } +type MemberInviteKeyType string + +func (t MemberInviteKeyType) SK() string { return string(t) } + // MemberInviteKey is used as the SK (with OrganisationKey as PK) for a member // invite. -func MemberInviteKey(email string) string { - return fmt.Sprintf("MEMBERINVITE#%s", base64.StdEncoding.EncodeToString([]byte(email))) +func MemberInviteKey(email string) MemberInviteKeyType { + return MemberInviteKeyType(memberInvitePrefix + "#" + base64.StdEncoding.EncodeToString([]byte(email))) } +type MemberIDKeyType string + +func (t MemberIDKeyType) SK() string { return string(t) } + // MemberIDKey is used as the SK (with OrganisationKey as PK) to allow // retrieving a member using their ID instead of their OneLogin sub. -func MemberIDKey(memberID string) string { - return "MEMBERID#" + memberID +func MemberIDKey(memberID string) MemberIDKeyType { + return MemberIDKeyType(memberIDPrefix + "#" + memberID) } +type MetadataKeyType string + +func (t MetadataKeyType) SK() string { return string(t) } +func (t MetadataKeyType) shareSort() {} // mark as usable with ShareSortKey + // MetadataKey is used as the SK when the value of the SK is not used for any purpose. -func MetadataKey(s string) string { - return "#METADATA#" + s +func MetadataKey(s string) MetadataKeyType { + return MetadataKeyType(metadataPrefix + "#" + s) } +type DonorShareKeyType string + +func (t DonorShareKeyType) PK() string { return string(t) } +func (t DonorShareKeyType) share() {} // mark as usable with ShareKey + // DonorShareKey is used as the PK for sharing an Lpa with a donor. -func DonorShareKey(code string) string { - return "DONORSHARE#" + code +func DonorShareKey(code string) DonorShareKeyType { + return DonorShareKeyType(donorSharePrefix + "#" + code) } +type DonorInviteKeyType string + +func (t DonorInviteKeyType) SK() string { return string(t) } +func (t DonorInviteKeyType) shareSort() {} // mark as usable with ShareSortKey + // DonorInviteKey is used as the SK (with DonorShareKey as PK) for an invitation // to a donor to link an Lpa being created by a member of an organisation. -func DonorInviteKey(organisationID, lpaID string) string { - return "DONORINVITE#" + organisationID + "#" + lpaID -} - -// ShareCodeKey is used as the PK for sharing an Lpa with another actor. -func ShareCodeKey(actorType actor.Type, shareCode string) (pk string, err error) { - switch actorType { - case actor.TypeDonor: - return DonorShareKey(shareCode), nil - // As attorneys and replacement attorneys share the same landing page we can't - // differentiate between them - case actor.TypeAttorney, actor.TypeReplacementAttorney, actor.TypeTrustCorporation, actor.TypeReplacementTrustCorporation: - return "ATTORNEYSHARE#" + shareCode, nil - case actor.TypeCertificateProvider: - return "CERTIFICATEPROVIDERSHARE#" + shareCode, nil - default: - return "", fmt.Errorf("cannot have share code for actorType=%v", actorType) - } +func DonorInviteKey(organisationID, lpaID string) DonorInviteKeyType { + return DonorInviteKeyType(donorInvitePrefix + "#" + organisationID + "#" + lpaID) +} + +type CertificateProviderShareKeyType string + +func (t CertificateProviderShareKeyType) PK() string { return string(t) } +func (t CertificateProviderShareKeyType) share() {} // mark as usable with ShareKey + +// CertificateProviderShareKey is used as the PK for sharing an Lpa with a donor. +func CertificateProviderShareKey(code string) CertificateProviderShareKeyType { + return CertificateProviderShareKeyType(certificateProviderSharePrefix + "#" + code) +} + +type AttorneyShareKeyType string + +func (t AttorneyShareKeyType) PK() string { return string(t) } +func (t AttorneyShareKeyType) share() {} // mark as usable with ShareKey + +// AttorneyShareKey is used as the PK for sharing an Lpa with a donor. +func AttorneyShareKey(code string) AttorneyShareKeyType { + return AttorneyShareKeyType(attorneySharePrefix + "#" + code) } diff --git a/internal/dynamo/keys_test.go b/internal/dynamo/keys_test.go index 87dc54a6c0..5bce95c1b4 100644 --- a/internal/dynamo/keys_test.go +++ b/internal/dynamo/keys_test.go @@ -1,69 +1,124 @@ package dynamo import ( + "encoding/json" "testing" - "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" + "github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue" + "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" "github.com/stretchr/testify/assert" ) -func TestStringKeys(t *testing.T) { +func TestReadKeyMalformed(t *testing.T) { + testcases := map[string]string{ + "empty": "", + "no hash": "DONOR", + "unknown prefix": "WHAT#123", + } + + for name, key := range testcases { + t.Run(name, func(t *testing.T) { + _, err := readKey(key) + assert.Error(t, err) + }) + } +} + +func TestPK(t *testing.T) { testcases := map[string]struct { - fn func(string) string - prefix string + key PK + str string }{ - "LpaKey": {LpaKey, "LPA#"}, - "DonorKey": {DonorKey, "#DONOR#"}, - "SubKey": {SubKey, "#SUB#"}, - "AttorneyKey": {AttorneyKey, "#ATTORNEY#"}, - "CertificateProviderKey": {CertificateProviderKey, "#CERTIFICATE_PROVIDER#"}, - "DocumentKey": {DocumentKey, "#DOCUMENT#"}, - "MemberKey": {MemberKey, "MEMBER#"}, - "MemberIDKey": {MemberIDKey, "MEMBERID#"}, - "OrganisationKey": {OrganisationKey, "ORGANISATION#"}, - "MetadataKey": {MetadataKey, "#METADATA#"}, - "DonorShareKey": {DonorShareKey, "DONORSHARE#"}, + "LpaKey": {LpaKey("S"), "LPA#S"}, + "OrganisationKey": {OrganisationKey("S"), "ORGANISATION#S"}, + "DonorShareKey": {DonorShareKey("S"), "DONORSHARE#S"}, + "CertificateProviderShareKey": {CertificateProviderShareKey("S"), "CERTIFICATEPROVIDERSHARE#S"}, + "AttorneyShareKey": {AttorneyShareKey("S"), "ATTORNEYSHARE#S"}, } for name, tc := range testcases { t.Run(name, func(t *testing.T) { - assert.Equal(t, tc.prefix+"S", tc.fn("S")) + assert.Equal(t, tc.str, tc.key.PK()) }) - } -} -func TestEvidenceReceivedKey(t *testing.T) { - assert.Equal(t, "#EVIDENCE_RECEIVED", EvidenceReceivedKey()) -} + t.Run(name+"/read", func(t *testing.T) { + pk, err := readKey(tc.str) + assert.Nil(t, err) + assert.Equal(t, tc.key, pk) + }) -func TestMemberInviteKey(t *testing.T) { - assert.Equal(t, "MEMBERINVITE#ZW1haWxAZXhhbXBsZS5jb20=", MemberInviteKey("email@example.com")) -} + t.Run(name+"/json", func(t *testing.T) { + data, err := json.Marshal(tc.key) + assert.Nil(t, err) + assert.Equal(t, `"`+tc.str+`"`, string(data)) + }) -func TestDonorInviteKey(t *testing.T) { - assert.Equal(t, "DONORINVITE#org-id#lpa-id", DonorInviteKey("org-id", "lpa-id")) + t.Run(name+"/attributevalue", func(t *testing.T) { + data, err := attributevalue.Marshal(tc.key) + assert.Nil(t, err) + assert.Equal(t, &types.AttributeValueMemberS{Value: tc.str}, data) + }) + } } -func TestShareCodeKey(t *testing.T) { - testcases := map[actor.Type]string{ - actor.TypeDonor: "DONORSHARE#", - actor.TypeAttorney: "ATTORNEYSHARE#", - actor.TypeReplacementAttorney: "ATTORNEYSHARE#", - actor.TypeTrustCorporation: "ATTORNEYSHARE#", - actor.TypeReplacementTrustCorporation: "ATTORNEYSHARE#", - actor.TypeCertificateProvider: "CERTIFICATEPROVIDERSHARE#", +func TestSK(t *testing.T) { + testcases := map[string]struct { + key SK + str string + }{ + "DonorKey": {DonorKey("S"), "DONOR#S"}, + "SubKey": {SubKey("S"), "SUB#S"}, + "AttorneyKey": {AttorneyKey("S"), "ATTORNEY#S"}, + "CertificateProviderKey": {CertificateProviderKey("S"), "CERTIFICATE_PROVIDER#S"}, + "DocumentKey": {DocumentKey("S"), "DOCUMENT#S"}, + "EvidenceReceivedKey": {EvidenceReceivedKey(), "EVIDENCE_RECEIVED#"}, + "MemberKey": {MemberKey("S"), "MEMBER#S"}, + "MemberInviteKey": {MemberInviteKey("email@example.com"), "MEMBERINVITE#ZW1haWxAZXhhbXBsZS5jb20="}, + "MemberIDKey": {MemberIDKey("S"), "MEMBERID#S"}, + "OrganisationKey": {OrganisationKey("S"), "ORGANISATION#S"}, + "MetadataKey": {MetadataKey("S"), "METADATA#S"}, + "DonorInviteKey": {DonorInviteKey("org-id", "lpa-id"), "DONORINVITE#org-id#lpa-id"}, } - for actorType, prefix := range testcases { - t.Run(actorType.String(), func(t *testing.T) { - pk, err := ShareCodeKey(actorType, "S") + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + assert.Equal(t, tc.str, tc.key.SK()) + }) + + t.Run(name+"/read", func(t *testing.T) { + sk, err := readKey(tc.str) + assert.Nil(t, err) + assert.Equal(t, tc.key, sk) + }) + + t.Run(name+"/json", func(t *testing.T) { + data, err := json.Marshal(tc.key) + assert.Nil(t, err) + assert.Equal(t, `"`+tc.str+`"`, string(data)) + }) + + t.Run(name+"/attributevalue", func(t *testing.T) { + data, err := attributevalue.Marshal(tc.key) assert.Nil(t, err) - assert.Equal(t, prefix+"S", pk) + assert.Equal(t, &types.AttributeValueMemberS{Value: tc.str}, data) }) } } -func TestShareCodeKeyWhenUnknownType(t *testing.T) { - _, err := ShareCodeKey(actor.TypeAuthorisedSignatory, "S") - assert.NotNil(t, err) +func TestLpaOwnerKeyTypes(t *testing.T) { + for _, key := range []interface{ lpaOwner() }{DonorKey("hey"), OrganisationKey("what")} { + key.lpaOwner() + } +} + +func TestShareKeyTypes(t *testing.T) { + for _, key := range []interface{ share() }{DonorShareKey("hey"), CertificateProviderShareKey("what"), AttorneyShareKey("hello")} { + key.share() + } +} + +func TestShareSortKeyTypes(t *testing.T) { + for _, key := range []interface{ shareSort() }{MetadataKey("hey"), DonorInviteKey("what", "hello")} { + key.shareSort() + } } diff --git a/internal/dynamo/mock_PK_test.go b/internal/dynamo/mock_PK_test.go new file mode 100644 index 0000000000..c21f59d575 --- /dev/null +++ b/internal/dynamo/mock_PK_test.go @@ -0,0 +1,77 @@ +// Code generated by mockery v2.42.2. DO NOT EDIT. + +package dynamo + +import mock "github.com/stretchr/testify/mock" + +// mockPK is an autogenerated mock type for the PK type +type mockPK struct { + mock.Mock +} + +type mockPK_Expecter struct { + mock *mock.Mock +} + +func (_m *mockPK) EXPECT() *mockPK_Expecter { + return &mockPK_Expecter{mock: &_m.Mock} +} + +// PK provides a mock function with given fields: +func (_m *mockPK) PK() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for PK") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// mockPK_PK_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PK' +type mockPK_PK_Call struct { + *mock.Call +} + +// PK is a helper method to define mock.On call +func (_e *mockPK_Expecter) PK() *mockPK_PK_Call { + return &mockPK_PK_Call{Call: _e.mock.On("PK")} +} + +func (_c *mockPK_PK_Call) Run(run func()) *mockPK_PK_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockPK_PK_Call) Return(_a0 string) *mockPK_PK_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockPK_PK_Call) RunAndReturn(run func() string) *mockPK_PK_Call { + _c.Call.Return(run) + return _c +} + +// newMockPK creates a new instance of mockPK. 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 newMockPK(t interface { + mock.TestingT + Cleanup(func()) +}) *mockPK { + mock := &mockPK{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/dynamo/mock_SK_test.go b/internal/dynamo/mock_SK_test.go new file mode 100644 index 0000000000..82ae6e2416 --- /dev/null +++ b/internal/dynamo/mock_SK_test.go @@ -0,0 +1,77 @@ +// Code generated by mockery v2.42.2. DO NOT EDIT. + +package dynamo + +import mock "github.com/stretchr/testify/mock" + +// mockSK is an autogenerated mock type for the SK type +type mockSK struct { + mock.Mock +} + +type mockSK_Expecter struct { + mock *mock.Mock +} + +func (_m *mockSK) EXPECT() *mockSK_Expecter { + return &mockSK_Expecter{mock: &_m.Mock} +} + +// SK provides a mock function with given fields: +func (_m *mockSK) SK() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for SK") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// mockSK_SK_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SK' +type mockSK_SK_Call struct { + *mock.Call +} + +// SK is a helper method to define mock.On call +func (_e *mockSK_Expecter) SK() *mockSK_SK_Call { + return &mockSK_SK_Call{Call: _e.mock.On("SK")} +} + +func (_c *mockSK_SK_Call) Run(run func()) *mockSK_SK_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *mockSK_SK_Call) Return(_a0 string) *mockSK_SK_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *mockSK_SK_Call) RunAndReturn(run func() string) *mockSK_SK_Call { + _c.Call.Return(run) + return _c +} + +// newMockSK creates a new instance of mockSK. 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 newMockSK(t interface { + mock.TestingT + Cleanup(func()) +}) *mockSK { + mock := &mockSK{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/lpastore/resolving_service.go b/internal/lpastore/resolving_service.go index 03e78bc6d3..94e9191e8d 100644 --- a/internal/lpastore/resolving_service.go +++ b/internal/lpastore/resolving_service.go @@ -3,7 +3,6 @@ package lpastore import ( "context" "errors" - "strings" "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" @@ -80,7 +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 - if donor.SK == dynamo.DonorKey("PAPER") { + if donor.SK.Equals(dynamo.DonorKey("PAPER")) { lpa.Submitted = true lpa.Paid = true // set to Professionally so we always show the certificate provider home @@ -91,7 +90,7 @@ func (s *ResolvingService) merge(lpa *Lpa, donor *actor.DonorProvidedDetails) *L lpa.DonorIdentityConfirmed = donor.DonorIdentityConfirmed() lpa.Submitted = !donor.SubmittedAt.IsZero() lpa.Paid = donor.Tasks.PayForLpa.IsCompleted() - lpa.IsOrganisationDonor = strings.HasPrefix(donor.SK, dynamo.OrganisationKey("")) + lpa.IsOrganisationDonor = donor.SK.IsOrganisation() lpa.Donor.Channel = actor.ChannelOnline // copy the relationship as it isn't stored in the lpastore. diff --git a/internal/lpastore/resolving_service_test.go b/internal/lpastore/resolving_service_test.go index e9371dd282..57812d0da7 100644 --- a/internal/lpastore/resolving_service_test.go +++ b/internal/lpastore/resolving_service_test.go @@ -21,7 +21,7 @@ func TestResolvingServiceGet(t *testing.T) { }{ "online with all true": { donor: &actor.DonorProvidedDetails{ - SK: dynamo.OrganisationKey("S"), + SK: dynamo.LpaOwnerKey(dynamo.OrganisationKey("S")), LpaID: "1", LpaUID: "M-1111", SubmittedAt: time.Now(), @@ -58,7 +58,7 @@ func TestResolvingServiceGet(t *testing.T) { }, "online with no lpastore record": { donor: &actor.DonorProvidedDetails{ - SK: dynamo.DonorKey("S"), + SK: dynamo.LpaOwnerKey(dynamo.DonorKey("S")), LpaUID: "M-1111", CertificateProvider: actor.CertificateProvider{ FirstNames: "John", @@ -94,7 +94,7 @@ func TestResolvingServiceGet(t *testing.T) { }, "online with all false": { donor: &actor.DonorProvidedDetails{ - SK: dynamo.DonorKey("S"), + SK: dynamo.LpaOwnerKey(dynamo.DonorKey("S")), LpaID: "1", LpaUID: "M-1111", }, @@ -107,7 +107,7 @@ func TestResolvingServiceGet(t *testing.T) { }, "paper": { donor: &actor.DonorProvidedDetails{ - SK: dynamo.DonorKey("PAPER"), + SK: dynamo.LpaOwnerKey(dynamo.DonorKey("PAPER")), LpaID: "1", LpaUID: "M-1111", }, @@ -233,7 +233,7 @@ func TestResolvingServiceResolveList(t *testing.T) { }{ "online with all true": { donors: []*actor.DonorProvidedDetails{{ - SK: dynamo.OrganisationKey("S"), + SK: dynamo.LpaOwnerKey(dynamo.OrganisationKey("S")), LpaID: "1", LpaUID: "M-1111", SubmittedAt: time.Now(), @@ -272,7 +272,7 @@ func TestResolvingServiceResolveList(t *testing.T) { }, "online with no lpastore record": { donors: []*actor.DonorProvidedDetails{{ - SK: dynamo.DonorKey("S"), + SK: dynamo.LpaOwnerKey(dynamo.DonorKey("S")), LpaUID: "M-1111", CertificateProvider: actor.CertificateProvider{ FirstNames: "John", @@ -308,7 +308,7 @@ func TestResolvingServiceResolveList(t *testing.T) { }, "online with all false": { donors: []*actor.DonorProvidedDetails{{ - SK: dynamo.DonorKey("S"), + SK: dynamo.LpaOwnerKey(dynamo.DonorKey("S")), LpaID: "1", LpaUID: "M-1111", }}, @@ -322,7 +322,7 @@ func TestResolvingServiceResolveList(t *testing.T) { }, "paper": { donors: []*actor.DonorProvidedDetails{{ - SK: dynamo.DonorKey("PAPER"), + SK: dynamo.LpaOwnerKey(dynamo.DonorKey("PAPER")), LpaID: "1", LpaUID: "M-1111", }}, diff --git a/internal/page/document.go b/internal/page/document.go index 2c758f8c22..397b90f791 100644 --- a/internal/page/document.go +++ b/internal/page/document.go @@ -3,10 +3,13 @@ package page import ( "slices" "time" + + "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" ) type Document struct { - PK, SK string + PK dynamo.LpaKeyType + SK dynamo.DocumentKeyType Filename string VirusDetected bool Scanned bool diff --git a/internal/page/donor/upload_evidence_test.go b/internal/page/donor/upload_evidence_test.go index 0479e7395a..d4b0830cba 100644 --- a/internal/page/donor/upload_evidence_test.go +++ b/internal/page/donor/upload_evidence_test.go @@ -13,6 +13,7 @@ import ( "testing" "github.com/ministryofjustice/opg-modernising-lpa/internal/actor" + "github.com/ministryofjustice/opg-modernising-lpa/internal/dynamo" "github.com/ministryofjustice/opg-modernising-lpa/internal/page" "github.com/ministryofjustice/opg-modernising-lpa/internal/pay" "github.com/ministryofjustice/opg-modernising-lpa/internal/validation" @@ -106,8 +107,8 @@ func TestPostUploadEvidenceWithUploadActionAcceptedFileTypes(t *testing.T) { documentStore.EXPECT(). Create(r.Context(), &actor.DonorProvidedDetails{LpaID: "lpa-id", LpaUID: "lpa-uid", FeeType: pay.HalfFee}, filename, mock.Anything). Return(page.Document{ - PK: "LPA#lpa-id", - SK: "#DOCUMENT#lpa-uid/evidence/a-uid", + PK: dynamo.LpaKey("lpa-id"), + SK: dynamo.DocumentKey("lpa-uid/evidence/a-uid"), Filename: filename, Key: "lpa-uid/evidence/a-uid", }, nil) @@ -117,8 +118,8 @@ func TestPostUploadEvidenceWithUploadActionAcceptedFileTypes(t *testing.T) { Execute(w, &uploadEvidenceData{ App: testAppData, Documents: page.Documents{{ - PK: "LPA#lpa-id", - SK: "#DOCUMENT#lpa-uid/evidence/a-uid", + PK: dynamo.LpaKey("lpa-id"), + SK: dynamo.DocumentKey("lpa-uid/evidence/a-uid"), Filename: filename, Key: "lpa-uid/evidence/a-uid"}, }, @@ -161,8 +162,8 @@ func TestPostUploadEvidenceWithUploadActionMultipleFiles(t *testing.T) { documentStore.EXPECT(). Create(r.Context(), &actor.DonorProvidedDetails{LpaID: "lpa-id", LpaUID: "lpa-uid", FeeType: pay.HalfFee}, "dummy.pdf", mock.Anything). Return(page.Document{ - PK: "LPA#lpa-id", - SK: "#DOCUMENT#lpa-uid/evidence/a-uid", + PK: dynamo.LpaKey("lpa-id"), + SK: dynamo.DocumentKey("lpa-uid/evidence/a-uid"), Filename: "dummy.pdf", Key: "lpa-uid/evidence/a-uid", }, nil). @@ -170,8 +171,8 @@ func TestPostUploadEvidenceWithUploadActionMultipleFiles(t *testing.T) { documentStore.EXPECT(). Create(r.Context(), &actor.DonorProvidedDetails{LpaID: "lpa-id", LpaUID: "lpa-uid", FeeType: pay.HalfFee}, "dummy.png", mock.Anything). Return(page.Document{ - PK: "LPA#lpa-id", - SK: "#DOCUMENT#lpa-uid/evidence/a-uid", + PK: dynamo.LpaKey("lpa-id"), + SK: dynamo.DocumentKey("lpa-uid/evidence/a-uid"), Filename: "dummy.png", Key: "lpa-uid/evidence/a-uid", }, nil). @@ -183,14 +184,14 @@ func TestPostUploadEvidenceWithUploadActionMultipleFiles(t *testing.T) { App: testAppData, Documents: page.Documents{ { - PK: "LPA#lpa-id", - SK: "#DOCUMENT#lpa-uid/evidence/a-uid", + PK: dynamo.LpaKey("lpa-id"), + SK: dynamo.DocumentKey("lpa-uid/evidence/a-uid"), Filename: "dummy.pdf", Key: "lpa-uid/evidence/a-uid", }, { - PK: "LPA#lpa-id", - SK: "#DOCUMENT#lpa-uid/evidence/a-uid", + PK: dynamo.LpaKey("lpa-id"), + SK: dynamo.DocumentKey("lpa-uid/evidence/a-uid"), Filename: "dummy.png", Key: "lpa-uid/evidence/a-uid", }, @@ -234,8 +235,8 @@ func TestPostUploadEvidenceWithUploadActionFilenameSpecialCharactersAreEscaped(t documentStore.EXPECT(). Create(r.Context(), &actor.DonorProvidedDetails{LpaID: "lpa-id", LpaUID: "lpa-uid", FeeType: pay.HalfFee}, "<img src=1 onerror=alert(document.domain)>’ brute.heic", mock.Anything). Return(page.Document{ - PK: "LPA#lpa-id", - SK: "#DOCUMENT#lpa-uid/evidence/a-uid", + PK: dynamo.LpaKey("lpa-id"), + SK: dynamo.DocumentKey("lpa-uid/evidence/a-uid"), Filename: "<img src=1 onerror=alert(document.domain)>’ brute.heic", Key: "lpa-uid/evidence/a-uid", }, nil) @@ -246,8 +247,8 @@ func TestPostUploadEvidenceWithUploadActionFilenameSpecialCharactersAreEscaped(t App: testAppData, Documents: page.Documents{ { - PK: "LPA#lpa-id", - SK: "#DOCUMENT#lpa-uid/evidence/a-uid", + PK: dynamo.LpaKey("lpa-id"), + SK: dynamo.DocumentKey("lpa-uid/evidence/a-uid"), Filename: "<img src=1 onerror=alert(document.domain)>’ brute.heic", Key: "lpa-uid/evidence/a-uid", }, @@ -273,14 +274,14 @@ func TestPostUploadEvidenceWithPayAction(t *testing.T) { donor := &actor.DonorProvidedDetails{LpaID: "lpa-id", LpaUID: "lpa-uid", FeeType: pay.HalfFee, EvidenceDelivery: pay.Upload} documents := page.Documents{{ - PK: "LPA#lpa-id", - SK: "#DOCUMENT#lpa-uid/evidence/a-uid", + PK: dynamo.LpaKey("lpa-id"), + SK: dynamo.DocumentKey("lpa-uid/evidence/a-uid"), Filename: "safe.file", Key: "lpa-uid/evidence/a-uid", Scanned: true, }, { - PK: "LPA#lpa-id", - SK: "#DOCUMENT#lpa-uid/evidence/with-virus", + PK: dynamo.LpaKey("lpa-id"), + SK: dynamo.DocumentKey("lpa-uid/evidence/with-virus"), Filename: "virus.file", Key: "lpa-uid/evidence/with-virus", Scanned: true, @@ -343,8 +344,8 @@ func TestPostUploadEvidenceWithPayActionWhenDocumentStoreSubmitErrors(t *testing documentStore.EXPECT(). GetAll(r.Context()). Return(page.Documents{{ - PK: "LPA#lpa-id", - SK: "#DOCUMENT#lpa-uid/evidence/a-uid", + PK: dynamo.LpaKey("lpa-id"), + SK: dynamo.DocumentKey("lpa-uid/evidence/a-uid"), Filename: "safe.file", Key: "lpa-uid/evidence/a-uid", Scanned: true, @@ -370,8 +371,8 @@ func TestPostUploadEvidenceWithPayActionWhenUnscannedDocument(t *testing.T) { documentStore.EXPECT(). GetAll(r.Context()). Return(page.Documents{{ - PK: "LPA#lpa-id", - SK: "#DOCUMENT#lpa-uid/evidence/a-uid", + PK: dynamo.LpaKey("lpa-id"), + SK: dynamo.DocumentKey("lpa-uid/evidence/a-uid"), Filename: "safe.file", Key: "lpa-uid/evidence/a-uid", }}, nil) diff --git a/internal/page/fixtures/certificate_provider.go b/internal/page/fixtures/certificate_provider.go index 2ef4134911..85bad832f3 100644 --- a/internal/page/fixtures/certificate_provider.go +++ b/internal/page/fixtures/certificate_provider.go @@ -78,7 +78,7 @@ func CertificateProvider( lpaID := random.UuidString() donorDetails = &actor.DonorProvidedDetails{ PK: dynamo.LpaKey(lpaID), - SK: dynamo.DonorKey("PAPER"), + SK: dynamo.LpaOwnerKey(dynamo.DonorKey("PAPER")), LpaID: lpaID, LpaUID: random.UuidString(), CreatedAt: time.Now(), diff --git a/internal/page/fixtures/supporter.go b/internal/page/fixtures/supporter.go index a2a14b7ec8..a84b4b91c0 100644 --- a/internal/page/fixtures/supporter.go +++ b/internal/page/fixtures/supporter.go @@ -10,6 +10,7 @@ import ( "time" "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/lpastore" "github.com/ministryofjustice/opg-modernising-lpa/internal/page" @@ -132,8 +133,8 @@ func Supporter( } if linkDonor { - shareCodeData.PK = "DONORSHARE#" + accessCode - shareCodeData.SK = "DONORINVITE#" + shareCodeData.SessionID + "#" + shareCodeData.LpaID + shareCodeData.PK = dynamo.ShareKey(dynamo.DonorShareKey(accessCode)) + shareCodeData.SK = dynamo.ShareSortKey(dynamo.DonorInviteKey(shareCodeData.SessionID, shareCodeData.LpaID)) shareCodeData.UpdatedAt = time.Now() if err := donorStore.Link(donorCtx, shareCodeData); err != nil { @@ -217,8 +218,8 @@ func Supporter( } invite := &actor.MemberInvite{ - PK: "ORGANISATION#" + org.ID, - SK: "MEMBERINVITE#" + base64.StdEncoding.EncodeToString([]byte(email)), + PK: dynamo.OrganisationKey(org.ID), + SK: dynamo.MemberInviteKey(email), CreatedAt: now, OrganisationID: org.ID, OrganisationName: org.Name, @@ -260,8 +261,8 @@ func Supporter( if err = memberStore.CreateFromInvite( memberCtx, &actor.MemberInvite{ - PK: random.String(12), - SK: random.String(12), + PK: dynamo.OrganisationKey(random.String(12)), + SK: dynamo.MemberInviteKey(random.String(12)), CreatedAt: time.Now(), UpdatedAt: time.Now(), OrganisationID: org.ID, diff --git a/internal/page/supporter/dashboard_test.go b/internal/page/supporter/dashboard_test.go index 225365ca37..29439733b4 100644 --- a/internal/page/supporter/dashboard_test.go +++ b/internal/page/supporter/dashboard_test.go @@ -24,7 +24,7 @@ func TestGetDashboard(t *testing.T) { w := httptest.NewRecorder() r, _ := http.NewRequest(http.MethodGet, url, nil) - keys := []dynamo.Key{{PK: "a", SK: "b"}} + keys := []dynamo.Keys{{PK: dynamo.LpaKey("a"), SK: dynamo.OrganisationKey("b")}} pagination := &search.Pagination{Total: 10} donors := []actor.DonorProvidedDetails{{LpaID: "abc"}} @@ -64,7 +64,7 @@ func TestGetDashboardWhenSearchClientErrors(t *testing.T) { searchClient := newMockSearchClient(t) searchClient.EXPECT(). Query(r.Context(), search.QueryRequest{Page: 1, PageSize: 10}). - Return(&search.QueryResponse{Keys: []dynamo.Key{}, Pagination: &search.Pagination{}}, expectedError) + Return(&search.QueryResponse{Keys: []dynamo.Keys{}, Pagination: &search.Pagination{}}, expectedError) err := Dashboard(nil, nil, searchClient)(testAppData, w, r, nil, nil) assert.Equal(t, expectedError, err) @@ -77,7 +77,7 @@ func TestGetDashboardWhenDonorStoreErrors(t *testing.T) { searchClient := newMockSearchClient(t) searchClient.EXPECT(). Query(r.Context(), search.QueryRequest{Page: 1, PageSize: 10}). - Return(&search.QueryResponse{Keys: []dynamo.Key{}, Pagination: &search.Pagination{}}, nil) + Return(&search.QueryResponse{Keys: []dynamo.Keys{}, Pagination: &search.Pagination{}}, nil) donorStore := newMockDonorStore(t) donorStore.EXPECT(). diff --git a/internal/page/supporter/donor_access_test.go b/internal/page/supporter/donor_access_test.go index ccb721d451..24b8eef14a 100644 --- a/internal/page/supporter/donor_access_test.go +++ b/internal/page/supporter/donor_access_test.go @@ -19,7 +19,7 @@ import ( func TestGetDonorAccess(t *testing.T) { donor := &actor.DonorProvidedDetails{Donor: actor.Donor{Email: "x"}} - shareCodeData := actor.ShareCodeData{PK: "1"} + shareCodeData := actor.ShareCodeData{PK: dynamo.ShareKey(dynamo.DonorShareKey("1"))} testcases := map[string]struct { data *donorAccessData @@ -286,7 +286,7 @@ func TestPostDonorAccessRecall(t *testing.T) { r, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader(form.Encode())) r.Header.Add("Content-Type", page.FormUrlEncoded) - shareCodeData := actor.ShareCodeData{PK: "1", InviteSentTo: "email@example.com"} + shareCodeData := actor.ShareCodeData{PK: dynamo.ShareKey(dynamo.DonorShareKey("1")), InviteSentTo: "email@example.com"} donorStore := newMockDonorStore(t) donorStore.EXPECT(). @@ -316,7 +316,7 @@ func TestPostDonorAccessRecallWhenDeleteErrors(t *testing.T) { r, _ := http.NewRequest(http.MethodPost, "/", strings.NewReader(form.Encode())) r.Header.Add("Content-Type", page.FormUrlEncoded) - shareCodeData := actor.ShareCodeData{PK: "1", InviteSentTo: "email@example.com"} + shareCodeData := actor.ShareCodeData{PK: dynamo.ShareKey(dynamo.DonorShareKey("1")), InviteSentTo: "email@example.com"} donorStore := newMockDonorStore(t) donorStore.EXPECT(). @@ -385,8 +385,8 @@ func TestPostDonorAccessRemove(t *testing.T) { r.Header.Add("Content-Type", page.FormUrlEncoded) shareCodeData := actor.ShareCodeData{ - PK: "1", - SK: "DONORINVITE#donor-session-id#lpa-id", + PK: dynamo.ShareKey(dynamo.DonorShareKey("1")), + SK: dynamo.ShareSortKey(dynamo.DonorInviteKey("donor-session-id", "lpa-id")), InviteSentTo: "email@example.com", SessionID: "session-id", } @@ -399,7 +399,7 @@ func TestPostDonorAccessRemove(t *testing.T) { Delete(r.Context(), shareCodeData). Return(nil) - donor := &actor.DonorProvidedDetails{SK: "#DONOR#donor-session-id"} + donor := &actor.DonorProvidedDetails{SK: dynamo.LpaOwnerKey(dynamo.DonorKey("donor-session-id"))} donorStore := newMockDonorStore(t) donorStore.EXPECT(). @@ -425,8 +425,8 @@ func TestPostDonorAccessRemoveWhenDonorHasPaid(t *testing.T) { r.Header.Add("Content-Type", page.FormUrlEncoded) shareCodeData := actor.ShareCodeData{ - PK: "1", - SK: "DONORINVITE#donor-session-id#lpa-id", + PK: dynamo.ShareKey(dynamo.DonorShareKey("1")), + SK: dynamo.ShareSortKey(dynamo.DonorInviteKey("donor-session-id", "lpa-id")), InviteSentTo: "email@example.com", SessionID: "session-id", } @@ -436,7 +436,7 @@ func TestPostDonorAccessRemoveWhenDonorHasPaid(t *testing.T) { GetDonor(r.Context()). Return(shareCodeData, nil) - donor := &actor.DonorProvidedDetails{SK: "#DONOR#donor-session-id", Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskCompleted}} + donor := &actor.DonorProvidedDetails{SK: dynamo.LpaOwnerKey(dynamo.DonorKey("donor-session-id")), Tasks: actor.DonorTasks{PayForLpa: actor.PaymentTaskCompleted}} donorStore := newMockDonorStore(t) donorStore.EXPECT(). @@ -465,7 +465,7 @@ func TestPostDonorAccessRemoveWhenDeleteError(t *testing.T) { Delete(mock.Anything, mock.Anything). Return(expectedError) - donor := &actor.DonorProvidedDetails{SK: "#DONOR#donor-session-id"} + donor := &actor.DonorProvidedDetails{SK: dynamo.LpaOwnerKey(dynamo.DonorKey("donor-session-id"))} donorStore := newMockDonorStore(t) donorStore.EXPECT(). @@ -494,7 +494,7 @@ func TestPostDonorAccessRemoveWhenDeleteLinkError(t *testing.T) { Delete(mock.Anything, mock.Anything). Return(nil) - donor := &actor.DonorProvidedDetails{SK: "#DONOR#donor-session-id"} + donor := &actor.DonorProvidedDetails{SK: dynamo.LpaOwnerKey(dynamo.DonorKey("donor-session-id"))} donorStore := newMockDonorStore(t) donorStore.EXPECT(). diff --git a/internal/page/supporter/mock_DonorStore_test.go b/internal/page/supporter/mock_DonorStore_test.go index fa56b44045..2ea93ed6b9 100644 --- a/internal/page/supporter/mock_DonorStore_test.go +++ b/internal/page/supporter/mock_DonorStore_test.go @@ -131,7 +131,7 @@ func (_c *mockDonorStore_Get_Call) RunAndReturn(run func(context.Context) (*acto } // GetByKeys provides a mock function with given fields: ctx, keys -func (_m *mockDonorStore) GetByKeys(ctx context.Context, keys []dynamo.Key) ([]actor.DonorProvidedDetails, error) { +func (_m *mockDonorStore) GetByKeys(ctx context.Context, keys []dynamo.Keys) ([]actor.DonorProvidedDetails, error) { ret := _m.Called(ctx, keys) if len(ret) == 0 { @@ -140,10 +140,10 @@ func (_m *mockDonorStore) GetByKeys(ctx context.Context, keys []dynamo.Key) ([]a var r0 []actor.DonorProvidedDetails var r1 error - if rf, ok := ret.Get(0).(func(context.Context, []dynamo.Key) ([]actor.DonorProvidedDetails, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, []dynamo.Keys) ([]actor.DonorProvidedDetails, error)); ok { return rf(ctx, keys) } - if rf, ok := ret.Get(0).(func(context.Context, []dynamo.Key) []actor.DonorProvidedDetails); ok { + if rf, ok := ret.Get(0).(func(context.Context, []dynamo.Keys) []actor.DonorProvidedDetails); ok { r0 = rf(ctx, keys) } else { if ret.Get(0) != nil { @@ -151,7 +151,7 @@ func (_m *mockDonorStore) GetByKeys(ctx context.Context, keys []dynamo.Key) ([]a } } - if rf, ok := ret.Get(1).(func(context.Context, []dynamo.Key) error); ok { + if rf, ok := ret.Get(1).(func(context.Context, []dynamo.Keys) error); ok { r1 = rf(ctx, keys) } else { r1 = ret.Error(1) @@ -167,14 +167,14 @@ type mockDonorStore_GetByKeys_Call struct { // GetByKeys is a helper method to define mock.On call // - ctx context.Context -// - keys []dynamo.Key +// - keys []dynamo.Keys func (_e *mockDonorStore_Expecter) GetByKeys(ctx interface{}, keys interface{}) *mockDonorStore_GetByKeys_Call { return &mockDonorStore_GetByKeys_Call{Call: _e.mock.On("GetByKeys", ctx, keys)} } -func (_c *mockDonorStore_GetByKeys_Call) Run(run func(ctx context.Context, keys []dynamo.Key)) *mockDonorStore_GetByKeys_Call { +func (_c *mockDonorStore_GetByKeys_Call) Run(run func(ctx context.Context, keys []dynamo.Keys)) *mockDonorStore_GetByKeys_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].([]dynamo.Key)) + run(args[0].(context.Context), args[1].([]dynamo.Keys)) }) return _c } @@ -184,7 +184,7 @@ func (_c *mockDonorStore_GetByKeys_Call) Return(_a0 []actor.DonorProvidedDetails return _c } -func (_c *mockDonorStore_GetByKeys_Call) RunAndReturn(run func(context.Context, []dynamo.Key) ([]actor.DonorProvidedDetails, error)) *mockDonorStore_GetByKeys_Call { +func (_c *mockDonorStore_GetByKeys_Call) RunAndReturn(run func(context.Context, []dynamo.Keys) ([]actor.DonorProvidedDetails, error)) *mockDonorStore_GetByKeys_Call { _c.Call.Return(run) return _c } diff --git a/internal/page/supporter/register.go b/internal/page/supporter/register.go index ad3c3e817f..a533225842 100644 --- a/internal/page/supporter/register.go +++ b/internal/page/supporter/register.go @@ -50,7 +50,7 @@ type MemberStore interface { type DonorStore interface { DeleteLink(ctx context.Context, shareCodeData actor.ShareCodeData) error Get(ctx context.Context) (*actor.DonorProvidedDetails, error) - GetByKeys(ctx context.Context, keys []dynamo.Key) ([]actor.DonorProvidedDetails, error) + GetByKeys(ctx context.Context, keys []dynamo.Keys) ([]actor.DonorProvidedDetails, error) Put(ctx context.Context, donor *actor.DonorProvidedDetails) error } diff --git a/internal/search/client.go b/internal/search/client.go index 6b20517889..4f38195508 100644 --- a/internal/search/client.go +++ b/internal/search/client.go @@ -47,7 +47,7 @@ type indicesClient interface { type QueryResponse struct { Pagination *Pagination - Keys []dynamo.Key + Keys []dynamo.Keys } type Lpa struct { @@ -151,9 +151,9 @@ func (c *Client) Query(ctx context.Context, req QueryRequest) (*QueryResponse, e return nil, err } - var keys []dynamo.Key + var keys []dynamo.Keys for _, hit := range resp.Hits.Hits { - var key dynamo.Key + var key dynamo.Keys if err := json.Unmarshal(hit.Source, &key); err != nil { return nil, err } @@ -223,10 +223,10 @@ func getSKFromContext(ctx context.Context) (string, error) { return "", err } - sk := "#DONOR#" + session.SessionID + var sk dynamo.SK = dynamo.DonorKey(session.SessionID) if session.OrganisationID != "" { - sk = "ORGANISATION#" + session.OrganisationID + sk = dynamo.OrganisationKey(session.OrganisationID) } - return sk, nil + return sk.SK(), nil } diff --git a/internal/search/client_test.go b/internal/search/client_test.go index ce088ae0a0..bd72d002f5 100644 --- a/internal/search/client_test.go +++ b/internal/search/client_test.go @@ -101,13 +101,13 @@ func TestClientIndex(t *testing.T) { Return(nil, nil) client := &Client{svc: svc, indexingEnabled: true} - err := client.Index(ctx, Lpa{DonorFullName: "x y", PK: "LPA#2020", SK: "abc#123"}) + err := client.Index(ctx, Lpa{DonorFullName: "x y", PK: dynamo.LpaKey("2020").PK(), SK: "abc#123"}) assert.Nil(t, err) } func TestClientIndexWhenNotEnabled(t *testing.T) { client := &Client{} - err := client.Index(ctx, Lpa{DonorFullName: "x y", PK: "LPA#2020", SK: "abc#123"}) + err := client.Index(ctx, Lpa{DonorFullName: "x y", PK: dynamo.LpaKey("2020").PK(), SK: "abc#123"}) assert.Nil(t, err) } @@ -118,38 +118,38 @@ func TestClientIndexWhenIndexErrors(t *testing.T) { Return(nil, expectedError) client := &Client{svc: svc, indexingEnabled: true} - err := client.Index(ctx, Lpa{DonorFullName: "x y", PK: "LPA#2020", SK: "abc#123"}) + err := client.Index(ctx, Lpa{DonorFullName: "x y", PK: dynamo.LpaKey("2020").PK(), SK: "abc#123"}) assert.Equal(t, expectedError, err) } func TestClientQuery(t *testing.T) { testcases := map[string]struct { session *page.SessionData - sk string + sk dynamo.SK from int page int }{ "donor": { session: &page.SessionData{SessionID: "abc"}, - sk: "#DONOR#abc", + sk: dynamo.DonorKey("abc"), from: 0, page: 1, }, "organisation": { session: &page.SessionData{SessionID: "abc", OrganisationID: "xyz"}, - sk: "ORGANISATION#xyz", + sk: dynamo.OrganisationKey("xyz"), from: 0, page: 1, }, "donor paged": { session: &page.SessionData{SessionID: "abc"}, - sk: "#DONOR#abc", + sk: dynamo.DonorKey("abc"), from: 40, page: 5, }, "organisation paged": { session: &page.SessionData{SessionID: "abc", OrganisationID: "xyz"}, - sk: "ORGANISATION#xyz", + sk: dynamo.OrganisationKey("xyz"), from: 40, page: 5, }, @@ -162,15 +162,15 @@ func TestClientQuery(t *testing.T) { resp := &opensearchapi.SearchResp{} resp.Hits.Total.Value = 10 resp.Hits.Hits = []opensearchapi.SearchHit{ - {Source: json.RawMessage(`{"PK":"abc#123","SK":"xyz#456"}`)}, - {Source: json.RawMessage(`{"PK":"abc#456","SK":"xyz#789"}`)}, + {Source: json.RawMessage(`{"PK":"LPA#123","SK":"DONOR#456"}`)}, + {Source: json.RawMessage(`{"PK":"LPA#456","SK":"DONOR#789"}`)}, } svc := newMockOpensearchapiClient(t) svc.EXPECT(). Search(ctx, &opensearchapi.SearchReq{ Indices: []string{indexName}, - Body: bytes.NewReader([]byte(fmt.Sprintf(`{"query":{"match":{"SK":"%s"}}}`, tc.sk))), + Body: bytes.NewReader([]byte(fmt.Sprintf(`{"query":{"match":{"SK":"%s"}}}`, tc.sk.SK()))), Params: opensearchapi.SearchParams{ From: aws.Int(tc.from), Size: aws.Int(10), @@ -184,9 +184,9 @@ func TestClientQuery(t *testing.T) { assert.Nil(t, err) assert.Equal(t, result, &QueryResponse{ Pagination: newPagination(10, tc.page, 10), - Keys: []dynamo.Key{ - {PK: "abc#123", SK: "xyz#456"}, - {PK: "abc#456", SK: "xyz#789"}, + Keys: []dynamo.Keys{ + {PK: dynamo.LpaKey("123"), SK: dynamo.DonorKey("456")}, + {PK: dynamo.LpaKey("456"), SK: dynamo.DonorKey("789")}, }, }) }) @@ -239,7 +239,7 @@ func TestClientCountWithQuery(t *testing.T) { }{ "no query - donor": { query: CountWithQueryReq{}, - body: []byte(`{"query":{"bool":{"must":{"match":{"SK":"#DONOR#1"}}}},"size":0,"track_total_hits":true}`), + body: []byte(`{"query":{"bool":{"must":{"match":{"SK":"DONOR#1"}}}},"size":0,"track_total_hits":true}`), session: &page.SessionData{SessionID: "1"}, }, "no query - organisation": { @@ -249,7 +249,7 @@ func TestClientCountWithQuery(t *testing.T) { }, "MustNotExist query - donor": { query: CountWithQueryReq{MustNotExist: "a-field"}, - body: []byte(`{"query":{"bool":{"must":{"match":{"SK":"#DONOR#1"}},"must_not":{"exists":{"field":"a-field"}}}},"size":0,"track_total_hits":true}`), + body: []byte(`{"query":{"bool":{"must":{"match":{"SK":"DONOR#1"}},"must_not":{"exists":{"field":"a-field"}}}},"size":0,"track_total_hits":true}`), session: &page.SessionData{SessionID: "1"}, }, "MustNotExist query - organisation": { diff --git a/terraform/environment/opensearch_ingestion_pipeline.tf b/terraform/environment/opensearch_ingestion_pipeline.tf index 92648c79fe..d8f3941475 100644 --- a/terraform/environment/opensearch_ingestion_pipeline.tf +++ b/terraform/environment/opensearch_ingestion_pipeline.tf @@ -165,8 +165,8 @@ locals { } } routes = { - lay_journey_lpas = "'contains(/SK, \"#DONOR#\") and contains(/PK, \"#LPA#\")'" - supporter_journey_lpas = "'contains(/SK, \"#ORGANISATION#\") and contains(/PK, \"#LPA#\")'" + lay_journey_lpas = "'contains(/SK, \"DONOR#\") and contains(/PK, \"LPA#\")'" + supporter_journey_lpas = "'contains(/SK, \"ORGANISATION#\") and contains(/PK, \"LPA#\")'" } sink = {