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
/
Copy pathdb_model_transactions.go
228 lines (193 loc) · 6.55 KB
/
db_model_transactions.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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
package bux
import (
"context"
"github.com/BuxOrg/bux/notifications"
"github.com/mrz1836/go-datastore"
)
// GetModelTableName will get the db table name of the current model
func (m *Transaction) GetModelTableName() string {
return tableTransactions
}
// Save will save the model into the Datastore
func (m *Transaction) Save(ctx context.Context) (err error) {
// Prepare the metadata
if len(m.Metadata) > 0 {
// set the metadata to be xpub specific, but only if we have a valid xpub ID
if m.XPubID != "" {
// was metadata set via opts ?
if m.XpubMetadata == nil {
m.XpubMetadata = make(XpubMetadata)
}
if _, ok := m.XpubMetadata[m.XPubID]; !ok {
m.XpubMetadata[m.XPubID] = make(Metadata)
}
for key, value := range m.Metadata {
m.XpubMetadata[m.XPubID][key] = value
}
} else {
m.Client().Logger().Debug().
Str("txID", m.ID).
Msg("xPub id is missing from transaction, cannot store metadata")
}
}
return Save(ctx, m)
}
// BeforeCreating will fire before the model is being inserted into the Datastore
func (m *Transaction) BeforeCreating(_ context.Context) error {
if m.beforeCreateCalled {
m.Client().Logger().Debug().
Str("txID", m.ID).
Msgf("skipping: %s BeforeCreating hook, because already called", m.Name())
return nil
}
m.Client().Logger().Debug().
Str("txID", m.ID).
Msgf("starting: %s BeforeCreating hook...", m.Name())
// Test for required field(s)
if len(m.Hex) == 0 {
return ErrMissingFieldHex
}
// Set the xPubID
m.setXPubID()
// Set the ID - will also parse and verify the tx
err := m.setID()
if err != nil {
return err
}
m.Client().Logger().Debug().
Str("txID", m.ID).
Msgf("end: %s BeforeCreating hook", m.Name())
m.beforeCreateCalled = true
return nil
}
// AfterCreated will fire after the model is created in the Datastore
func (m *Transaction) AfterCreated(ctx context.Context) error {
m.Client().Logger().Debug().
Str("txID", m.ID).
Msgf("starting: %s AfterCreated hook...", m.Name())
// Pre-build the options
opts := m.GetOptions(false)
// update the xpub balances
for xPubID, balance := range m.XpubOutputValue {
// todo: run this in a go routine? (move this into a function on the xpub model?)
xPub, err := getXpubWithCache(ctx, m.Client(), "", xPubID, opts...)
if err != nil {
return err
} else if xPub == nil {
return ErrMissingRequiredXpub
}
if err = xPub.incrementBalance(ctx, balance); err != nil {
return err
}
}
// Update the draft transaction, process broadcasting
// todo: go routine (however it's not working, panic in save for missing datastore)
if m.draftTransaction != nil {
m.draftTransaction.Status = DraftStatusComplete
m.draftTransaction.FinalTxID = m.ID
if err := m.draftTransaction.Save(ctx); err != nil {
return err
}
}
// Fire notifications (this is already in a go routine)
notify(notifications.EventTypeCreate, m)
m.Client().Logger().Debug().
Str("txID", m.ID).
Msgf("end: %s AfterCreated hook...", m.Name())
return nil
}
// AfterUpdated will fire after the model is updated in the Datastore
func (m *Transaction) AfterUpdated(_ context.Context) error {
m.Client().Logger().Debug().
Str("txID", m.ID).
Msgf("starting: %s AfterUpdated hook...", m.Name())
// Fire notifications (this is already in a go routine)
notify(notifications.EventTypeUpdate, m)
m.Client().Logger().Debug().
Str("txID", m.ID).
Msgf("end: %s AfterUpdated hook", m.Name())
return nil
}
// AfterDeleted will fire after the model is deleted in the Datastore
func (m *Transaction) AfterDeleted(_ context.Context) error {
m.Client().Logger().Debug().Msgf("starting: %s AfterDeleted hook...", m.Name())
// Fire notifications (this is already in a go routine)
notify(notifications.EventTypeDelete, m)
m.Client().Logger().Debug().Msgf("end: %s AfterDeleted hook", m.Name())
return nil
}
// ChildModels will get any related sub models
func (m *Transaction) ChildModels() (childModels []ModelInterface) {
// Add the UTXOs if found
for index := range m.utxos {
childModels = append(childModels, &m.utxos[index])
}
// Add the broadcast transaction record
if m.syncTransaction != nil {
childModels = append(childModels, m.syncTransaction)
}
return
}
// Migrate model specific migration on startup
func (m *Transaction) Migrate(client datastore.ClientInterface) error {
tableName := client.GetTableName(tableTransactions)
if client.Engine() == datastore.MySQL {
if err := m.migrateMySQL(client, tableName); err != nil {
return err
}
} else if client.Engine() == datastore.PostgreSQL {
if err := m.migratePostgreSQL(client, tableName); err != nil {
return err
}
}
return client.IndexMetadata(tableName, xPubMetadataField)
}
// migratePostgreSQL is specific migration SQL for Postgresql
func (m *Transaction) migratePostgreSQL(client datastore.ClientInterface, tableName string) error {
tx := client.Execute(`CREATE INDEX IF NOT EXISTS idx_` + tableName + `_xpub_in_ids ON ` +
tableName + ` USING gin (xpub_in_ids jsonb_ops)`)
if tx.Error != nil {
return tx.Error
}
if tx = client.Execute(`CREATE INDEX IF NOT EXISTS idx_` + tableName + `_xpub_out_ids ON ` +
tableName + ` USING gin (xpub_out_ids jsonb_ops)`); tx.Error != nil {
return tx.Error
}
return nil
}
// migrateMySQL is specific migration SQL for MySQL
func (m *Transaction) migrateMySQL(client datastore.ClientInterface, tableName string) error {
idxName := "idx_" + tableName + "_xpub_in_ids"
idxExists, err := client.IndexExists(tableName, idxName)
if err != nil {
return err
}
if !idxExists {
tx := client.Execute("ALTER TABLE `" + tableName + "`" +
" ADD INDEX " + idxName + " ( (CAST(xpub_in_ids AS CHAR(64) ARRAY)) )")
if tx.Error != nil {
m.Client().Logger().Error().Msg("failed creating json index on mysql: " + tx.Error.Error())
return nil //nolint:nolintlint,nilerr // error is not needed
}
}
idxName = "idx_" + tableName + "_xpub_out_ids"
if idxExists, err = client.IndexExists(
tableName, idxName,
); err != nil {
return err
}
if !idxExists {
tx := client.Execute("ALTER TABLE `" + tableName + "`" +
" ADD INDEX " + idxName + " ( (CAST(xpub_out_ids AS CHAR(64) ARRAY)) )")
if tx.Error != nil {
m.Client().Logger().Error().Msg("failed creating json index on mysql: " + tx.Error.Error())
return nil //nolint:nolintlint,nilerr // error is not needed
}
}
tx := client.Execute("ALTER TABLE `" + tableName + "` MODIFY COLUMN hex longtext")
if tx.Error != nil {
m.Client().Logger().Error().Msg("failed changing hex type to longtext in MySQL: " + tx.Error.Error())
return nil //nolint:nolintlint,nilerr // error is not needed
}
return nil
}