/
opUpdate.go
98 lines (84 loc) · 2.85 KB
/
opUpdate.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//
// Copyright (C) 2022 Dmitry Kolesnikov
//
// This file may be modified and distributed under the terms
// of the MIT license. See the LICENSE file for details.
// https://github.com/fogfish/dynamo
//
package ddb
import (
"context"
"strings"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
"github.com/fogfish/dynamo/v2"
)
// Update applies a partial patch to entity using update expression abstraction
func (db *Storage[T]) UpdateWith(ctx context.Context, expression UpdateItemExpression[T], opts ...interface{ ConditionExpression(T) }) (T, error) {
gen, err := db.codec.Encode(expression.entity)
if err != nil {
return db.undefined, errInvalidEntity.New(err)
}
req := expression.request
req.Key = db.codec.KeyOnly(gen)
req.TableName = db.table
req.ReturnValues = "ALL_NEW"
maybeUpdateConditionExpression(
&req.ConditionExpression,
req.ExpressionAttributeNames,
req.ExpressionAttributeValues,
opts,
)
return db.update(ctx, expression.entity, req)
}
// Update applies a partial patch to entity and returns new values
func (db *Storage[T]) Update(ctx context.Context, entity T, opts ...interface{ ConditionExpression(T) }) (T, error) {
gen, err := db.codec.Encode(entity)
if err != nil {
return db.undefined, errInvalidEntity.New(err)
}
names := map[string]string{}
values := map[string]types.AttributeValue{}
update := make([]string, 0)
for k, v := range gen {
if k != db.codec.pkPrefix && k != db.codec.skSuffix && k != "id" {
names["#__"+k+"__"] = k
values[":__"+k+"__"] = v
update = append(update, "#__"+k+"__="+":__"+k+"__")
}
}
expression := aws.String("SET " + strings.Join(update, ","))
req := &dynamodb.UpdateItemInput{
Key: db.codec.KeyOnly(gen),
ExpressionAttributeNames: names,
ExpressionAttributeValues: values,
UpdateExpression: expression,
TableName: db.table,
ReturnValues: "ALL_NEW",
}
maybeUpdateConditionExpression(
&req.ConditionExpression,
req.ExpressionAttributeNames,
req.ExpressionAttributeValues,
opts,
)
return db.update(ctx, entity, req)
}
func (db *Storage[T]) update(ctx context.Context, key dynamo.Thing, req *dynamodb.UpdateItemInput) (T, error) {
val, err := db.service.UpdateItem(ctx, req)
if err != nil {
if recoverConditionalCheckFailedException(err) {
return db.undefined, errPreConditionFailed(err, key,
strings.Contains(*req.ConditionExpression, "attribute_not_exists") || strings.Contains(*req.ConditionExpression, "="),
strings.Contains(*req.ConditionExpression, "attribute_exists") || strings.Contains(*req.ConditionExpression, "<>"),
)
}
return db.undefined, errServiceIO.New(err)
}
obj, err := db.codec.Decode(val.Attributes)
if err != nil {
return db.undefined, errInvalidEntity.New(err)
}
return obj, nil
}