generated from mrz1836/go-template
-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathadmin_api.go
440 lines (384 loc) · 20.1 KB
/
admin_api.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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
package spvwallet
import (
"context"
"fmt"
"net/url"
"github.com/bitcoin-sv/spv-wallet-go-client/commands"
"github.com/bitcoin-sv/spv-wallet-go-client/config"
"github.com/bitcoin-sv/spv-wallet-go-client/internal/api/v1/admin/accesskeys"
"github.com/bitcoin-sv/spv-wallet-go-client/internal/api/v1/admin/contacts"
"github.com/bitcoin-sv/spv-wallet-go-client/internal/api/v1/admin/invitations"
"github.com/bitcoin-sv/spv-wallet-go-client/internal/api/v1/admin/paymails"
"github.com/bitcoin-sv/spv-wallet-go-client/internal/api/v1/admin/stats"
"github.com/bitcoin-sv/spv-wallet-go-client/internal/api/v1/admin/status"
"github.com/bitcoin-sv/spv-wallet-go-client/internal/api/v1/admin/transactions"
"github.com/bitcoin-sv/spv-wallet-go-client/internal/api/v1/admin/utxos"
"github.com/bitcoin-sv/spv-wallet-go-client/internal/api/v1/admin/webhooks"
"github.com/bitcoin-sv/spv-wallet-go-client/internal/api/v1/admin/xpubs"
"github.com/bitcoin-sv/spv-wallet-go-client/internal/api/v1/configs"
"github.com/bitcoin-sv/spv-wallet-go-client/internal/api/v1/errutil"
"github.com/bitcoin-sv/spv-wallet-go-client/internal/auth"
"github.com/bitcoin-sv/spv-wallet-go-client/internal/constants"
"github.com/bitcoin-sv/spv-wallet-go-client/internal/restyutil"
"github.com/bitcoin-sv/spv-wallet-go-client/notifications"
"github.com/bitcoin-sv/spv-wallet-go-client/queries"
"github.com/bitcoin-sv/spv-wallet/models"
"github.com/bitcoin-sv/spv-wallet/models/filter"
"github.com/bitcoin-sv/spv-wallet/models/response"
)
// AdminAPI provides a simplified interface for interacting with admin-related APIs.
// It abstracts the complexities of making HTTP requests and handling responses,
// allowing developers to easily interact with admin API endpoints.
//
// A zero-value AdminAPI is not usable. Use the NewAdminAPI function to create
// a properly initialized instance.
//
// Methods may return wrapped errors, including models.SPVError or
// ErrUnrecognizedAPIResponse, depending on the behavior of the SPV Wallet API.
type AdminAPI struct {
configsAPI *configs.API
xpubsAPI *xpubs.API
paymailsAPI *paymails.API
accessKeyAPI *accesskeys.API
transactionsAPI *transactions.API
utxosAPI *utxos.API
contactsAPI *contacts.API
invitationsAPI *invitations.API
webhooksAPI *webhooks.API
statusAPI *status.API
statsAPI *stats.API
}
// SharedConfig retrieves the shared configuration via the configurations API.
// The response is unmarshaled into a response.SharedConfig.
// Returns an error if the request fails or the response cannot be decoded.
func (a *AdminAPI) SharedConfig(ctx context.Context) (*response.SharedConfig, error) {
res, err := a.configsAPI.SharedConfig(ctx)
if err != nil {
return nil, errutil.NewHTTPErrorFormatter(constants.AdminSharedConfigAPI, "retrieve shared configuration", err).FormatGetErr()
}
return res, nil
}
// CreateXPub creates a new XPub record via the Admin XPubs API.
// The provided command contains the necessary parameters to define the XPub record.
//
// The API response is unmarshaled into a *response.Xpub struct.
// Returns an error if the API request fails or the response cannot be decoded.
func (a *AdminAPI) CreateXPub(ctx context.Context, cmd *commands.CreateUserXpub) (*response.Xpub, error) {
res, err := a.xpubsAPI.CreateXPub(ctx, cmd)
if err != nil {
return nil, errutil.NewHTTPErrorFormatter(constants.AdminXPubsAPI, "create XPub", err).FormatPostErr()
}
return res, nil
}
// XPubs retrieves a paginated list of user XPubs via the Admin XPubs API.
// The response includes user XPubs along with pagination metadata, such as
// the current page number, sort order, and the field used for sorting (sortBy).
//
// Query parameters can be configured using optional query options. These options allow
// filtering based on metadata, pagination settings, or specific XPub attributes.
//
// The API response is unmarshaled into a *queries.XPubPage struct.
// Returns an error if the API request fails or the response cannot be decoded.
func (a *AdminAPI) XPubs(ctx context.Context, opts ...queries.QueryOption[filter.XpubFilter]) (*queries.XPubPage, error) {
res, err := a.xpubsAPI.XPubs(ctx, opts...)
if err != nil {
return nil, errutil.NewHTTPErrorFormatter(constants.AdminXPubsAPI, "retrieve XPubs page", err).FormatGetErr()
}
return res, nil
}
// CreateContact creates a new contact record via the Admin Contacts API.
// It accepts a command containing the necessary parameters to define the contact record,
// such as the creator's paymail, contact's full name, paymail and any associated metadata.
//
// The method sends a request to the Contacts API to create the contact, and the API
// response is unmarshaled into a *response.Contact struct, representing the newly created contact.
//
// If the API request fails or the response cannot be decoded, an error is returned. The error is
// wrapped with additional context to assist in troubleshooting.
func (a *AdminAPI) CreateContact(ctx context.Context, cmd *commands.CreateContact) (*response.Contact, error) {
res, err := a.contactsAPI.CreateContact(ctx, cmd)
if err != nil {
return nil, errutil.NewHTTPErrorFormatter(constants.AdminContactsAPI, "create user contacts page", err).FormatPostErr()
}
return res, nil
}
// Contacts retrieves a paginated list of user contacts from the admin contacts API.
//
// The response includes contact data along with pagination details, such as the
// current page, sort order, and sortBy field. Optional query parameters can be
// provided using query options. The result is unmarshaled into a *queries.ContactsPage.
// Returns an error if the API request fails or the response cannot be decoded.
func (a *AdminAPI) Contacts(ctx context.Context, opts ...queries.QueryOption[filter.AdminContactFilter]) (*queries.ContactsPage, error) {
res, err := a.contactsAPI.Contacts(ctx, opts...)
if err != nil {
return nil, errutil.NewHTTPErrorFormatter(constants.AdminContactsAPI, "retrieve user contacts page", err).FormatGetErr()
}
return res, nil
}
// ContactUpdate updates a user's contact information through the admin contacts API.
//
// This method uses the `UpdateContact` command to specify the details of the contact to update.
// It sends the update request to the API, unmarshals the response into a `*response.Contact`,
// and returns the updated contact. If the API request fails or the response cannot be decoded,
// an error is returned.
func (a *AdminAPI) ContactUpdate(ctx context.Context, cmd *commands.UpdateContact) (*response.Contact, error) {
res, err := a.contactsAPI.UpdateContact(ctx, cmd)
if err != nil {
msg := fmt.Sprintf("update contact with ID: %s", cmd.ID)
return nil, errutil.NewHTTPErrorFormatter(constants.AdminContactsAPI, msg, err).FormatPutErr()
}
return res, nil
}
// DeleteContact deletes a user contact with the given ID via the admin contacts API.
// Returns an error if the API request fails or the response cannot be decoded.
// A nil error indicates the deleting contact was successful.
func (a *AdminAPI) DeleteContact(ctx context.Context, ID string) error {
err := a.contactsAPI.DeleteContact(ctx, ID)
if err != nil {
msg := fmt.Sprintf("delete contact with ID: %s", ID)
return errutil.NewHTTPErrorFormatter(constants.AdminContactsAPI, msg, err).FormatDeleteErr()
}
return nil
}
// ConfirmContacts confirms contact between two users given their paymails in a request body.
// Returns an error if the API fails to confirm both contacts.
// A nil error indicates the confirmation was successful.
func (a *AdminAPI) ConfirmContacts(ctx context.Context, cmd *commands.ConfirmContacts) error {
err := a.contactsAPI.ConfirmContacts(ctx, cmd)
if err != nil {
msg := fmt.Sprintf("confirm contacts: %s & %s", cmd.PaymailA, cmd.PaymailB)
return errutil.NewHTTPErrorFormatter(constants.AdminContactsAPI, msg, err).FormatPostErr()
}
return nil
}
// AcceptInvitation processes and accepts a user contact invitation using the given ID via the admin invitations API.
// Returns an error if the API request fails. A nil error indicates the invitation was successfully accepted.
func (a *AdminAPI) AcceptInvitation(ctx context.Context, ID string) error {
err := a.invitationsAPI.AcceptInvitation(ctx, ID)
if err != nil {
msg := fmt.Sprintf("accept invitation with ID: %s", ID)
return errutil.NewHTTPErrorFormatter(constants.AdminInvitationsAPI, msg, err).FormatDeleteErr()
}
return nil
}
// RejectInvitation processes and rejects a user contact invitation using the given ID via the admin invitations API.
// Returns an error if the API request fails. A nil error indicates the invitation was successfully rejected.
func (a *AdminAPI) RejectInvitation(ctx context.Context, ID string) error {
err := a.invitationsAPI.RejectInvitation(ctx, ID)
if err != nil {
msg := fmt.Sprintf("delete invitation with ID: %s", ID)
return errutil.NewHTTPErrorFormatter(constants.AdminInvitationsAPI, msg, err).FormatDeleteErr()
}
return nil
}
// Transactions retrieves a paginated list of transactions via the Admin transactions API.
// The returned response includes transactions and pagination details, such as the page number,
// sort order, and sorting field (sortBy).
//
// This method allows optional query parameters to be applied via the provided query options.
// The response is expected to be to unmarshal into a *queries.TransactionPage struct.
// Returns an error if the request fails or the response cannot be decoded.
func (a *AdminAPI) Transactions(ctx context.Context, opts ...queries.QueryOption[filter.AdminTransactionFilter]) (*queries.TransactionPage, error) {
res, err := a.transactionsAPI.Transactions(ctx, opts...)
if err != nil {
return nil, errutil.NewHTTPErrorFormatter(constants.AdminTransactionsAPI, "retrieve transactions page", err).FormatGetErr()
}
return res, nil
}
// Transaction retrieves a specific transaction by its ID via the Admin transactions API.
// The response is expected to be unmarshaled into a *response.Transaction struct.
// Returns an error if the request fails or the response cannot be decoded.
func (a *AdminAPI) Transaction(ctx context.Context, ID string) (*response.Transaction, error) {
res, err := a.transactionsAPI.Transaction(ctx, ID)
if err != nil {
msg := fmt.Sprintf("retrieve a transaction with ID: %s", ID)
return nil, errutil.NewHTTPErrorFormatter(constants.AdminTransactionsAPI, msg, err).FormatGetErr()
}
return res, nil
}
// AccessKeys retrieves a paginated list of access keys via the Admin XPubs API.
// The response includes access keys and pagination details, such as the page number,
// sort order, and sorting field (sortBy).
//
// This method allows optional query parameters to be applied via the provided query options.
// The response is expected to unmarshal into a *queries.AccessKeyPage struct.
// Returns an error if the request fails or the response cannot be decoded.
func (a *AdminAPI) AccessKeys(ctx context.Context, opts ...queries.QueryOption[filter.AdminAccessKeyFilter]) (*queries.AccessKeyPage, error) {
res, err := a.accessKeyAPI.AccessKeys(ctx, opts...)
if err != nil {
return nil, errutil.NewHTTPErrorFormatter(constants.AdminAccessKeyAPI, "retrieve access keys page ", err).FormatGetErr()
}
return res, nil
}
// SubscribeWebhook registers a webhook subscription using the Admin Webhooks API.
// The provided command contains the parameters required to define the webhook subscription.
// Accepts context for controlling cancellation and timeout for the API request.
// The CreateWebhookSubscription command includes the webhook URL and authentication details.
// Returns a formatted error if the API request fails. A nil error indicates the webhook subscription was successful.
func (a *AdminAPI) SubscribeWebhook(ctx context.Context, cmd *commands.CreateWebhookSubscription) error {
err := a.webhooksAPI.SubscribeWebhook(ctx, cmd)
if err != nil {
msg := fmt.Sprintf("subscribe webhook URL address: %s", cmd.URL)
return errutil.NewHTTPErrorFormatter(constants.AdminWebhooksAPI, msg, err).FormatPostErr()
}
return nil
}
// UnsubscribeWebhook removes a webhook subscription using the Admin Webhooks API.
// Accepts the context for controlling cancellation and timeout for the API request.
// CancelWebhookSubscription command specifies the webhook URL to be unsubscribed.
// Returns a formatted error if the API request fails. A nil error indicates the webhook subscription was successfully deleted.
func (a *AdminAPI) UnsubscribeWebhook(ctx context.Context, cmd *commands.CancelWebhookSubscription) error {
err := a.webhooksAPI.UnsubscribeWebhook(ctx, cmd)
if err != nil {
msg := fmt.Sprintf("unsubscribe webhook URL address: %s", cmd.URL)
return errutil.NewHTTPErrorFormatter(constants.AdminWebhooksAPI, msg, err).FormatDeleteErr()
}
return nil
}
// GetAllWebhooks retrieves all webhook subscriptions using the Admin Webhooks API.
// Accepts the context for controlling cancellation and timeout for the API request.
// Returns a list of Webhook objects or an error if the API request fails.
func (a *AdminAPI) GetAllWebhooks(ctx context.Context) ([]*notifications.Webhook, error) {
webhooks, err := a.webhooksAPI.AdminGetAllWebhooks(ctx)
if err != nil {
return nil, errutil.NewHTTPErrorFormatter(constants.AdminWebhooksAPI, "get all webhooks", err).FormatGetErr()
}
return webhooks, nil
}
// UTXOs fetches a paginated list of UTXOs via the Admin XPubs API.
// The response includes UTXOs along with pagination details, such as page number,
// sort order, and sorting field.
//
// Optional query parameters can be applied using the provided query options.
// The response is unmarshaled into a *queries.UtxosPage struct.
// Returns an error if the request fails or the response cannot be decoded.
func (a *AdminAPI) UTXOs(ctx context.Context, opts ...queries.QueryOption[filter.AdminUtxoFilter]) (*queries.UtxosPage, error) {
res, err := a.utxosAPI.UTXOs(ctx, opts...)
if err != nil {
return nil, errutil.NewHTTPErrorFormatter(constants.AdminUtxosAPI, "retrieve utxos page ", err).FormatGetErr()
}
return res, nil
}
// Paymails retrieves a paginated list of paymail addresses via the Admin Paymails API.
// The response includes user paymails along with pagination metadata, such as
// the current page number, sort order, and the field used for sorting (sortBy).
//
// Query parameters can be configured using optional query options. These options allow
// filtering based on metadata, pagination settings, or specific paymail attributes.
//
// The API response is unmarshaled into a *queries.PaymailsPage struct.
// Returns an error if the API request fails or the response cannot be decoded.
func (a *AdminAPI) Paymails(ctx context.Context, opts ...queries.QueryOption[filter.AdminPaymailFilter]) (*queries.PaymailsPage, error) {
res, err := a.paymailsAPI.Paymails(ctx, opts...)
if err != nil {
return nil, errutil.NewHTTPErrorFormatter(constants.AdminPaymailAPI, "retrieve paymail addresses page", err).FormatGetErr()
}
return res, nil
}
// Paymail retrieves the paymail address associated with the specified ID via the Admin Paymails API.
// The response is expected to be unmarshaled into a *response.PaymailAddress struct.
// Returns an error if the request fails or the response cannot be decoded.
func (a *AdminAPI) Paymail(ctx context.Context, ID string) (*response.PaymailAddress, error) {
res, err := a.paymailsAPI.Paymail(ctx, ID)
if err != nil {
msg := fmt.Sprintf("retrieve paymail address with ID: %s", ID)
return nil, errutil.NewHTTPErrorFormatter(constants.AdminPaymailAPI, msg, err).FormatGetErr()
}
return res, nil
}
// CreatePaymail creates a new paymail address record via the Admin Paymails API.
// The provided command contains the necessary parameters to define the paymail address record.
//
// The API response is unmarshaled into a *response.Xpub PaymailAddress.
// Returns an error if the API request fails or the response cannot be decoded.
func (a *AdminAPI) CreatePaymail(ctx context.Context, cmd *commands.CreatePaymail) (*response.PaymailAddress, error) {
res, err := a.paymailsAPI.CreatePaymail(ctx, cmd)
if err != nil {
return nil, errutil.NewHTTPErrorFormatter(constants.AdminPaymailAPI, "create paymail address", err).FormatPostErr()
}
return res, nil
}
// DeletePaymail deletes a paymail address with via the Admin Paymails API.
// This function accepts an id, which corresponds to the database record id of the paymail to be deleted.
//
// It returns an error if the API request fails. A nil error indicates that the paymail
// was successfully deleted.
func (a *AdminAPI) DeletePaymail(ctx context.Context, id string) error {
err := a.paymailsAPI.DeletePaymail(ctx, id)
if err != nil {
msg := fmt.Sprintf("remove paymail address with id: %s", id)
return errutil.NewHTTPErrorFormatter(constants.AdminPaymailAPI, msg, err).FormatDeleteErr()
}
return nil
}
// Stats retrieves information about the login status via the Admin XPubs API.
// It accepts a context for controlling cancellation and timeout of the API request.
// The response is expected to be unmarshaled into a *models.AdminStats struct.
// A nil error with a valid response indicates the request was successful.
// Returns a formatted error if the API request fails.
func (a *AdminAPI) Stats(ctx context.Context) (*models.AdminStats, error) {
res, err := a.statsAPI.Stats(ctx)
if err != nil {
return nil, errutil.NewHTTPErrorFormatter(constants.AdminStatsAPI, "retrieve stats", err).FormatGetErr()
}
return res, nil
}
// Status retrieves information about the key type used during the authentication phase.
// If the key corresponds to the admin key, the method returns true with a nil error.
// Otherwise, it returns false with a nil error, indicating that the key used does not match
// the SPV Wallet API admin key. A non-nil error is returned if the API request fails.
func (a *AdminAPI) Status(ctx context.Context) (bool, error) {
ok, err := a.statusAPI.Status(ctx)
if err != nil {
return false, errutil.NewHTTPErrorFormatter(constants.AdminStatusAPI, "retrieve information about the used key type: %w", err).FormatGetErr()
}
return ok, nil
}
// NewAdminAPIWithXPriv initializes a new AdminAPI instance using an extended private key (xPriv).
// This function configures the API client with the provided configuration and uses the xPriv key for authentication.
// If any step fails, an appropriate error is returned.
//
// Note: Requests made with this instance will be securely signed.
func NewAdminAPIWithXPriv(cfg config.Config, xPriv string) (*AdminAPI, error) {
authenticator, err := auth.NewXprivAuthenticator(xPriv)
if err != nil {
return nil, fmt.Errorf("failed to initialize xPriv authenticator: %w", err)
}
return initAdminAPI(cfg, authenticator)
}
// NewAdminAPIWithXPub initializes a new AdminAPI instance using an extended public key (xPub).
// This function configures the API client with the provided configuration and uses the xPub key for authentication.
// If any configuration or initialization step fails, an appropriate error is returned.
//
// Note: Requests made with this instance will not be signed.
// For enhanced security, it is strongly recommended to use `NewAdminAPIWithXPriv` instead.
func NewAdminAPIWithXPub(cfg config.Config, xPub string) (*AdminAPI, error) {
authenticator, err := auth.NewXpubOnlyAuthenticator(xPub)
if err != nil {
return nil, fmt.Errorf("failed to initialize xPub authenticator: %w", err)
}
return initAdminAPI(cfg, authenticator)
}
func initAdminAPI(cfg config.Config, auth authenticator) (*AdminAPI, error) {
url, err := url.Parse(cfg.Addr)
if err != nil {
return nil, fmt.Errorf("failed to parse addr to url.URL: %w", err)
}
httpClient := restyutil.NewHTTPClient(cfg, auth)
if httpClient == nil {
return nil, fmt.Errorf("failed to initialize HTTP client - nil value")
}
return &AdminAPI{
configsAPI: configs.NewAPI(url, httpClient),
paymailsAPI: paymails.NewAPI(url, httpClient),
transactionsAPI: transactions.NewAPI(url, httpClient),
xpubsAPI: xpubs.NewAPI(url, httpClient),
utxosAPI: utxos.NewAPI(url, httpClient),
accessKeyAPI: accesskeys.NewAPI(url, httpClient),
webhooksAPI: webhooks.NewAPI(url, httpClient),
contactsAPI: contacts.NewAPI(url, httpClient),
invitationsAPI: invitations.NewAPI(url, httpClient),
statusAPI: status.NewAPI(url, httpClient),
statsAPI: stats.NewAPI(url, httpClient),
}, nil
}