This repository has been archived by the owner on Apr 2, 2024. It is now read-only.
generated from mrz1836/go-template
-
-
Notifications
You must be signed in to change notification settings - Fork 7
/
model_save.go
145 lines (128 loc) · 4.01 KB
/
model_save.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
package bux
import (
"context"
"time"
"github.com/mrz1836/go-datastore"
"github.com/pkg/errors"
)
// Save will save the model(s) into the Datastore
func Save(ctx context.Context, model ModelInterface) (err error) {
// Check for a client
c := model.Client()
if c == nil {
return ErrMissingClient
}
// Check for a datastore
ds := c.Datastore()
if ds == nil {
return ErrDatastoreRequired
}
// Create new Datastore transaction
// @siggi: we need this to be in a callback context for Mongo
// NOTE: a DB error is not being returned from here
return ds.NewTx(ctx, func(tx *datastore.Transaction) (err error) {
parentBeforeHook := _beforeHook(model)
if err = parentBeforeHook(ctx); err != nil {
return _closeTxWithError(tx, err)
}
// Set the record's timestamps
model.SetRecordTime(model.IsNew())
// Start the list of models to Save
modelsToSave := append(make([]ModelInterface, 0), model)
// Add any child models (fire before hooks)
if children := model.ChildModels(); len(children) > 0 {
for _, child := range children {
childBeforeHook := _beforeHook(child)
if err = childBeforeHook(ctx); err != nil {
return _closeTxWithError(tx, err)
}
// Set the record's timestamps
child.SetRecordTime(child.IsNew())
}
// Add to list for saving
modelsToSave = append(modelsToSave, children...)
}
// Logs for saving models
model.Client().Logger().Debug().Msgf("saving %d models...", len(modelsToSave))
// Save all models (or fail!)
for index := range modelsToSave {
modelsToSave[index].Client().Logger().Debug().
Str("modelID", modelsToSave[index].GetID()).
Msgf("starting to save model: %s", modelsToSave[index].Name())
if err = modelsToSave[index].Client().Datastore().SaveModel(
ctx, modelsToSave[index], tx, modelsToSave[index].IsNew(), false,
); err != nil {
return _closeTxWithError(tx, err)
}
}
// Commit all the model(s) if needed
if tx.CanCommit() {
model.Client().Logger().Debug().Msg("committing db transaction...")
if err = tx.Commit(); err != nil {
return
}
}
// Fire after hooks (only on commit success)
var afterErr error
for index := range modelsToSave {
if modelsToSave[index].IsNew() {
modelsToSave[index].NotNew() // NOTE: calling it before this method... after created assumes it's been saved already
afterErr = modelsToSave[index].AfterCreated(ctx)
} else {
afterErr = modelsToSave[index].AfterUpdated(ctx)
}
if afterErr != nil {
if err == nil { // First error - set the error
err = afterErr
} else { // Got more than one error, wrap it!
err = errors.Wrap(err, afterErr.Error())
}
}
}
return
})
}
// saveToCache will save the model to the cache using the given key(s)
//
// ttl of 0 will cache forever
func saveToCache(ctx context.Context, keys []string, model ModelInterface, ttl time.Duration) error { //nolint:nolintlint,unparam // this does not matter
// NOTE: this check is in place in-case a model does not load its parent Client()
if model.Client() != nil {
for _, key := range keys {
if err := model.Client().Cachestore().SetModel(ctx, key, model, ttl); err != nil {
return err
}
}
} else {
model.Client().Logger().Debug().
Str("modelID", model.GetID()).
Msg("ignoring saveToCache: client or cachestore is missing")
}
return nil
}
// _closeTxWithError will close the transaction with the given error
// It's crucial to run this rollback to prevent hanging db connections.
func _closeTxWithError(tx *datastore.Transaction, baseError error) error {
if tx == nil {
if baseError != nil {
return baseError
}
return errors.New("transaction is nil during rollback")
}
if err := tx.Rollback(); err != nil {
if baseError != nil {
return errors.Wrap(baseError, err.Error())
}
return err
}
if baseError != nil {
return baseError
}
return errors.New("closing transaction with error")
}
func _beforeHook(model ModelInterface) func(context.Context) error {
if model.IsNew() {
return model.BeforeCreating
}
return model.BeforeUpdating
}