diff --git a/actions/admin/paymail_addresses.go b/actions/admin/paymail_addresses.go index 2e0f26092..0aef63493 100644 --- a/actions/admin/paymail_addresses.go +++ b/actions/admin/paymail_addresses.go @@ -32,7 +32,7 @@ func (a *Action) paymailGetAddress(c *gin.Context) { } if requestBody.Address == "" { - c.JSON(http.StatusBadRequest, "address is required") + spverrors.ErrorResponse(c, spverrors.ErrMissingAddress, a.Services.Logger) return } @@ -157,7 +157,7 @@ func (a *Action) paymailCreateAddress(c *gin.Context) { paymailAddress, err := a.Services.SpvWalletEngine.NewPaymailAddress( c.Request.Context(), requestBody.Key, requestBody.Address, requestBody.PublicName, requestBody.Avatar, opts...) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) return } diff --git a/actions/methods.go b/actions/methods.go index 4208957d8..c41cde4ae 100644 --- a/actions/methods.go +++ b/actions/methods.go @@ -3,7 +3,7 @@ package actions import ( "net/http" - "github.com/bitcoin-sv/spv-wallet/dictionary" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/gin-gonic/gin" ) @@ -14,10 +14,10 @@ func StatusOK(c *gin.Context) { // NotFound handles all 404 requests func NotFound(c *gin.Context) { - c.JSON(http.StatusNotFound, dictionary.GetError(dictionary.ErrorRequestNotFound, c.Request.RequestURI)) + spverrors.ErrorResponse(c, spverrors.ErrRouteNotFound, nil) } // MethodNotAllowed handles all 405 requests func MethodNotAllowed(c *gin.Context) { - c.JSON(http.StatusMethodNotAllowed, dictionary.GetError(dictionary.ErrorMethodNotAllowed, c.Request.Method, c.Request.RequestURI)) + spverrors.ErrorResponse(c, spverrors.ErrRouteMethodNotAllowed, nil) } diff --git a/actions/utxos/search.go b/actions/utxos/search.go index 34662537d..76199c101 100644 --- a/actions/utxos/search.go +++ b/actions/utxos/search.go @@ -35,7 +35,7 @@ func (a *Action) search(c *gin.Context) { conditions, err := reqParams.Conditions.ToDbConditions() if err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrInvalidConditions, a.Services.Logger) return } diff --git a/actions/xpubs/update.go b/actions/xpubs/update.go index 40ec53431..902146d23 100644 --- a/actions/xpubs/update.go +++ b/actions/xpubs/update.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/bitcoin-sv/spv-wallet/engine" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" "github.com/bitcoin-sv/spv-wallet/mappings" "github.com/bitcoin-sv/spv-wallet/server/auth" "github.com/gin-gonic/gin" @@ -28,7 +29,7 @@ func (a *Action) update(c *gin.Context) { var requestBody engine.Metadata if err := c.Bind(&requestBody); err != nil { - c.JSON(http.StatusBadRequest, err.Error()) + spverrors.ErrorResponse(c, spverrors.ErrCannotBindRequest, a.Services.Logger) return } @@ -39,7 +40,8 @@ func (a *Action) update(c *gin.Context) { c.Request.Context(), reqXPubID, requestBody, ) if err != nil { - c.JSON(http.StatusInternalServerError, err.Error()) + spverrors.ErrorResponse(c, err, a.Services.Logger) + return } signed := c.GetBool("auth_signed") diff --git a/engine/action_xpub.go b/engine/action_xpub.go index 2c6ace53d..71bf1c1a5 100644 --- a/engine/action_xpub.go +++ b/engine/action_xpub.go @@ -2,8 +2,10 @@ package engine import ( "context" + "errors" "github.com/bitcoin-sv/spv-wallet/engine/datastore" + "github.com/bitcoin-sv/spv-wallet/engine/spverrors" ) // NewXpub will parse the xPub and save it into the Datastore @@ -14,8 +16,16 @@ func (c *Client) NewXpub(ctx context.Context, xPubKey string, opts ...ModelOps) // Check for existing NewRelic transaction ctx = c.GetOrStartTxn(ctx, "new_xpub") + // Check if the xpub already exists + xPub, err := getXpubWithCache(ctx, c, xPubKey, "", c.DefaultModelOptions()...) + if err != nil && !errors.Is(err, spverrors.ErrCouldNotFindXpub) { + return nil, err + } else if xPub != nil { + return nil, spverrors.ErrXPubAlreadyExists + } + // Create the model & set the default options (gives options from client->model) - xPub := newXpub( + xPub = newXpub( xPubKey, c.DefaultModelOptions(append(opts, New())...)..., ) diff --git a/engine/action_xpub_test.go b/engine/action_xpub_test.go index a1a5d3251..42bd4c494 100644 --- a/engine/action_xpub_test.go +++ b/engine/action_xpub_test.go @@ -71,7 +71,7 @@ func (ts *EmbeddedDBTestSuite) TestClient_NewXpub() { assert.ErrorIs(t, err, spverrors.ErrXpubInvalidLength) _, err = tc.client.NewXpub(tc.ctx, "", tc.client.DefaultModelOptions()...) - assert.ErrorIs(t, err, spverrors.ErrXpubInvalidLength) + assert.ErrorIs(t, err, spverrors.ErrMissingFieldXpubID) }) ts.T().Run(testCase.name+" - duplicate xPub", func(t *testing.T) { diff --git a/engine/spverrors/definitions.go b/engine/spverrors/definitions.go index 3fd63bf6b..3653ecd6a 100644 --- a/engine/spverrors/definitions.go +++ b/engine/spverrors/definitions.go @@ -294,6 +294,9 @@ var ErrXpubIDMisMatch = models.SPVError{Message: "xpub_id mismatch", StatusCode: // ////////////////////////////////// MISSING FIELDS +// ErrXPubAlreadyExists is when xpub already exists +var ErrXPubAlreadyExists = models.SPVError{Message: "xpub already exists", StatusCode: 409, Code: "error-xpub-already-exists"} + // ErrOneOfTheFieldsIsRequired is when all of required fields are missing var ErrOneOfTheFieldsIsRequired = models.SPVError{Message: "missing all of the fields, one of them is required", StatusCode: 400, Code: "error-missing-field-all-required"} @@ -345,3 +348,11 @@ var ErrWebhookSubscriptionNotFound = models.SPVError{Message: "webhook subscript // ErrNotificationsDisabled happens when the notifications are not enabled in the config var ErrNotificationsDisabled = models.SPVError{Message: "notifications are disabled", StatusCode: 404, Code: "error-notifications-disabled"} + +//////////////////////////////////// ROUTES ERRORS + +// ErrRouteNotFound is when route is not found +var ErrRouteNotFound = models.SPVError{Message: "route not found", StatusCode: 404, Code: "error-route-not-found"} + +// ErrRouteMethodNotAllowed is when route method is not allowed +var ErrRouteMethodNotAllowed = models.SPVError{Message: "method not allowed", StatusCode: 405, Code: "error-route-method-not-allowed"}