/
tx.go
181 lines (159 loc) · 4.76 KB
/
tx.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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package ds
import (
"context"
"reflect"
"cloud.google.com/go/datastore"
)
// Tx is the datastore transaction wrapper
type Tx struct {
*datastore.Transaction
invalidateKeys []*datastore.Key
}
// RunInTx is the RunInTransaction wrapper
func (client *Client) RunInTx(ctx context.Context, f func(tx *Tx) error, opts ...datastore.TransactionOption) (*datastore.Commit, error) {
var tx *Tx
commit, err := client.RunInTransaction(ctx, func(t *datastore.Transaction) error {
tx = &Tx{t, nil}
return f(tx)
})
if err == nil && client.Cache != nil && len(tx.invalidateKeys) > 0 {
// find unique keys
mapKey := map[string]*datastore.Key{}
for _, key := range tx.invalidateKeys {
mapKey[key.String()] = key
}
uKeys := []*datastore.Key{}
for _, k := range mapKey {
uKeys = append(uKeys, k)
}
client.Cache.DelMulti(uKeys)
}
return commit, err
}
// GetByKey retrieves model from datastore by key
func (tx *Tx) GetByKey(key *datastore.Key, dst interface{}) error {
err := tx.Get(key, dst)
SetKey(key, dst)
if err != nil {
return err
}
return nil
}
// GetByKeys retrieves models from datastore by keys
func (tx *Tx) GetByKeys(keys []*datastore.Key, dst interface{}) error {
// prepare slice if dst is pointer to 0 len slice
if rf := reflect.ValueOf(dst); rf.Kind() == reflect.Ptr {
rs := rf.Elem()
if rs.Kind() == reflect.Slice && rs.Len() == 0 {
l := len(keys)
rs.Set(reflect.MakeSlice(rs.Type(), l, l))
}
dst = rs.Interface()
}
err := tx.GetMulti(keys, dst)
SetKeys(keys, dst)
if err != nil {
return err
}
return nil
}
// GetByModel retrieves model from datastore by key from model
func (tx *Tx) GetByModel(dst interface{}) error {
key := ExtractKey(dst)
return tx.GetByKey(key, dst)
}
// GetByModels retrieves models from datastore by keys from models
func (tx *Tx) GetByModels(dst interface{}) error {
keys := ExtractKeys(dst)
return tx.GetByKeys(keys, dst)
}
// GetByID retrieves model from datastore by id
func (tx *Tx) GetByID(kind string, id int64, dst interface{}) error {
return tx.GetByKey(datastore.IDKey(kind, id, nil), dst)
}
// GetByIDs retrieves models from datastore by ids
func (tx *Tx) GetByIDs(kind string, ids []int64, dst interface{}) error {
keys := BuildIDKeys(kind, ids)
return tx.GetByKeys(keys, dst)
}
// GetByStringID retrieves model from datastore by string id
func (tx *Tx) GetByStringID(kind string, id string, dst interface{}) error {
tid := parseID(id)
if tid == 0 {
return datastore.ErrInvalidKey
}
return tx.GetByKey(datastore.IDKey(kind, tid, nil), dst)
}
// GetByStringIDs retrieves models from datastore by string ids
func (tx *Tx) GetByStringIDs(kind string, ids []string, dst interface{}) error {
keys := BuildStringIDKeys(kind, ids)
return tx.GetByKeys(keys, dst)
}
// GetByName retrieves model from datastore by name
func (tx *Tx) GetByName(kind string, name string, dst interface{}) error {
return tx.GetByKey(datastore.NameKey(kind, name, nil), dst)
}
// GetByNames retrieves models from datastore by names
func (tx *Tx) GetByNames(kind string, names []string, dst interface{}) error {
keys := BuildNameKeys(kind, names)
return tx.GetByKeys(keys, dst)
}
// PutModel puts a model to datastore
func (tx *Tx) PutModel(src interface{}) (*datastore.PendingKey, error) {
x := src.(KeyGetSetter)
key := x.GetKey()
pKey, err := tx.Put(key, x)
if key != nil && !key.Incomplete() {
tx.invalidateKeys = append(tx.invalidateKeys, key)
}
return pKey, err
}
// PutModels puts models to datastore
func (tx *Tx) PutModels(src interface{}) ([]*datastore.PendingKey, error) {
xs := valueOf(src)
keys := make([]*datastore.Key, xs.Len())
for i := range keys {
x := xs.Index(i).Interface()
keys[i] = x.(KeyGetter).GetKey()
}
// TODO: store pending key inside model ?
ks, err := tx.PutMulti(keys, src)
for _, key := range keys {
if key != nil && !key.Incomplete() {
tx.invalidateKeys = append(tx.invalidateKeys, key)
}
}
return ks, err
}
// SaveModel saves model to datastore
func (tx *Tx) SaveModel(src interface{}) (*datastore.PendingKey, error) {
beforeSave(src)
return tx.PutModel(src)
}
// SaveModels saves models to datastore
func (tx *Tx) SaveModels(src interface{}) ([]*datastore.PendingKey, error) {
xs := valueOf(src)
for i := 0; i < xs.Len(); i++ {
x := xs.Index(i).Interface()
beforeSave(x)
}
return tx.PutModels(src)
}
// DeleteByKey deletes a model by key
func (tx *Tx) DeleteByKey(key *datastore.Key) error {
err := tx.Delete(key)
if key != nil && !key.Incomplete() {
tx.invalidateKeys = append(tx.invalidateKeys, key)
}
return err
}
// DeleteByKeys deletes models by keys
func (tx *Tx) DeleteByKeys(keys []*datastore.Key) error {
err := tx.DeleteMulti(keys)
for _, key := range keys {
if key != nil && !key.Incomplete() {
tx.invalidateKeys = append(tx.invalidateKeys, key)
}
}
return err
}