From 67f763229d6aae67d3ffe3208d7048b796d96d50 Mon Sep 17 00:00:00 2001 From: vankongv Date: Wed, 4 Sep 2024 12:59:07 +1000 Subject: [PATCH 01/11] feat: implement delete item function --- interface.go | 1 + 1 file changed, 1 insertion(+) diff --git a/interface.go b/interface.go index f6b1cff..a69faa1 100644 --- a/interface.go +++ b/interface.go @@ -25,6 +25,7 @@ type WriteAPI interface { // Create or update given item in DynamoDB. Must implemenmt DynamoRecord interface. // DynamoRecord.GetKeys will be called to get values for parition and sort keys. PutItem(ctx context.Context, pk Attribute, sk Attribute, item interface{}) error + DeleteItem(ctx context.Context, pk Attribute, sk Attribute) error } type TransactionAPI interface { From 03e329e5b0fa4f6854981a37b244423b761885d1 Mon Sep 17 00:00:00 2001 From: vankongv Date: Wed, 4 Sep 2024 14:37:35 +1000 Subject: [PATCH 02/11] chore: add unit test --- delete_item.go | 12 ++++---- tests/delete_item_test.go | 60 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 tests/delete_item_test.go diff --git a/delete_item.go b/delete_item.go index f82da19..91c9aa0 100644 --- a/delete_item.go +++ b/delete_item.go @@ -14,20 +14,20 @@ import ( * @param sk the sort key of the record * @return true if the record was deleted, false otherwise */ -func (t *Client) DeleteItem(ctx context.Context, pk string, sk string) error { +func (t *Client) DeleteItem(ctx context.Context, pk Attribute, sk Attribute) error { //delete item from dynamodb input := &dynamodb.DeleteItemInput{ TableName: &t.TableName, Key: map[string]types.AttributeValue{ - "pk": &types.AttributeValueMemberS{Value: pk}, - "sk": &types.AttributeValueMemberS{Value: sk}, + "pk": pk, + "sk": sk, }, } - resp, err := t.client.DeleteItem(ctx, input) + _, err := t.client.DeleteItem(ctx, input) - if err != nil && resp == nil { - log.Println("failed to delete record into database. Error:" + err.Error()) + if err != nil { + log.Println("failed to delete record from database. Error:" + err.Error()) return err } diff --git a/tests/delete_item_test.go b/tests/delete_item_test.go new file mode 100644 index 0000000..2c9fc7e --- /dev/null +++ b/tests/delete_item_test.go @@ -0,0 +1,60 @@ +package tests + +import ( + "context" + "testing" + + "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" + "github.com/oolio-group/dynago" +) + +type user struct { + Pk string `dynamodbav:"pk"` + Sk string `dynamodbav:"sk"` +} + +func TestDeleteItem(t *testing.T) { + table := prepareTable(t, dynamoEndpoint, "delete_test") + + ctx := context.TODO() + + // Insert a single item + item := user{Pk: "users#org1", Sk: "user#1"} + err := table.PutItem(ctx, dynago.StringValue(item.Pk), dynago.StringValue(item.Sk), item) + if err != nil { + t.Fatalf("failed to insert item; got %s", err) + } + + // Query the item to ensure it exists + var output []user + _, err = table.Query(ctx, "pk = :pk and sk = :sk", map[string]types.AttributeValue{ + ":pk": &types.AttributeValueMemberS{Value: item.Pk}, + ":sk": &types.AttributeValueMemberS{Value: item.Sk}, + }, &output) + if err != nil { + t.Fatalf("expected query to succeed; got %s", err) + } + if len(output) == 0 { + t.Fatalf("expected item to be found, but found none") + } + + // Delete the item + err = table.DeleteItem(ctx, dynago.StringValue(item.Pk), dynago.StringValue(item.Sk)) + if err != nil { + t.Fatalf("expected delete to succeed; got %s", err) + } + + // Query the item again to ensure it no longer exists + var deleteOutput []user + _, err = table.Query(ctx, "pk = :pk and sk = :sk", map[string]types.AttributeValue{ + ":pk": &types.AttributeValueMemberS{Value: item.Pk}, + ":sk": &types.AttributeValueMemberS{Value: item.Sk}, + }, &deleteOutput) + + if err != nil { + t.Fatalf("expected query to succeed; got %s", err) + } + if len(deleteOutput) != 0 { + t.Fatalf("expected item to be deleted; found %v", deleteOutput) + } +} From 537380df9a2870cdcecc661207c3c72ae3fd3625 Mon Sep 17 00:00:00 2001 From: vankongv Date: Thu, 5 Sep 2024 15:10:58 +1000 Subject: [PATCH 03/11] chore: update test case to check multiple items --- tests/delete_item_test.go | 69 ++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/tests/delete_item_test.go b/tests/delete_item_test.go index 2c9fc7e..86c71c0 100644 --- a/tests/delete_item_test.go +++ b/tests/delete_item_test.go @@ -18,43 +18,66 @@ func TestDeleteItem(t *testing.T) { ctx := context.TODO() - // Insert a single item - item := user{Pk: "users#org1", Sk: "user#1"} - err := table.PutItem(ctx, dynago.StringValue(item.Pk), dynago.StringValue(item.Sk), item) - if err != nil { - t.Fatalf("failed to insert item; got %s", err) + items := []user{ + {Pk: "users#org1", Sk: "user#1"}, + {Pk: "users#org1", Sk: "user#2"}, + {Pk: "users#org1", Sk: "user#3"}, } - // Query the item to ensure it exists - var output []user - _, err = table.Query(ctx, "pk = :pk and sk = :sk", map[string]types.AttributeValue{ - ":pk": &types.AttributeValueMemberS{Value: item.Pk}, - ":sk": &types.AttributeValueMemberS{Value: item.Sk}, - }, &output) - if err != nil { - t.Fatalf("expected query to succeed; got %s", err) + for _, item := range items { + err := table.PutItem(ctx, dynago.StringValue(item.Pk), dynago.StringValue(item.Sk), item) + if err != nil { + t.Fatalf("failed to insert item %v; got %s", item, err) + } } - if len(output) == 0 { - t.Fatalf("expected item to be found, but found none") + + //query all items to check they exist + for _, item := range items { + var output []user + _, err := table.Query(ctx, "pk = :pk and sk = :sk", map[string]types.AttributeValue{ + ":pk": &types.AttributeValueMemberS{Value: item.Pk}, + ":sk": &types.AttributeValueMemberS{Value: item.Sk}, + }, &output) + if err != nil { + t.Fatalf("expected query to succeed for item %v; got %s", item, err) + } + if len(output) == 0 { + t.Fatalf("expected item %v to be found, but found none", item) + } } - // Delete the item - err = table.DeleteItem(ctx, dynago.StringValue(item.Pk), dynago.StringValue(item.Sk)) + // delete 1st item + err := table.DeleteItem(ctx, dynago.StringValue(items[0].Pk), dynago.StringValue(items[0].Sk)) if err != nil { - t.Fatalf("expected delete to succeed; got %s", err) + t.Fatalf("expected delete to succeed for item %v; got %s", items[0], err) } - // Query the item again to ensure it no longer exists + // query first item to confirm it is deleted var deleteOutput []user _, err = table.Query(ctx, "pk = :pk and sk = :sk", map[string]types.AttributeValue{ - ":pk": &types.AttributeValueMemberS{Value: item.Pk}, - ":sk": &types.AttributeValueMemberS{Value: item.Sk}, + ":pk": &types.AttributeValueMemberS{Value: items[0].Pk}, + ":sk": &types.AttributeValueMemberS{Value: items[0].Sk}, }, &deleteOutput) if err != nil { - t.Fatalf("expected query to succeed; got %s", err) + t.Fatalf("expected query to succeed for item %v after deletion; got %s", items[0], err) } if len(deleteOutput) != 0 { - t.Fatalf("expected item to be deleted; found %v", deleteOutput) + t.Fatalf("expected item %v to be deleted; found %v", items[0], deleteOutput) + } + + // query other items to confirm they still exist + for i := 1; i < len(items); i++ { + var output []user + _, err := table.Query(ctx, "pk = :pk and sk = :sk", map[string]types.AttributeValue{ + ":pk": &types.AttributeValueMemberS{Value: items[i].Pk}, + ":sk": &types.AttributeValueMemberS{Value: items[i].Sk}, + }, &output) + if err != nil { + t.Fatalf("expected query to succeed for item %v; got %s", items[i], err) + } + if len(output) == 0 { + t.Fatalf("expected item %v to be found, but found none", items[i]) + } } } From 131bf2b25591c35a2f92082d89f40ecf43d2887d Mon Sep 17 00:00:00 2001 From: vankongv Date: Fri, 6 Sep 2024 13:33:53 +1000 Subject: [PATCH 04/11] chore: refactor test case to use getItem for single items --- tests/delete_item_test.go | 41 ++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/tests/delete_item_test.go b/tests/delete_item_test.go index 86c71c0..53d681b 100644 --- a/tests/delete_item_test.go +++ b/tests/delete_item_test.go @@ -4,7 +4,6 @@ import ( "context" "testing" - "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" "github.com/oolio-group/dynago" ) @@ -33,51 +32,45 @@ func TestDeleteItem(t *testing.T) { //query all items to check they exist for _, item := range items { - var output []user - _, err := table.Query(ctx, "pk = :pk and sk = :sk", map[string]types.AttributeValue{ - ":pk": &types.AttributeValueMemberS{Value: item.Pk}, - ":sk": &types.AttributeValueMemberS{Value: item.Sk}, - }, &output) + var output user + err, found := table.GetItem(ctx, dynago.StringValue(item.Pk), dynago.StringValue(item.Sk), &output) + if err != nil { t.Fatalf("expected query to succeed for item %v; got %s", item, err) } - if len(output) == 0 { - t.Fatalf("expected item %v to be found, but found none", item) + if found == false { + t.Fatalf("expected item %v to be found; got none", item) } } // delete 1st item err := table.DeleteItem(ctx, dynago.StringValue(items[0].Pk), dynago.StringValue(items[0].Sk)) + if err != nil { - t.Fatalf("expected delete to succeed for item %v; got %s", items[0], err) + t.Fatalf("failed to delete item %v; got %s", items[0], err) } // query first item to confirm it is deleted - var deleteOutput []user - _, err = table.Query(ctx, "pk = :pk and sk = :sk", map[string]types.AttributeValue{ - ":pk": &types.AttributeValueMemberS{Value: items[0].Pk}, - ":sk": &types.AttributeValueMemberS{Value: items[0].Sk}, - }, &deleteOutput) + var deletedItem user + err, found := table.GetItem(ctx, dynago.StringValue(items[0].Pk), dynago.StringValue(items[0].Sk), &deletedItem) if err != nil { - t.Fatalf("expected query to succeed for item %v after deletion; got %s", items[0], err) + t.Fatalf("expected query to succeed for item %v; got %s", items[0], err) } - if len(deleteOutput) != 0 { - t.Fatalf("expected item %v to be deleted; found %v", items[0], deleteOutput) + if found == true { + t.Fatalf("expected item to be deleted; got %v", items[0]) } // query other items to confirm they still exist for i := 1; i < len(items); i++ { - var output []user - _, err := table.Query(ctx, "pk = :pk and sk = :sk", map[string]types.AttributeValue{ - ":pk": &types.AttributeValueMemberS{Value: items[i].Pk}, - ":sk": &types.AttributeValueMemberS{Value: items[i].Sk}, - }, &output) + var output user + err, found := table.GetItem(ctx, dynago.StringValue(items[i].Pk), dynago.StringValue(items[i].Sk), &output) + if err != nil { t.Fatalf("expected query to succeed for item %v; got %s", items[i], err) } - if len(output) == 0 { - t.Fatalf("expected item %v to be found, but found none", items[i]) + if found == false { + t.Fatalf("expected item %v to be found; got none", items[i]) } } } From 49b9295dd385847e99fa915ecba80fc9f4986876 Mon Sep 17 00:00:00 2001 From: vankongv Date: Tue, 10 Sep 2024 11:41:44 +1000 Subject: [PATCH 05/11] chore: updated test cases --- tests/delete_item_test.go | 207 +++++++++++++++++++++++++++++--------- tests/seedTable.go | 54 ++++++++++ 2 files changed, 212 insertions(+), 49 deletions(-) create mode 100644 tests/seedTable.go diff --git a/tests/delete_item_test.go b/tests/delete_item_test.go index 53d681b..a5e7345 100644 --- a/tests/delete_item_test.go +++ b/tests/delete_item_test.go @@ -2,75 +2,184 @@ package tests import ( "context" + "fmt" + "slices" "testing" + "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" "github.com/oolio-group/dynago" ) type user struct { - Pk string `dynamodbav:"pk"` - Sk string `dynamodbav:"sk"` + Name string + Phone string } -func TestDeleteItem(t *testing.T) { - table := prepareTable(t, dynamoEndpoint, "delete_test") +type userKeys struct { + pk string + sk string +} + +func getSeedData(ctx context.Context, table *dynago.Client, seedData []tableRecord) ([]tableRecord, error) { + itemsToGet := make([]map[string]types.AttributeValue, 0, len(seedData)) + for _, v := range seedData { + itemsToGet = append(itemsToGet, map[string]types.AttributeValue{ + "pk": dynago.StringValue(v.Pk), + "sk": dynago.StringValue(v.Sk), + }) + } + var out []tableRecord + + err := table.BatchGetItems(ctx, itemsToGet, &out) + if err != nil { + return nil, fmt.Errorf("Unable to get seed data: %w", err) + } + + return out, nil +} + +func TestDeleteItem(t *testing.T) { ctx := context.TODO() + table := prepareTable(t, dynamoEndpoint, "delete_test") - items := []user{ - {Pk: "users#org1", Sk: "user#1"}, - {Pk: "users#org1", Sk: "user#2"}, - {Pk: "users#org1", Sk: "user#3"}, + records := []tableRecord{ + { + Pk: "users#org1", + Sk: "user#1", + Record: user{ + Name: "User 1", + Phone: "xyz", + }, + }, + { + Pk: "users#org1", + Sk: "user#2", + Record: user{ + Name: "User 2", + Phone: "asd", + }, + }, + { + Pk: "users#org1", + Sk: "user#3", + Record: user{ + Name: "User 3", + Phone: "qwe", + }, + }, + { + Pk: "users#org2", + Sk: "user#4", + Record: user{ + Name: "User 4", + Phone: "fgh", + }, + }, } - for _, item := range items { - err := table.PutItem(ctx, dynago.StringValue(item.Pk), dynago.StringValue(item.Sk), item) - if err != nil { - t.Fatalf("failed to insert item %v; got %s", item, err) - } + type testCase struct { + title string + itemsToDelete []userKeys + itemsLeft int + seedData []tableRecord } - //query all items to check they exist - for _, item := range items { - var output user - err, found := table.GetItem(ctx, dynago.StringValue(item.Pk), dynago.StringValue(item.Sk), &output) - - if err != nil { - t.Fatalf("expected query to succeed for item %v; got %s", item, err) - } - if found == false { - t.Fatalf("expected item %v to be found; got none", item) - } + cases := []testCase{ + { + title: "Delete User 2", + itemsToDelete: []userKeys{ + { + pk: "users#org1", + sk: "user#2", + }, + }, + itemsLeft: 3, + seedData: records, + }, + { + title: "Delete all users", + itemsToDelete: []userKeys{ + { + pk: "users#org1", + sk: "user#1", + }, + { + pk: "users#org1", + sk: "user#2", + }, + { + pk: "users#org1", + sk: "user#3", + }, + { + pk: "users#org2", + sk: "user#4", + }, + }, + itemsLeft: 0, + seedData: records, + }, + { + title: "Delete wrong user", + itemsToDelete: []userKeys{ + { + pk: "users#org1", + sk: "user#none", + }, + }, + itemsLeft: 4, + seedData: records, + }, + { + title: "Delete user with invalid keys", + itemsToDelete: []userKeys{ + { + pk: "invalid#org", + sk: "invalid#user", + }, + }, + itemsLeft: 4, + seedData: records, + }, } - // delete 1st item - err := table.DeleteItem(ctx, dynago.StringValue(items[0].Pk), dynago.StringValue(items[0].Sk)) + for _, c := range cases { + t.Run(c.title, func(t *testing.T) { + err := seedRecords(ctx, table, c.seedData) + if err != nil { + t.Fatalf("failed to prepare records; got %s", err) + } - if err != nil { - t.Fatalf("failed to delete item %v; got %s", items[0], err) - } + for _, item := range c.itemsToDelete { + err = table.DeleteItem(ctx, dynago.StringValue(item.pk), dynago.StringValue(item.sk)) + if err != nil { + t.Fatalf("unable to delete records; got %s", err) + } + } - // query first item to confirm it is deleted - var deletedItem user - err, found := table.GetItem(ctx, dynago.StringValue(items[0].Pk), dynago.StringValue(items[0].Sk), &deletedItem) + data, err := getSeedData(ctx, table, c.seedData) + if err != nil { + t.Fatalf("failed to prepare records; got %s", err) + } - if err != nil { - t.Fatalf("expected query to succeed for item %v; got %s", items[0], err) - } - if found == true { - t.Fatalf("expected item to be deleted; got %v", items[0]) - } + // see if "itemsToDelete" are the ones deleted. + dataKeys := make([]string, 0, len(data)) + for _, item := range data { + dataKeys = append(dataKeys, fmt.Sprintf("%s--%s", item.Pk, item.Sk)) + } + + for _, v := range c.itemsToDelete { + recKey := fmt.Sprintf("%s--%s", v.pk, v.sk) + if slices.Contains(dataKeys, recKey) { + t.Fatalf("expected items to deleted in db but found it, pk %s; sk %s", v.pk, v.sk) + } + } - // query other items to confirm they still exist - for i := 1; i < len(items); i++ { - var output user - err, found := table.GetItem(ctx, dynago.StringValue(items[i].Pk), dynago.StringValue(items[i].Sk), &output) - - if err != nil { - t.Fatalf("expected query to succeed for item %v; got %s", items[i], err) - } - if found == false { - t.Fatalf("expected item %v to be found; got none", items[i]) - } + // compare items remaining is seed data matches test + if len(data) != c.itemsLeft { + t.Fatalf("expected items in db; %v found; %v", c.itemsLeft, len(data)) + } + }) } } diff --git a/tests/seedTable.go b/tests/seedTable.go new file mode 100644 index 0000000..69d6d45 --- /dev/null +++ b/tests/seedTable.go @@ -0,0 +1,54 @@ +package tests + +import ( + "context" + "fmt" + + "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" + "github.com/oolio-group/dynago" +) + +type tableRecord struct { + Pk string `dynamodbav:"pk"` + Sk string `dynamodbav:"sk"` + Record interface{} +} + +func seedRecords(ctx context.Context, table *dynago.Client, input []tableRecord) error { + items := make([]*dynago.TransactPutItemsInput, 0, len(input)) + for _, item := range input { + in := dynago.TransactPutItemsInput{ + PartitionKeyValue: dynago.StringValue(item.Pk), + SortKeyValue: dynago.StringValue(item.Sk), + Item: item, + } + + items = append(items, &in) + } + + err := table.TransactPutItems(ctx, items) + if err != nil { + return fmt.Errorf("failed to insert items; got %s", err) + } + + requests := make([]map[string]types.AttributeValue, 0, len(input)) + for _, item := range input { + req := map[string]types.AttributeValue{ + "pk": dynago.StringValue(item.Pk), + "sk": dynago.StringValue(item.Sk), + } + requests = append(requests, req) + } + + var output []tableRecord + err = table.BatchGetItems(ctx, requests, &output) + if err != nil { + return fmt.Errorf("failed to get items; got %s", err) + } + + if len(input) != len(output) { + return fmt.Errorf("count of items does not match input items; got %s", err) + } + + return nil +} From 512b0f192fab40b7ee5385ad29086c08dc91642c Mon Sep 17 00:00:00 2001 From: vankongv Date: Tue, 10 Sep 2024 15:42:26 +1000 Subject: [PATCH 06/11] chore: cleanup, update documentation --- README.md | 2 +- tests/delete_item_test.go | 98 ++++++++++------------ tests/{seedTable.go => seed_table_test.go} | 21 +---- tests/transact_items_test.go | 3 +- 4 files changed, 50 insertions(+), 74 deletions(-) rename tests/{seedTable.go => seed_table_test.go} (55%) diff --git a/README.md b/README.md index 539f6e9..8cea3b3 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ table, err := dynago.NewClient(ctx, dynago.ClientOptions{ ### Run dynago.locally ```sh -docker run -p docker run -p 8000:8000 amazon/dynago.local +docker run -p 8000:8000 amazon/dynamodb-local ``` ```go diff --git a/tests/delete_item_test.go b/tests/delete_item_test.go index a5e7345..74cfad3 100644 --- a/tests/delete_item_test.go +++ b/tests/delete_item_test.go @@ -16,27 +16,15 @@ type user struct { } type userKeys struct { - pk string - sk string + Pk string + Sk string } -func getSeedData(ctx context.Context, table *dynago.Client, seedData []tableRecord) ([]tableRecord, error) { - itemsToGet := make([]map[string]types.AttributeValue, 0, len(seedData)) - for _, v := range seedData { - itemsToGet = append(itemsToGet, map[string]types.AttributeValue{ - "pk": dynago.StringValue(v.Pk), - "sk": dynago.StringValue(v.Sk), - }) - } - - var out []tableRecord - - err := table.BatchGetItems(ctx, itemsToGet, &out) - if err != nil { - return nil, fmt.Errorf("Unable to get seed data: %w", err) - } - - return out, nil +type testCase struct { + title string + itemsToDelete []userKeys + itemsLeft int + seedData []tableRecord } func TestDeleteItem(t *testing.T) { @@ -78,43 +66,36 @@ func TestDeleteItem(t *testing.T) { }, } - type testCase struct { - title string - itemsToDelete []userKeys - itemsLeft int - seedData []tableRecord - } - cases := []testCase{ { title: "Delete User 2", itemsToDelete: []userKeys{ { - pk: "users#org1", - sk: "user#2", + Pk: "users#org1", + Sk: "user#2", }, }, itemsLeft: 3, - seedData: records, + seedData: records, //each test case will have the same seed data }, { title: "Delete all users", itemsToDelete: []userKeys{ { - pk: "users#org1", - sk: "user#1", + Pk: "users#org1", + Sk: "user#1", }, { - pk: "users#org1", - sk: "user#2", + Pk: "users#org1", + Sk: "user#2", }, { - pk: "users#org1", - sk: "user#3", + Pk: "users#org1", + Sk: "user#3", }, { - pk: "users#org2", - sk: "user#4", + Pk: "users#org2", + Sk: "user#4", }, }, itemsLeft: 0, @@ -124,8 +105,8 @@ func TestDeleteItem(t *testing.T) { title: "Delete wrong user", itemsToDelete: []userKeys{ { - pk: "users#org1", - sk: "user#none", + Pk: "users#org1", + Sk: "user#none", }, }, itemsLeft: 4, @@ -135,8 +116,8 @@ func TestDeleteItem(t *testing.T) { title: "Delete user with invalid keys", itemsToDelete: []userKeys{ { - pk: "invalid#org", - sk: "invalid#user", + Pk: "invalid#org", + Sk: "invalid#user", }, }, itemsLeft: 4, @@ -146,39 +127,52 @@ func TestDeleteItem(t *testing.T) { for _, c := range cases { t.Run(c.title, func(t *testing.T) { + //prepare records err := seedRecords(ctx, table, c.seedData) if err != nil { t.Fatalf("failed to prepare records; got %s", err) } + //delete records for _, item := range c.itemsToDelete { - err = table.DeleteItem(ctx, dynago.StringValue(item.pk), dynago.StringValue(item.sk)) + err = table.DeleteItem(ctx, dynago.StringValue(item.Pk), dynago.StringValue(item.Sk)) if err != nil { t.Fatalf("unable to delete records; got %s", err) } } - data, err := getSeedData(ctx, table, c.seedData) + //get original seed records + itemsToGet := make([]map[string]types.AttributeValue, 0, len(c.seedData)) + for _, v := range c.seedData { + itemsToGet = append(itemsToGet, map[string]types.AttributeValue{ + "pk": dynago.StringValue(v.Pk), + "sk": dynago.StringValue(v.Sk), + }) + } + + var out []tableRecord + + err = table.BatchGetItems(ctx, itemsToGet, &out) if err != nil { - t.Fatalf("failed to prepare records; got %s", err) + t.Fatalf("Unable to get seed data: %s", err) } - // see if "itemsToDelete" are the ones deleted. - dataKeys := make([]string, 0, len(data)) - for _, item := range data { + //check if deleted items are not in db + dataKeys := make([]string, 0, len(out)) + for _, item := range out { dataKeys = append(dataKeys, fmt.Sprintf("%s--%s", item.Pk, item.Sk)) } for _, v := range c.itemsToDelete { - recKey := fmt.Sprintf("%s--%s", v.pk, v.sk) + recKey := fmt.Sprintf("%s--%s", v.Pk, v.Sk) if slices.Contains(dataKeys, recKey) { - t.Fatalf("expected items to deleted in db but found it, pk %s; sk %s", v.pk, v.sk) + t.Fatalf("expected items to deleted in db but found it, pk %s; sk %s", v.Pk, v.Sk) } } - // compare items remaining is seed data matches test - if len(data) != c.itemsLeft { - t.Fatalf("expected items in db; %v found; %v", c.itemsLeft, len(data)) + //check if remaining records match expected number of items left + if len(out) != c.itemsLeft { + t.Fatalf("expected items in db; %v found; %v", c.itemsLeft, len(out)) } }) } diff --git a/tests/seedTable.go b/tests/seed_table_test.go similarity index 55% rename from tests/seedTable.go rename to tests/seed_table_test.go index 69d6d45..8b3635c 100644 --- a/tests/seedTable.go +++ b/tests/seed_table_test.go @@ -4,7 +4,6 @@ import ( "context" "fmt" - "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" "github.com/oolio-group/dynago" ) @@ -15,6 +14,7 @@ type tableRecord struct { } func seedRecords(ctx context.Context, table *dynago.Client, input []tableRecord) error { + //insert items items := make([]*dynago.TransactPutItemsInput, 0, len(input)) for _, item := range input { in := dynago.TransactPutItemsInput{ @@ -31,24 +31,5 @@ func seedRecords(ctx context.Context, table *dynago.Client, input []tableRecord) return fmt.Errorf("failed to insert items; got %s", err) } - requests := make([]map[string]types.AttributeValue, 0, len(input)) - for _, item := range input { - req := map[string]types.AttributeValue{ - "pk": dynago.StringValue(item.Pk), - "sk": dynago.StringValue(item.Sk), - } - requests = append(requests, req) - } - - var output []tableRecord - err = table.BatchGetItems(ctx, requests, &output) - if err != nil { - return fmt.Errorf("failed to get items; got %s", err) - } - - if len(input) != len(output) { - return fmt.Errorf("count of items does not match input items; got %s", err) - } - return nil } diff --git a/tests/transact_items_test.go b/tests/transact_items_test.go index 8be681a..21142f7 100644 --- a/tests/transact_items_test.go +++ b/tests/transact_items_test.go @@ -89,7 +89,7 @@ func TestTransactItems(t *testing.T) { items := make([]*dynago.TransactPutItemsInput, 0, len(tc.newItems)) for _, item := range tc.newItems { items = append(items, &dynago.TransactPutItemsInput{ - dynago.StringValue(item.Pk), dynago.StringValue(item.Sk), item, + PartitionKeyValue: dynago.StringValue(item.Pk), SortKeyValue: dynago.StringValue(item.Sk), Item: item, }) } err := table.TransactPutItems(ctx, items) @@ -98,6 +98,7 @@ func TestTransactItems(t *testing.T) { } } //perform operations + if len(tc.operations) > 0 { err := table.TransactItems(ctx, tc.operations...) if err != nil { From 3e7d7f61344cf83a90a551570c373f8bcd05dada Mon Sep 17 00:00:00 2001 From: vankongv Date: Tue, 10 Sep 2024 15:56:42 +1000 Subject: [PATCH 07/11] chore: delete 1 test case --- tests/delete_item_test.go | 59 ++++++++++++--------------------------- 1 file changed, 18 insertions(+), 41 deletions(-) diff --git a/tests/delete_item_test.go b/tests/delete_item_test.go index 74cfad3..163c328 100644 --- a/tests/delete_item_test.go +++ b/tests/delete_item_test.go @@ -21,10 +21,10 @@ type userKeys struct { } type testCase struct { - title string - itemsToDelete []userKeys - itemsLeft int - seedData []tableRecord + title string + itemsToDelete []userKeys + expectedItemsLeft int + seedData []tableRecord } func TestDeleteItem(t *testing.T) { @@ -75,53 +75,30 @@ func TestDeleteItem(t *testing.T) { Sk: "user#2", }, }, - itemsLeft: 3, - seedData: records, //each test case will have the same seed data + expectedItemsLeft: 3, + seedData: records, //each test case will have the same seed data }, { - title: "Delete all users", - itemsToDelete: []userKeys{ - { - Pk: "users#org1", - Sk: "user#1", - }, - { - Pk: "users#org1", - Sk: "user#2", - }, - { - Pk: "users#org1", - Sk: "user#3", - }, - { - Pk: "users#org2", - Sk: "user#4", - }, - }, - itemsLeft: 0, - seedData: records, - }, - { - title: "Delete wrong user", + title: "Delete item with wrong sk", itemsToDelete: []userKeys{ { Pk: "users#org1", Sk: "user#none", }, }, - itemsLeft: 4, - seedData: records, + expectedItemsLeft: 4, + seedData: records, }, { - title: "Delete user with invalid keys", + title: "Delete item with wrong pk and sk", itemsToDelete: []userKeys{ { Pk: "invalid#org", Sk: "invalid#user", }, }, - itemsLeft: 4, - seedData: records, + expectedItemsLeft: 4, + seedData: records, }, } @@ -150,16 +127,16 @@ func TestDeleteItem(t *testing.T) { }) } - var out []tableRecord + var remainingItems []tableRecord - err = table.BatchGetItems(ctx, itemsToGet, &out) + err = table.BatchGetItems(ctx, itemsToGet, &remainingItems) if err != nil { t.Fatalf("Unable to get seed data: %s", err) } //check if deleted items are not in db - dataKeys := make([]string, 0, len(out)) - for _, item := range out { + dataKeys := make([]string, 0, len(remainingItems)) + for _, item := range remainingItems { dataKeys = append(dataKeys, fmt.Sprintf("%s--%s", item.Pk, item.Sk)) } @@ -171,8 +148,8 @@ func TestDeleteItem(t *testing.T) { } //check if remaining records match expected number of items left - if len(out) != c.itemsLeft { - t.Fatalf("expected items in db; %v found; %v", c.itemsLeft, len(out)) + if len(remainingItems) != c.expectedItemsLeft { + t.Fatalf("expected items in db; %v found; %v", c.expectedItemsLeft, len(remainingItems)) } }) } From 5b8b46c2bd0811bd077dfd541003caec961a8f21 Mon Sep 17 00:00:00 2001 From: vankongv Date: Tue, 10 Sep 2024 15:57:27 +1000 Subject: [PATCH 08/11] chore: revert parameter type for deleteitem --- delete_item.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/delete_item.go b/delete_item.go index 91c9aa0..f82da19 100644 --- a/delete_item.go +++ b/delete_item.go @@ -14,20 +14,20 @@ import ( * @param sk the sort key of the record * @return true if the record was deleted, false otherwise */ -func (t *Client) DeleteItem(ctx context.Context, pk Attribute, sk Attribute) error { +func (t *Client) DeleteItem(ctx context.Context, pk string, sk string) error { //delete item from dynamodb input := &dynamodb.DeleteItemInput{ TableName: &t.TableName, Key: map[string]types.AttributeValue{ - "pk": pk, - "sk": sk, + "pk": &types.AttributeValueMemberS{Value: pk}, + "sk": &types.AttributeValueMemberS{Value: sk}, }, } - _, err := t.client.DeleteItem(ctx, input) + resp, err := t.client.DeleteItem(ctx, input) - if err != nil { - log.Println("failed to delete record from database. Error:" + err.Error()) + if err != nil && resp == nil { + log.Println("failed to delete record into database. Error:" + err.Error()) return err } From aec272dc2ef1368cfdb8492db5713e70b2f25fae Mon Sep 17 00:00:00 2001 From: vankongv Date: Tue, 10 Sep 2024 15:59:35 +1000 Subject: [PATCH 09/11] chore: revert changes --- tests/delete_item_test.go | 156 ----------------------------------- tests/seed_table_test.go | 35 -------- tests/transact_items_test.go | 5 +- 3 files changed, 2 insertions(+), 194 deletions(-) delete mode 100644 tests/delete_item_test.go delete mode 100644 tests/seed_table_test.go diff --git a/tests/delete_item_test.go b/tests/delete_item_test.go deleted file mode 100644 index 163c328..0000000 --- a/tests/delete_item_test.go +++ /dev/null @@ -1,156 +0,0 @@ -package tests - -import ( - "context" - "fmt" - "slices" - "testing" - - "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" - "github.com/oolio-group/dynago" -) - -type user struct { - Name string - Phone string -} - -type userKeys struct { - Pk string - Sk string -} - -type testCase struct { - title string - itemsToDelete []userKeys - expectedItemsLeft int - seedData []tableRecord -} - -func TestDeleteItem(t *testing.T) { - ctx := context.TODO() - table := prepareTable(t, dynamoEndpoint, "delete_test") - - records := []tableRecord{ - { - Pk: "users#org1", - Sk: "user#1", - Record: user{ - Name: "User 1", - Phone: "xyz", - }, - }, - { - Pk: "users#org1", - Sk: "user#2", - Record: user{ - Name: "User 2", - Phone: "asd", - }, - }, - { - Pk: "users#org1", - Sk: "user#3", - Record: user{ - Name: "User 3", - Phone: "qwe", - }, - }, - { - Pk: "users#org2", - Sk: "user#4", - Record: user{ - Name: "User 4", - Phone: "fgh", - }, - }, - } - - cases := []testCase{ - { - title: "Delete User 2", - itemsToDelete: []userKeys{ - { - Pk: "users#org1", - Sk: "user#2", - }, - }, - expectedItemsLeft: 3, - seedData: records, //each test case will have the same seed data - }, - { - title: "Delete item with wrong sk", - itemsToDelete: []userKeys{ - { - Pk: "users#org1", - Sk: "user#none", - }, - }, - expectedItemsLeft: 4, - seedData: records, - }, - { - title: "Delete item with wrong pk and sk", - itemsToDelete: []userKeys{ - { - Pk: "invalid#org", - Sk: "invalid#user", - }, - }, - expectedItemsLeft: 4, - seedData: records, - }, - } - - for _, c := range cases { - t.Run(c.title, func(t *testing.T) { - //prepare records - err := seedRecords(ctx, table, c.seedData) - if err != nil { - t.Fatalf("failed to prepare records; got %s", err) - } - - //delete records - for _, item := range c.itemsToDelete { - err = table.DeleteItem(ctx, dynago.StringValue(item.Pk), dynago.StringValue(item.Sk)) - if err != nil { - t.Fatalf("unable to delete records; got %s", err) - } - } - - //get original seed records - itemsToGet := make([]map[string]types.AttributeValue, 0, len(c.seedData)) - for _, v := range c.seedData { - itemsToGet = append(itemsToGet, map[string]types.AttributeValue{ - "pk": dynago.StringValue(v.Pk), - "sk": dynago.StringValue(v.Sk), - }) - } - - var remainingItems []tableRecord - - err = table.BatchGetItems(ctx, itemsToGet, &remainingItems) - if err != nil { - t.Fatalf("Unable to get seed data: %s", err) - } - - //check if deleted items are not in db - dataKeys := make([]string, 0, len(remainingItems)) - for _, item := range remainingItems { - dataKeys = append(dataKeys, fmt.Sprintf("%s--%s", item.Pk, item.Sk)) - } - - for _, v := range c.itemsToDelete { - recKey := fmt.Sprintf("%s--%s", v.Pk, v.Sk) - if slices.Contains(dataKeys, recKey) { - t.Fatalf("expected items to deleted in db but found it, pk %s; sk %s", v.Pk, v.Sk) - } - } - - //check if remaining records match expected number of items left - if len(remainingItems) != c.expectedItemsLeft { - t.Fatalf("expected items in db; %v found; %v", c.expectedItemsLeft, len(remainingItems)) - } - }) - } -} diff --git a/tests/seed_table_test.go b/tests/seed_table_test.go deleted file mode 100644 index 8b3635c..0000000 --- a/tests/seed_table_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package tests - -import ( - "context" - "fmt" - - "github.com/oolio-group/dynago" -) - -type tableRecord struct { - Pk string `dynamodbav:"pk"` - Sk string `dynamodbav:"sk"` - Record interface{} -} - -func seedRecords(ctx context.Context, table *dynago.Client, input []tableRecord) error { - //insert items - items := make([]*dynago.TransactPutItemsInput, 0, len(input)) - for _, item := range input { - in := dynago.TransactPutItemsInput{ - PartitionKeyValue: dynago.StringValue(item.Pk), - SortKeyValue: dynago.StringValue(item.Sk), - Item: item, - } - - items = append(items, &in) - } - - err := table.TransactPutItems(ctx, items) - if err != nil { - return fmt.Errorf("failed to insert items; got %s", err) - } - - return nil -} diff --git a/tests/transact_items_test.go b/tests/transact_items_test.go index 21142f7..7777057 100644 --- a/tests/transact_items_test.go +++ b/tests/transact_items_test.go @@ -89,7 +89,7 @@ func TestTransactItems(t *testing.T) { items := make([]*dynago.TransactPutItemsInput, 0, len(tc.newItems)) for _, item := range tc.newItems { items = append(items, &dynago.TransactPutItemsInput{ - PartitionKeyValue: dynago.StringValue(item.Pk), SortKeyValue: dynago.StringValue(item.Sk), Item: item, + dynago.StringValue(item.Pk), dynago.StringValue(item.Sk), item, }) } err := table.TransactPutItems(ctx, items) @@ -98,9 +98,8 @@ func TestTransactItems(t *testing.T) { } } //perform operations - if len(tc.operations) > 0 { - err := table.TransactItems(ctx, tc.operations...) + err := table.TransactItems(ctx, tc.operations) if err != nil { t.Fatalf("error occurred %s", err) } From 2a9f50cb068846b39cfa30c3f53fb63d2df0ec86 Mon Sep 17 00:00:00 2001 From: vankongv Date: Tue, 10 Sep 2024 16:00:32 +1000 Subject: [PATCH 10/11] chore: revert --- tests/transact_items_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/transact_items_test.go b/tests/transact_items_test.go index 7777057..8be681a 100644 --- a/tests/transact_items_test.go +++ b/tests/transact_items_test.go @@ -99,7 +99,7 @@ func TestTransactItems(t *testing.T) { } //perform operations if len(tc.operations) > 0 { - err := table.TransactItems(ctx, tc.operations) + err := table.TransactItems(ctx, tc.operations...) if err != nil { t.Fatalf("error occurred %s", err) } From 44042fc484ff29df9c01e59aa3a02bea571f52ca Mon Sep 17 00:00:00 2001 From: vankongv Date: Tue, 10 Sep 2024 16:02:12 +1000 Subject: [PATCH 11/11] chore: string input --- interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface.go b/interface.go index a69faa1..71bfd3b 100644 --- a/interface.go +++ b/interface.go @@ -25,7 +25,7 @@ type WriteAPI interface { // Create or update given item in DynamoDB. Must implemenmt DynamoRecord interface. // DynamoRecord.GetKeys will be called to get values for parition and sort keys. PutItem(ctx context.Context, pk Attribute, sk Attribute, item interface{}) error - DeleteItem(ctx context.Context, pk Attribute, sk Attribute) error + DeleteItem(ctx context.Context, pk string, sk string) error } type TransactionAPI interface {