Skip to content

Commit b5c55a1

Browse files
revert #28 and add tests (#29)
* Revert "Skipping empty string in update expressions as it used to convert to … (#28)" This reverts commit 7fec125. * add tests
1 parent a7c61bf commit b5c55a1

7 files changed

Lines changed: 514 additions & 150 deletions

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.idea
22
vendor
33
Gopkg.*
4-
.coverage
4+
.coverage*
5+
coverage*

dynamo_repository.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,6 @@ func (repository Repository) UpdateWithContext(ctx context.Context, expression U
109109
}
110110

111111
for expr, value := range values {
112-
if isEmptyString(value) {
113-
continue
114-
}
115112
if expression == Add {
116113
update.Add(expr, value)
117114
}
@@ -162,9 +159,6 @@ func (repository Repository) prepareUpdateWithUpdateExpressions(
162159
expression := UpdateExpression(updateExpression)
163160

164161
for expr, value := range v {
165-
if isEmptyString(value) {
166-
continue
167-
}
168162
if expression == Add {
169163
update.Add(expr, value)
170164
}

helper.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@ func valueFromPtr[T any](ptr *T) T {
1313
return *ptr
1414
}
1515

16-
func isEmptyString(value any) bool {
17-
str, ok := value.(string)
18-
return ok && str == ""
19-
}
20-
2116
func buildTableKeyCondition(table dynamo.Table, key KeyInterface) *dynamo.Query {
2217
q := table.Get(*key.HashKeyName(), key.HashKey())
2318

key.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,5 @@ func isValidHashKey(key KeyInterface) error {
100100
if key.HashKey() == nil {
101101
return ErrInvalidHashKeyValue
102102
}
103-
if str, ok := key.HashKey().(string); ok && str == "" {
104-
return ErrInvalidHashKeyValue
105-
}
106103
return nil
107104
}

repository_batch_get_test.go

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
package djoemo_test
2+
3+
import (
4+
"context"
5+
"errors"
6+
7+
"github.com/adjoeio/djoemo"
8+
"github.com/adjoeio/djoemo/mock"
9+
"github.com/aws/aws-sdk-go/service/dynamodb"
10+
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
11+
. "github.com/onsi/ginkgo/v2"
12+
. "github.com/onsi/gomega"
13+
"go.uber.org/mock/gomock"
14+
)
15+
16+
var _ = Describe("Repository BatchGetItemsWithContext", func() {
17+
const UserTableName = "UserTable"
18+
19+
var (
20+
dAPIMock *mock.MockDynamoDBAPI
21+
repository djoemo.RepositoryInterface
22+
logMock *mock.MockLogInterface
23+
metricsMock *mock.MockMetricsInterface
24+
)
25+
26+
BeforeEach(func() {
27+
mockCtrl := gomock.NewController(GinkgoT())
28+
dAPIMock = mock.NewMockDynamoDBAPI(mockCtrl)
29+
logMock = mock.NewMockLogInterface(mockCtrl)
30+
metricsMock = mock.NewMockMetricsInterface(mockCtrl)
31+
repository = djoemo.NewRepository(dAPIMock)
32+
repository.WithLog(logMock)
33+
repository.WithMetrics(metricsMock)
34+
})
35+
36+
It("should return false and nil when keys slice is empty", func() {
37+
users := &[]User{}
38+
found, err := repository.BatchGetItemsWithContext(context.Background(), []djoemo.KeyInterface{}, users)
39+
Expect(err).To(BeNil())
40+
Expect(found).To(BeFalse())
41+
})
42+
43+
It("should return error when key has no table name", func() {
44+
invalidKey := djoemo.Key().WithHashKeyName("UUID").WithHashKey("uuid")
45+
metricsMock.EXPECT().Record(gomock.Any(), djoemo.OpRead, invalidKey, gomock.Any(), false)
46+
47+
users := &[]User{}
48+
found, err := repository.BatchGetItemsWithContext(
49+
context.Background(),
50+
[]djoemo.KeyInterface{invalidKey},
51+
users,
52+
)
53+
Expect(err).To(Equal(djoemo.ErrInvalidTableName))
54+
Expect(found).To(BeFalse())
55+
})
56+
57+
It("should return error when keys refer to different tables", func() {
58+
key1 := djoemo.Key().WithTableName(UserTableName).
59+
WithHashKeyName("UUID").WithHashKey("uuid1")
60+
key2 := djoemo.Key().WithTableName("OtherTable").
61+
WithHashKeyName("UUID").WithHashKey("uuid2")
62+
63+
// Note: success flag captured from outer err var which isn't
64+
// updated for ErrInvalidBatchRequest return path.
65+
metricsMock.EXPECT().Record(gomock.Any(), djoemo.OpRead, gomock.Any(), gomock.Any(), gomock.Any()).Times(2)
66+
67+
users := &[]User{}
68+
found, err := repository.BatchGetItemsWithContext(
69+
context.Background(),
70+
[]djoemo.KeyInterface{key1, key2},
71+
users,
72+
)
73+
Expect(err).To(Equal(djoemo.ErrInvalidBatchRequest))
74+
Expect(found).To(BeFalse())
75+
})
76+
77+
It("should return items when batch get succeeds by hash key", func() {
78+
key1 := djoemo.Key().WithTableName(UserTableName).
79+
WithHashKeyName("UUID").WithHashKey("uuid1")
80+
key2 := djoemo.Key().WithTableName(UserTableName).
81+
WithHashKeyName("UUID").WithHashKey("uuid2")
82+
83+
items := []map[string]*dynamodb.AttributeValue{}
84+
for _, u := range []map[string]interface{}{
85+
{"UUID": "uuid1", "UserName": "name1"},
86+
{"UUID": "uuid2", "UserName": "name2"},
87+
} {
88+
av, _ := dynamodbattribute.MarshalMap(u)
89+
items = append(items, av)
90+
}
91+
output := &dynamodb.BatchGetItemOutput{
92+
Responses: map[string][]map[string]*dynamodb.AttributeValue{
93+
UserTableName: items,
94+
},
95+
}
96+
97+
dAPIMock.EXPECT().
98+
BatchGetItemWithContext(gomock.Any(), gomock.Any()).
99+
Return(output, nil).
100+
AnyTimes()
101+
102+
metricsMock.EXPECT().Record(gomock.Any(), djoemo.OpRead, key1, gomock.Any(), true)
103+
metricsMock.EXPECT().Record(gomock.Any(), djoemo.OpRead, key2, gomock.Any(), true)
104+
105+
users := &[]User{}
106+
found, err := repository.BatchGetItemsWithContext(
107+
context.Background(),
108+
[]djoemo.KeyInterface{key1, key2},
109+
users,
110+
)
111+
Expect(err).To(BeNil())
112+
Expect(found).To(BeTrue())
113+
Expect(len(*users)).To(Equal(2))
114+
})
115+
116+
It("should handle hash and range keys", func() {
117+
key1 := djoemo.Key().WithTableName(UserTableName).
118+
WithHashKeyName("UUID").WithHashKey("uuid1").
119+
WithRangeKeyName("Email").WithRangeKey("a@adjoe.io")
120+
key2 := djoemo.Key().WithTableName(UserTableName).
121+
WithHashKeyName("UUID").WithHashKey("uuid2").
122+
WithRangeKeyName("Email").WithRangeKey("b@adjoe.io")
123+
124+
items := []map[string]*dynamodb.AttributeValue{}
125+
for _, u := range []map[string]interface{}{
126+
{"UUID": "uuid1", "Email": "a@adjoe.io"},
127+
{"UUID": "uuid2", "Email": "b@adjoe.io"},
128+
} {
129+
av, _ := dynamodbattribute.MarshalMap(u)
130+
items = append(items, av)
131+
}
132+
output := &dynamodb.BatchGetItemOutput{
133+
Responses: map[string][]map[string]*dynamodb.AttributeValue{
134+
UserTableName: items,
135+
},
136+
}
137+
138+
dAPIMock.EXPECT().
139+
BatchGetItemWithContext(gomock.Any(), gomock.Any()).
140+
Return(output, nil).
141+
AnyTimes()
142+
143+
metricsMock.EXPECT().Record(gomock.Any(), djoemo.OpRead, key1, gomock.Any(), true)
144+
metricsMock.EXPECT().Record(gomock.Any(), djoemo.OpRead, key2, gomock.Any(), true)
145+
146+
profiles := &[]Profile{}
147+
found, err := repository.BatchGetItemsWithContext(
148+
context.Background(),
149+
[]djoemo.KeyInterface{key1, key2},
150+
profiles,
151+
)
152+
Expect(err).To(BeNil())
153+
Expect(found).To(BeTrue())
154+
Expect(len(*profiles)).To(Equal(2))
155+
})
156+
157+
It("should return false and nil when dynamo returns ErrNotFound", func() {
158+
key := djoemo.Key().WithTableName(UserTableName).
159+
WithHashKeyName("UUID").WithHashKey("uuid")
160+
161+
output := &dynamodb.BatchGetItemOutput{
162+
Responses: map[string][]map[string]*dynamodb.AttributeValue{
163+
UserTableName: {},
164+
},
165+
}
166+
167+
dAPIMock.EXPECT().
168+
BatchGetItemWithContext(gomock.Any(), gomock.Any()).
169+
Return(output, nil).
170+
AnyTimes()
171+
172+
metricsMock.EXPECT().Record(gomock.Any(), djoemo.OpRead, key, gomock.Any(), true)
173+
logMock.EXPECT().WithContext(gomock.Any()).Return(logMock)
174+
logMock.EXPECT().WithField(djoemo.TableName, UserTableName).Return(logMock)
175+
logMock.EXPECT().Info(djoemo.ErrNoItemFound.Error())
176+
177+
users := &[]User{}
178+
found, err := repository.BatchGetItemsWithContext(
179+
context.Background(),
180+
[]djoemo.KeyInterface{key},
181+
users,
182+
)
183+
Expect(err).To(BeNil())
184+
Expect(found).To(BeFalse())
185+
})
186+
187+
It("should return false and error when dynamo returns an error", func() {
188+
key := djoemo.Key().WithTableName(UserTableName).
189+
WithHashKeyName("UUID").WithHashKey("uuid")
190+
191+
dbErr := errors.New("some dynamo error")
192+
193+
dAPIMock.EXPECT().
194+
BatchGetItemWithContext(gomock.Any(), gomock.Any()).
195+
Return(nil, dbErr).
196+
AnyTimes()
197+
198+
metricsMock.EXPECT().Record(gomock.Any(), djoemo.OpRead, key, gomock.Any(), false)
199+
200+
users := &[]User{}
201+
found, err := repository.BatchGetItemsWithContext(
202+
context.Background(),
203+
[]djoemo.KeyInterface{key},
204+
users,
205+
)
206+
Expect(err).To(HaveOccurred())
207+
Expect(found).To(BeFalse())
208+
})
209+
})

0 commit comments

Comments
 (0)