mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-06 14:16:43 +00:00
[NOD-398] Change API server type HandlerError to work with errors instead of error strings (#454)
* [NOD-398] Change API server type HandlerError to work with errors instead of error strings * [NOD-398] Rename OriginalError -> Cause and isHandleError -> ok
This commit is contained in:
parent
3cc6f2d648
commit
a9ff9b0e70
@ -2,10 +2,10 @@ package controllers
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/daglabs/btcd/apiserver/apimodels"
|
||||
"github.com/daglabs/btcd/apiserver/dbmodels"
|
||||
"github.com/daglabs/btcd/httpserverutils"
|
||||
"github.com/pkg/errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/daglabs/btcd/apiserver/database"
|
||||
@ -27,38 +27,38 @@ const (
|
||||
const maxGetBlocksLimit = 100
|
||||
|
||||
// GetBlockByHashHandler returns a block by a given hash.
|
||||
func GetBlockByHashHandler(blockHash string) (interface{}, *httpserverutils.HandlerError) {
|
||||
func GetBlockByHashHandler(blockHash string) (interface{}, error) {
|
||||
if bytes, err := hex.DecodeString(blockHash); err != nil || len(bytes) != daghash.HashSize {
|
||||
return nil, httpserverutils.NewHandlerError(http.StatusUnprocessableEntity,
|
||||
fmt.Sprintf("The given block hash is not a hex-encoded %d-byte hash.", daghash.HashSize))
|
||||
errors.Errorf("The given block hash is not a hex-encoded %d-byte hash.", daghash.HashSize))
|
||||
}
|
||||
|
||||
db, err := database.DB()
|
||||
if err != nil {
|
||||
return nil, httpserverutils.NewInternalServerHandlerError(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block := &dbmodels.Block{}
|
||||
dbResult := db.Where(&dbmodels.Block{BlockHash: blockHash}).Preload("AcceptingBlock").First(block)
|
||||
dbErrors := dbResult.GetErrors()
|
||||
if httpserverutils.IsDBRecordNotFoundError(dbErrors) {
|
||||
return nil, httpserverutils.NewHandlerError(http.StatusNotFound, "No block with the given block hash was found.")
|
||||
return nil, httpserverutils.NewHandlerError(http.StatusNotFound, errors.New("No block with the given block hash was found"))
|
||||
}
|
||||
if httpserverutils.HasDBError(dbErrors) {
|
||||
return nil, httpserverutils.NewHandlerErrorFromDBErrors("Some errors were encountered when loading transactions from the database:", dbResult.GetErrors())
|
||||
return nil, httpserverutils.NewErrorFromDBErrors("Some errors were encountered when loading transactions from the database:", dbResult.GetErrors())
|
||||
}
|
||||
return convertBlockModelToBlockResponse(block), nil
|
||||
}
|
||||
|
||||
// GetBlocksHandler searches for all blocks
|
||||
func GetBlocksHandler(order string, skip uint64, limit uint64) (interface{}, *httpserverutils.HandlerError) {
|
||||
func GetBlocksHandler(order string, skip uint64, limit uint64) (interface{}, error) {
|
||||
if limit > maxGetBlocksLimit {
|
||||
return nil, httpserverutils.NewHandlerError(http.StatusUnprocessableEntity, fmt.Sprintf("The maximum allowed value for the limit is %d", maxGetTransactionsLimit))
|
||||
return nil, httpserverutils.NewHandlerError(http.StatusUnprocessableEntity, errors.Errorf("The maximum allowed value for the limit is %d", maxGetTransactionsLimit))
|
||||
}
|
||||
blocks := []*dbmodels.Block{}
|
||||
db, err := database.DB()
|
||||
if err != nil {
|
||||
return nil, httpserverutils.NewHandlerError(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
return nil, err
|
||||
}
|
||||
query := db.
|
||||
Limit(limit).
|
||||
@ -69,7 +69,7 @@ func GetBlocksHandler(order string, skip uint64, limit uint64) (interface{}, *ht
|
||||
} else if order == OrderDescending {
|
||||
query = query.Order("`id` DESC")
|
||||
} else {
|
||||
return nil, httpserverutils.NewHandlerError(http.StatusUnprocessableEntity, fmt.Sprintf("'%s' is not a valid order", order))
|
||||
return nil, httpserverutils.NewHandlerError(http.StatusUnprocessableEntity, errors.Errorf("'%s' is not a valid order", order))
|
||||
}
|
||||
query.Find(&blocks)
|
||||
blockResponses := make([]*apimodels.BlockResponse, len(blocks))
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"github.com/daglabs/btcd/blockdag"
|
||||
"github.com/daglabs/btcd/httpserverutils"
|
||||
"github.com/daglabs/btcd/util/subnetworkid"
|
||||
"github.com/pkg/errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/daglabs/btcd/apiserver/database"
|
||||
@ -23,15 +24,15 @@ import (
|
||||
const maxGetTransactionsLimit = 1000
|
||||
|
||||
// GetTransactionByIDHandler returns a transaction by a given transaction ID.
|
||||
func GetTransactionByIDHandler(txID string) (interface{}, *httpserverutils.HandlerError) {
|
||||
func GetTransactionByIDHandler(txID string) (interface{}, error) {
|
||||
if bytes, err := hex.DecodeString(txID); err != nil || len(bytes) != daghash.TxIDSize {
|
||||
return nil, httpserverutils.NewHandlerError(http.StatusUnprocessableEntity,
|
||||
fmt.Sprintf("The given txid is not a hex-encoded %d-byte hash.", daghash.TxIDSize))
|
||||
errors.Errorf("The given txid is not a hex-encoded %d-byte hash.", daghash.TxIDSize))
|
||||
}
|
||||
|
||||
db, err := database.DB()
|
||||
if err != nil {
|
||||
return nil, httpserverutils.NewInternalServerHandlerError(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tx := &dbmodels.Transaction{}
|
||||
@ -39,24 +40,24 @@ func GetTransactionByIDHandler(txID string) (interface{}, *httpserverutils.Handl
|
||||
dbResult := addTxPreloadedFields(query).First(&tx)
|
||||
dbErrors := dbResult.GetErrors()
|
||||
if httpserverutils.IsDBRecordNotFoundError(dbErrors) {
|
||||
return nil, httpserverutils.NewHandlerError(http.StatusNotFound, "No transaction with the given txid was found.")
|
||||
return nil, httpserverutils.NewHandlerError(http.StatusNotFound, errors.New("No transaction with the given txid was found"))
|
||||
}
|
||||
if httpserverutils.HasDBError(dbErrors) {
|
||||
return nil, httpserverutils.NewHandlerErrorFromDBErrors("Some errors were encountered when loading transaction from the database:", dbErrors)
|
||||
return nil, httpserverutils.NewErrorFromDBErrors("Some errors were encountered when loading transaction from the database:", dbErrors)
|
||||
}
|
||||
return convertTxDBModelToTxResponse(tx), nil
|
||||
}
|
||||
|
||||
// GetTransactionByHashHandler returns a transaction by a given transaction hash.
|
||||
func GetTransactionByHashHandler(txHash string) (interface{}, *httpserverutils.HandlerError) {
|
||||
func GetTransactionByHashHandler(txHash string) (interface{}, error) {
|
||||
if bytes, err := hex.DecodeString(txHash); err != nil || len(bytes) != daghash.HashSize {
|
||||
return nil, httpserverutils.NewHandlerError(http.StatusUnprocessableEntity,
|
||||
fmt.Sprintf("The given txhash is not a hex-encoded %d-byte hash.", daghash.HashSize))
|
||||
errors.Errorf("The given txhash is not a hex-encoded %d-byte hash.", daghash.HashSize))
|
||||
}
|
||||
|
||||
db, err := database.DB()
|
||||
if err != nil {
|
||||
return nil, httpserverutils.NewHandlerError(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tx := &dbmodels.Transaction{}
|
||||
@ -64,25 +65,25 @@ func GetTransactionByHashHandler(txHash string) (interface{}, *httpserverutils.H
|
||||
dbResult := addTxPreloadedFields(query).First(&tx)
|
||||
dbErrors := dbResult.GetErrors()
|
||||
if httpserverutils.IsDBRecordNotFoundError(dbErrors) {
|
||||
return nil, httpserverutils.NewHandlerError(http.StatusNotFound, "No transaction with the given txhash was found.")
|
||||
return nil, httpserverutils.NewHandlerError(http.StatusNotFound, errors.Errorf("No transaction with the given txhash was found."))
|
||||
}
|
||||
if httpserverutils.HasDBError(dbErrors) {
|
||||
return nil, httpserverutils.NewHandlerErrorFromDBErrors("Some errors were encountered when loading transaction from the database:", dbErrors)
|
||||
return nil, httpserverutils.NewErrorFromDBErrors("Some errors were encountered when loading transaction from the database:", dbErrors)
|
||||
}
|
||||
return convertTxDBModelToTxResponse(tx), nil
|
||||
}
|
||||
|
||||
// GetTransactionsByAddressHandler searches for all transactions
|
||||
// where the given address is either an input or an output.
|
||||
func GetTransactionsByAddressHandler(address string, skip uint64, limit uint64) (interface{}, *httpserverutils.HandlerError) {
|
||||
func GetTransactionsByAddressHandler(address string, skip uint64, limit uint64) (interface{}, error) {
|
||||
if limit > maxGetTransactionsLimit {
|
||||
return nil, httpserverutils.NewHandlerError(http.StatusUnprocessableEntity,
|
||||
fmt.Sprintf("The maximum allowed value for the limit is %d", maxGetTransactionsLimit))
|
||||
errors.Errorf("The maximum allowed value for the limit is %d", maxGetTransactionsLimit))
|
||||
}
|
||||
|
||||
db, err := database.DB()
|
||||
if err != nil {
|
||||
return nil, httpserverutils.NewHandlerError(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txs := []*dbmodels.Transaction{}
|
||||
@ -90,7 +91,7 @@ func GetTransactionsByAddressHandler(address string, skip uint64, limit uint64)
|
||||
Joins("LEFT JOIN `transaction_outputs` ON `transaction_outputs`.`transaction_id` = `transactions`.`id`").
|
||||
Joins("LEFT JOIN `addresses` AS `out_addresses` ON `out_addresses`.`id` = `transaction_outputs`.`address_id`").
|
||||
Joins("LEFT JOIN `transaction_inputs` ON `transaction_inputs`.`transaction_id` = `transactions`.`id`").
|
||||
Joins("LEFT JOIN `transaction_outputs` AS `inputs_outs` ON `inputs_outs`.`id` = `transaction_inputs`.`transaction_output_id`").
|
||||
Joins("LEFT JOIN `transaction_outputs` AS `inputs_outs` ON `inputs_outs`.`id` = `transaction_inputs`.`previous_transaction_output_id`").
|
||||
Joins("LEFT JOIN `addresses` AS `in_addresses` ON `in_addresses`.`id` = `inputs_outs`.`address_id`").
|
||||
Where("`out_addresses`.`address` = ?", address).
|
||||
Or("`in_addresses`.`address` = ?", address).
|
||||
@ -100,7 +101,7 @@ func GetTransactionsByAddressHandler(address string, skip uint64, limit uint64)
|
||||
dbResult := addTxPreloadedFields(query).Find(&txs)
|
||||
dbErrors := dbResult.GetErrors()
|
||||
if httpserverutils.HasDBError(dbErrors) {
|
||||
return nil, httpserverutils.NewHandlerErrorFromDBErrors("Some errors were encountered when loading transactions from the database:", dbErrors)
|
||||
return nil, httpserverutils.NewErrorFromDBErrors("Some errors were encountered when loading transactions from the database:", dbErrors)
|
||||
}
|
||||
txResponses := make([]*apimodels.TransactionResponse, len(txs))
|
||||
for i, tx := range txs {
|
||||
@ -109,10 +110,10 @@ func GetTransactionsByAddressHandler(address string, skip uint64, limit uint64)
|
||||
return txResponses, nil
|
||||
}
|
||||
|
||||
func fetchSelectedTipBlueScore() (uint64, *httpserverutils.HandlerError) {
|
||||
func fetchSelectedTipBlueScore() (uint64, error) {
|
||||
db, err := database.DB()
|
||||
if err != nil {
|
||||
return 0, httpserverutils.NewInternalServerHandlerError(err.Error())
|
||||
return 0, err
|
||||
}
|
||||
block := &dbmodels.Block{}
|
||||
dbResult := db.Order("blue_score DESC").
|
||||
@ -121,16 +122,16 @@ func fetchSelectedTipBlueScore() (uint64, *httpserverutils.HandlerError) {
|
||||
First(block)
|
||||
dbErrors := dbResult.GetErrors()
|
||||
if httpserverutils.HasDBError(dbErrors) {
|
||||
return 0, httpserverutils.NewHandlerErrorFromDBErrors("Some errors were encountered when loading transactions from the database:", dbErrors)
|
||||
return 0, httpserverutils.NewErrorFromDBErrors("Some errors were encountered when loading transactions from the database:", dbErrors)
|
||||
}
|
||||
return block.BlueScore, nil
|
||||
}
|
||||
|
||||
// GetUTXOsByAddressHandler searches for all UTXOs that belong to a certain address.
|
||||
func GetUTXOsByAddressHandler(address string) (interface{}, *httpserverutils.HandlerError) {
|
||||
func GetUTXOsByAddressHandler(address string) (interface{}, error) {
|
||||
db, err := database.DB()
|
||||
if err != nil {
|
||||
return nil, httpserverutils.NewInternalServerHandlerError(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var transactionOutputs []*dbmodels.TransactionOutput
|
||||
@ -141,12 +142,12 @@ func GetUTXOsByAddressHandler(address string) (interface{}, *httpserverutils.Han
|
||||
Preload("Transaction.Subnetwork").
|
||||
Find(&transactionOutputs).GetErrors()
|
||||
if len(dbErrors) > 0 {
|
||||
return nil, httpserverutils.NewHandlerErrorFromDBErrors("Some errors were encountered when loading UTXOs from the database:", dbErrors)
|
||||
return nil, httpserverutils.NewErrorFromDBErrors("Some errors were encountered when loading UTXOs from the database:", dbErrors)
|
||||
}
|
||||
|
||||
selectedTipBlueScore, hErr := fetchSelectedTipBlueScore()
|
||||
if hErr != nil {
|
||||
return nil, hErr
|
||||
selectedTipBlueScore, err := fetchSelectedTipBlueScore()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
UTXOsResponses := make([]*apimodels.TransactionOutputResponse, len(transactionOutputs))
|
||||
@ -154,7 +155,7 @@ func GetUTXOsByAddressHandler(address string) (interface{}, *httpserverutils.Han
|
||||
subnetworkID := &subnetworkid.SubnetworkID{}
|
||||
err := subnetworkid.Decode(subnetworkID, transactionOutput.Transaction.Subnetwork.SubnetworkID)
|
||||
if err != nil {
|
||||
return nil, httpserverutils.NewInternalServerHandlerError(fmt.Sprintf("Couldn't decode subnetwork id %s: %s", transactionOutput.Transaction.Subnetwork.SubnetworkID, err))
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("Couldn't decode subnetwork id %s", transactionOutput.Transaction.Subnetwork.SubnetworkID))
|
||||
}
|
||||
var acceptingBlockHash *string
|
||||
var confirmations uint64
|
||||
@ -188,24 +189,24 @@ func addTxPreloadedFields(query *gorm.DB) *gorm.DB {
|
||||
}
|
||||
|
||||
// PostTransaction forwards a raw transaction to the JSON-RPC API server
|
||||
func PostTransaction(requestBody []byte) *httpserverutils.HandlerError {
|
||||
func PostTransaction(requestBody []byte) error {
|
||||
client, err := jsonrpc.GetClient()
|
||||
if err != nil {
|
||||
return httpserverutils.NewInternalServerHandlerError(err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
rawTx := &apimodels.RawTransaction{}
|
||||
err = json.Unmarshal(requestBody, rawTx)
|
||||
if err != nil {
|
||||
return httpserverutils.NewHandlerErrorWithCustomClientMessage(http.StatusUnprocessableEntity,
|
||||
fmt.Sprintf("Error unmarshalling request body: %s", err),
|
||||
errors.Wrap(err, "Error unmarshalling request body"),
|
||||
"The request body is not json-formatted")
|
||||
}
|
||||
|
||||
txBytes, err := hex.DecodeString(rawTx.RawTransaction)
|
||||
if err != nil {
|
||||
return httpserverutils.NewHandlerErrorWithCustomClientMessage(http.StatusUnprocessableEntity,
|
||||
fmt.Sprintf("Error decoding hex raw transaction: %s", err),
|
||||
errors.Wrap(err, "Error decoding hex raw transaction"),
|
||||
"The raw transaction is not a hex-encoded transaction")
|
||||
}
|
||||
|
||||
@ -214,16 +215,16 @@ func PostTransaction(requestBody []byte) *httpserverutils.HandlerError {
|
||||
err = tx.BtcDecode(txReader, 0)
|
||||
if err != nil {
|
||||
return httpserverutils.NewHandlerErrorWithCustomClientMessage(http.StatusUnprocessableEntity,
|
||||
fmt.Sprintf("Error decoding raw transaction: %s", err),
|
||||
errors.Wrap(err, "Error decoding raw transaction"),
|
||||
"Error decoding raw transaction")
|
||||
}
|
||||
|
||||
_, err = client.SendRawTransaction(tx, true)
|
||||
if err != nil {
|
||||
if rpcErr, ok := err.(*btcjson.RPCError); ok && rpcErr.Code == btcjson.ErrRPCVerify {
|
||||
return httpserverutils.NewHandlerError(http.StatusInternalServerError, rpcErr.Message)
|
||||
return httpserverutils.NewHandlerError(http.StatusInternalServerError, err)
|
||||
}
|
||||
return httpserverutils.NewHandlerError(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -3,6 +3,7 @@ package server
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/daglabs/btcd/httpserverutils"
|
||||
"github.com/pkg/errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
@ -29,7 +30,7 @@ const (
|
||||
defaultGetBlocksOrder = controllers.OrderAscending
|
||||
)
|
||||
|
||||
func mainHandler(_ *httpserverutils.ServerContext, _ *http.Request, _ map[string]string, _ map[string]string, _ []byte) (interface{}, *httpserverutils.HandlerError) {
|
||||
func mainHandler(_ *httpserverutils.ServerContext, _ *http.Request, _ map[string]string, _ map[string]string, _ []byte) (interface{}, error) {
|
||||
return struct {
|
||||
Message string `json:"message"`
|
||||
}{
|
||||
@ -81,11 +82,11 @@ func addRoutes(router *mux.Router) {
|
||||
Methods("POST")
|
||||
}
|
||||
|
||||
func convertQueryParamToInt(queryParams map[string]string, param string, defaultValue int) (int, *httpserverutils.HandlerError) {
|
||||
func convertQueryParamToInt(queryParams map[string]string, param string, defaultValue int) (int, error) {
|
||||
if _, ok := queryParams[param]; ok {
|
||||
intValue, err := strconv.Atoi(queryParams[param])
|
||||
if err != nil {
|
||||
return 0, httpserverutils.NewHandlerError(http.StatusUnprocessableEntity, fmt.Sprintf("Couldn't parse the '%s' query parameter: %s", param, err))
|
||||
return 0, httpserverutils.NewHandlerError(http.StatusUnprocessableEntity, errors.Wrap(err, fmt.Sprintf("Couldn't parse the '%s' query parameter", param)))
|
||||
}
|
||||
return intValue, nil
|
||||
}
|
||||
@ -93,72 +94,72 @@ func convertQueryParamToInt(queryParams map[string]string, param string, default
|
||||
}
|
||||
|
||||
func getTransactionByIDHandler(_ *httpserverutils.ServerContext, _ *http.Request, routeParams map[string]string, _ map[string]string,
|
||||
_ []byte) (interface{}, *httpserverutils.HandlerError) {
|
||||
_ []byte) (interface{}, error) {
|
||||
|
||||
return controllers.GetTransactionByIDHandler(routeParams[routeParamTxID])
|
||||
}
|
||||
|
||||
func getTransactionByHashHandler(_ *httpserverutils.ServerContext, _ *http.Request, routeParams map[string]string, _ map[string]string,
|
||||
_ []byte) (interface{}, *httpserverutils.HandlerError) {
|
||||
_ []byte) (interface{}, error) {
|
||||
|
||||
return controllers.GetTransactionByHashHandler(routeParams[routeParamTxHash])
|
||||
}
|
||||
|
||||
func getTransactionsByAddressHandler(_ *httpserverutils.ServerContext, _ *http.Request, routeParams map[string]string, queryParams map[string]string,
|
||||
_ []byte) (interface{}, *httpserverutils.HandlerError) {
|
||||
_ []byte) (interface{}, error) {
|
||||
|
||||
skip, hErr := convertQueryParamToInt(queryParams, queryParamSkip, 0)
|
||||
if hErr != nil {
|
||||
return nil, hErr
|
||||
skip, err := convertQueryParamToInt(queryParams, queryParamSkip, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
limit, hErr := convertQueryParamToInt(queryParams, queryParamLimit, defaultGetTransactionsLimit)
|
||||
if hErr != nil {
|
||||
return nil, hErr
|
||||
limit, err := convertQueryParamToInt(queryParams, queryParamLimit, defaultGetTransactionsLimit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, ok := queryParams[queryParamLimit]; ok {
|
||||
var err error
|
||||
skip, err = strconv.Atoi(queryParams[queryParamLimit])
|
||||
if err != nil {
|
||||
return nil, httpserverutils.NewHandlerError(http.StatusUnprocessableEntity,
|
||||
fmt.Sprintf("Couldn't parse the '%s' query parameter: %s", queryParamLimit, err))
|
||||
errors.Wrap(err, fmt.Sprintf("Couldn't parse the '%s' query parameter", queryParamLimit)))
|
||||
}
|
||||
}
|
||||
return controllers.GetTransactionsByAddressHandler(routeParams[routeParamAddress], uint64(skip), uint64(limit))
|
||||
}
|
||||
|
||||
func getUTXOsByAddressHandler(_ *httpserverutils.ServerContext, _ *http.Request, routeParams map[string]string, _ map[string]string,
|
||||
_ []byte) (interface{}, *httpserverutils.HandlerError) {
|
||||
_ []byte) (interface{}, error) {
|
||||
|
||||
return controllers.GetUTXOsByAddressHandler(routeParams[routeParamAddress])
|
||||
}
|
||||
|
||||
func getBlockByHashHandler(_ *httpserverutils.ServerContext, _ *http.Request, routeParams map[string]string, _ map[string]string,
|
||||
_ []byte) (interface{}, *httpserverutils.HandlerError) {
|
||||
_ []byte) (interface{}, error) {
|
||||
|
||||
return controllers.GetBlockByHashHandler(routeParams[routeParamBlockHash])
|
||||
}
|
||||
|
||||
func getFeeEstimatesHandler(_ *httpserverutils.ServerContext, _ *http.Request, _ map[string]string, _ map[string]string,
|
||||
_ []byte) (interface{}, *httpserverutils.HandlerError) {
|
||||
_ []byte) (interface{}, error) {
|
||||
|
||||
return controllers.GetFeeEstimatesHandler()
|
||||
}
|
||||
|
||||
func getBlocksHandler(_ *httpserverutils.ServerContext, _ *http.Request, _ map[string]string, queryParams map[string]string,
|
||||
_ []byte) (interface{}, *httpserverutils.HandlerError) {
|
||||
_ []byte) (interface{}, error) {
|
||||
|
||||
skip, hErr := convertQueryParamToInt(queryParams, queryParamSkip, 0)
|
||||
if hErr != nil {
|
||||
return nil, hErr
|
||||
skip, err := convertQueryParamToInt(queryParams, queryParamSkip, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
limit, hErr := convertQueryParamToInt(queryParams, queryParamLimit, defaultGetBlocksLimit)
|
||||
if hErr != nil {
|
||||
return nil, hErr
|
||||
limit, err := convertQueryParamToInt(queryParams, queryParamLimit, defaultGetBlocksLimit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
order := defaultGetBlocksOrder
|
||||
if orderParamValue, ok := queryParams[queryParamOrder]; ok {
|
||||
if orderParamValue != controllers.OrderAscending && orderParamValue != controllers.OrderDescending {
|
||||
return nil, httpserverutils.NewHandlerError(http.StatusUnprocessableEntity, fmt.Sprintf("'%s' is not a valid value for the '%s' query parameter", orderParamValue, queryParamLimit))
|
||||
return nil, httpserverutils.NewHandlerError(http.StatusUnprocessableEntity, errors.Errorf("'%s' is not a valid value for the '%s' query parameter", orderParamValue, queryParamLimit))
|
||||
}
|
||||
order = orderParamValue
|
||||
}
|
||||
@ -166,6 +167,6 @@ func getBlocksHandler(_ *httpserverutils.ServerContext, _ *http.Request, _ map[s
|
||||
}
|
||||
|
||||
func postTransactionHandler(_ *httpserverutils.ServerContext, _ *http.Request, _ map[string]string, _ map[string]string,
|
||||
requestBody []byte) (interface{}, *httpserverutils.HandlerError) {
|
||||
requestBody []byte) (interface{}, error) {
|
||||
return nil, controllers.PostTransaction(requestBody)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"github.com/daglabs/btcd/faucet/database"
|
||||
"github.com/daglabs/btcd/httpserverutils"
|
||||
"github.com/pkg/errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
@ -23,43 +24,43 @@ func ipFromRequest(r *http.Request) (string, error) {
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
func validateIPUsage(r *http.Request) *httpserverutils.HandlerError {
|
||||
func validateIPUsage(r *http.Request) error {
|
||||
db, err := database.DB()
|
||||
if err != nil {
|
||||
return httpserverutils.NewInternalServerHandlerError(err.Error())
|
||||
return err
|
||||
}
|
||||
now := time.Now()
|
||||
timeBeforeMinRequestInterval := now.Add(-minRequestInterval)
|
||||
var count int
|
||||
ip, err := ipFromRequest(r)
|
||||
if err != nil {
|
||||
return httpserverutils.NewInternalServerHandlerError(err.Error())
|
||||
return err
|
||||
}
|
||||
dbResult := db.Model(&ipUse{}).Where(&ipUse{IP: ip}).Where("last_use BETWEEN ? AND ?", timeBeforeMinRequestInterval, now).Count(&count)
|
||||
dbErrors := dbResult.GetErrors()
|
||||
if httpserverutils.HasDBError(dbErrors) {
|
||||
return httpserverutils.NewHandlerErrorFromDBErrors("Some errors were encountered when checking the last use of an IP:", dbResult.GetErrors())
|
||||
return httpserverutils.NewErrorFromDBErrors("Some errors were encountered when checking the last use of an IP:", dbResult.GetErrors())
|
||||
}
|
||||
if count != 0 {
|
||||
return httpserverutils.NewHandlerError(http.StatusForbidden, "A user is allowed to to have one request from the faucet every 24 hours.")
|
||||
return httpserverutils.NewHandlerError(http.StatusForbidden, errors.New("A user is allowed to to have one request from the faucet every 24 hours"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateIPUsage(r *http.Request) *httpserverutils.HandlerError {
|
||||
func updateIPUsage(r *http.Request) error {
|
||||
db, err := database.DB()
|
||||
if err != nil {
|
||||
return httpserverutils.NewInternalServerHandlerError(err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
ip, err := ipFromRequest(r)
|
||||
if err != nil {
|
||||
return httpserverutils.NewInternalServerHandlerError(err.Error())
|
||||
return err
|
||||
}
|
||||
dbResult := db.Where(&ipUse{IP: ip}).Assign(&ipUse{LastUse: time.Now()}).FirstOrCreate(&ipUse{})
|
||||
dbErrors := dbResult.GetErrors()
|
||||
if httpserverutils.HasDBError(dbErrors) {
|
||||
return httpserverutils.NewHandlerErrorFromDBErrors("Some errors were encountered when upserting the IP to the new date:", dbResult.GetErrors())
|
||||
return httpserverutils.NewErrorFromDBErrors("Some errors were encountered when upserting the IP to the new date:", dbResult.GetErrors())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -3,10 +3,10 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/daglabs/btcd/faucet/config"
|
||||
"github.com/daglabs/btcd/httpserverutils"
|
||||
"github.com/daglabs/btcd/util"
|
||||
"github.com/pkg/errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@ -51,7 +51,7 @@ type requestMoneyData struct {
|
||||
}
|
||||
|
||||
func requestMoneyHandler(_ *httpserverutils.ServerContext, r *http.Request, _ map[string]string, _ map[string]string,
|
||||
requestBody []byte) (interface{}, *httpserverutils.HandlerError) {
|
||||
requestBody []byte) (interface{}, error) {
|
||||
hErr := validateIPUsage(r)
|
||||
if hErr != nil {
|
||||
return nil, hErr
|
||||
@ -60,18 +60,18 @@ func requestMoneyHandler(_ *httpserverutils.ServerContext, r *http.Request, _ ma
|
||||
err := json.Unmarshal(requestBody, requestData)
|
||||
if err != nil {
|
||||
return nil, httpserverutils.NewHandlerErrorWithCustomClientMessage(http.StatusUnprocessableEntity,
|
||||
fmt.Sprintf("Error unmarshalling request body: %s", err),
|
||||
errors.Wrap(err, "Error unmarshalling request body"),
|
||||
"The request body is not json-formatted")
|
||||
}
|
||||
address, err := util.DecodeAddress(requestData.Address, config.ActiveNetParams().Prefix)
|
||||
if err != nil {
|
||||
return nil, httpserverutils.NewHandlerErrorWithCustomClientMessage(http.StatusUnprocessableEntity,
|
||||
fmt.Sprintf("Error decoding address: %s", err),
|
||||
errors.Wrap(err, "Error decoding address"),
|
||||
"Error decoding address")
|
||||
}
|
||||
tx, err := sendToAddress(address)
|
||||
if err != nil {
|
||||
return nil, httpserverutils.NewInternalServerHandlerError(err.Error())
|
||||
return nil, err
|
||||
}
|
||||
hErr = updateIPUsage(r)
|
||||
if hErr != nil {
|
||||
|
@ -13,29 +13,29 @@ import (
|
||||
// a rest route handler or a middleware.
|
||||
type HandlerError struct {
|
||||
Code int
|
||||
Message string
|
||||
Cause error
|
||||
ClientMessage string
|
||||
}
|
||||
|
||||
func (hErr *HandlerError) Error() string {
|
||||
return hErr.Message
|
||||
return hErr.Cause.Error()
|
||||
}
|
||||
|
||||
// NewHandlerError returns a HandlerError with the given code and message.
|
||||
func NewHandlerError(code int, message string) *HandlerError {
|
||||
func NewHandlerError(code int, err error) error {
|
||||
return &HandlerError{
|
||||
Code: code,
|
||||
Message: message,
|
||||
ClientMessage: message,
|
||||
Cause: err,
|
||||
ClientMessage: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewHandlerErrorWithCustomClientMessage returns a HandlerError with
|
||||
// the given code, message and client error message.
|
||||
func NewHandlerErrorWithCustomClientMessage(code int, message, clientMessage string) *HandlerError {
|
||||
func NewHandlerErrorWithCustomClientMessage(code int, err error, clientMessage string) error {
|
||||
return &HandlerError{
|
||||
Code: code,
|
||||
Message: message,
|
||||
Cause: err,
|
||||
ClientMessage: clientMessage,
|
||||
}
|
||||
}
|
||||
@ -43,8 +43,8 @@ func NewHandlerErrorWithCustomClientMessage(code int, message, clientMessage str
|
||||
// NewInternalServerHandlerError returns a HandlerError with
|
||||
// the given message, and the http.StatusInternalServerError
|
||||
// status text as client message.
|
||||
func NewInternalServerHandlerError(message string) *HandlerError {
|
||||
return NewHandlerErrorWithCustomClientMessage(http.StatusInternalServerError, message, http.StatusText(http.StatusInternalServerError))
|
||||
func NewInternalServerHandlerError(err error) error {
|
||||
return NewHandlerErrorWithCustomClientMessage(http.StatusInternalServerError, err, http.StatusText(http.StatusInternalServerError))
|
||||
}
|
||||
|
||||
// NewErrorFromDBErrors takes a slice of database errors and a prefix, and
|
||||
@ -58,13 +58,6 @@ func NewErrorFromDBErrors(prefix string, dbErrors []error) error {
|
||||
return errors.Errorf("%s [%s]", prefix, strings.Join(dbErrorsStrings, ","))
|
||||
}
|
||||
|
||||
// NewHandlerErrorFromDBErrors takes a slice of database errors and a prefix, and
|
||||
// returns an HandlerError with error code http.StatusInternalServerError with
|
||||
// all of the database errors formatted to one string with the given prefix
|
||||
func NewHandlerErrorFromDBErrors(prefix string, dbErrors []error) *HandlerError {
|
||||
return NewInternalServerHandlerError(NewErrorFromDBErrors(prefix, dbErrors).Error())
|
||||
}
|
||||
|
||||
// IsDBRecordNotFoundError returns true if the given dbErrors contains only a RecordNotFound error
|
||||
func IsDBRecordNotFoundError(dbErrors []error) bool {
|
||||
return len(dbErrors) == 1 && gorm.IsRecordNotFoundError(dbErrors[0])
|
||||
@ -88,9 +81,13 @@ func (err *ClientError) Error() string {
|
||||
|
||||
// SendErr takes a HandlerError and create a ClientError out of it that is sent
|
||||
// to the http client.
|
||||
func SendErr(ctx *ServerContext, w http.ResponseWriter, hErr *HandlerError) {
|
||||
errMsg := fmt.Sprintf("got error: %s", hErr)
|
||||
ctx.Warnf(errMsg)
|
||||
func SendErr(ctx *ServerContext, w http.ResponseWriter, err error) {
|
||||
var hErr *HandlerError
|
||||
var ok bool
|
||||
if hErr, ok = err.(*HandlerError); !ok {
|
||||
hErr = NewInternalServerHandlerError(err).(*HandlerError)
|
||||
}
|
||||
ctx.Warnf("got error: %s", err)
|
||||
w.WriteHeader(hErr.Code)
|
||||
SendJSONResponse(w, &ClientError{
|
||||
ErrorCode: hErr.Code,
|
||||
|
@ -2,6 +2,7 @@ package httpserverutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
)
|
||||
@ -38,10 +39,16 @@ func RecoveryMiddleware(h http.Handler) http.Handler {
|
||||
defer func() {
|
||||
recoveryErr := recover()
|
||||
if recoveryErr != nil {
|
||||
var recoveryErrAsError error
|
||||
if rErr, ok := recoveryErr.(error); ok {
|
||||
recoveryErrAsError = rErr
|
||||
} else {
|
||||
recoveryErrAsError = errors.Errorf("%s", recoveryErr)
|
||||
}
|
||||
recoveryErrStr := fmt.Sprintf("%s", recoveryErr)
|
||||
log.Criticalf("Fatal error: %s", recoveryErrStr)
|
||||
log.Criticalf("Fatal error: %+v", recoveryErrStr)
|
||||
log.Criticalf("Stack trace: %s", debug.Stack())
|
||||
SendErr(ctx, w, NewInternalServerHandlerError(recoveryErrStr))
|
||||
SendErr(ctx, w, recoveryErrAsError)
|
||||
}
|
||||
}()
|
||||
h.ServeHTTP(w, r)
|
||||
|
@ -1,8 +1,8 @@
|
||||
package httpserverutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
@ -11,7 +11,7 @@ import (
|
||||
// MakeHandler wrapper and gets the relevant request fields
|
||||
// from it.
|
||||
type HandlerFunc func(ctx *ServerContext, r *http.Request, routeParams map[string]string, queryParams map[string]string, requestBody []byte) (
|
||||
interface{}, *HandlerError)
|
||||
interface{}, error)
|
||||
|
||||
// MakeHandler is a wrapper function that takes a handler in the form of HandlerFunc
|
||||
// and returns a function that can be used as a handler in mux.Router.HandleFunc.
|
||||
@ -24,19 +24,19 @@ func MakeHandler(handler HandlerFunc) func(http.ResponseWriter, *http.Request) {
|
||||
var err error
|
||||
requestBody, err = ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
SendErr(ctx, w, NewInternalServerHandlerError("Error reading POST data"))
|
||||
SendErr(ctx, w, errors.New("Error reading POST data"))
|
||||
}
|
||||
}
|
||||
|
||||
flattenedQueryParams, hErr := flattenQueryParams(r.URL.Query())
|
||||
if hErr != nil {
|
||||
SendErr(ctx, w, hErr)
|
||||
flattenedQueryParams, err := flattenQueryParams(r.URL.Query())
|
||||
if err != nil {
|
||||
SendErr(ctx, w, err)
|
||||
return
|
||||
}
|
||||
|
||||
response, hErr := handler(ctx, r, mux.Vars(r), flattenedQueryParams, requestBody)
|
||||
if hErr != nil {
|
||||
SendErr(ctx, w, hErr)
|
||||
response, err := handler(ctx, r, mux.Vars(r), flattenedQueryParams, requestBody)
|
||||
if err != nil {
|
||||
SendErr(ctx, w, err)
|
||||
return
|
||||
}
|
||||
if response != nil {
|
||||
@ -45,11 +45,11 @@ func MakeHandler(handler HandlerFunc) func(http.ResponseWriter, *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func flattenQueryParams(queryParams map[string][]string) (map[string]string, *HandlerError) {
|
||||
func flattenQueryParams(queryParams map[string][]string) (map[string]string, error) {
|
||||
flattenedMap := make(map[string]string)
|
||||
for param, valuesSlice := range queryParams {
|
||||
if len(valuesSlice) > 1 {
|
||||
return nil, NewHandlerError(http.StatusUnprocessableEntity, fmt.Sprintf("Couldn't parse the '%s' query parameter:"+
|
||||
return nil, NewHandlerError(http.StatusUnprocessableEntity, errors.Errorf("Couldn't parse the '%s' query parameter:"+
|
||||
" expected a single value but got multiple values", param))
|
||||
}
|
||||
flattenedMap[param] = valuesSlice[0]
|
||||
|
Loading…
x
Reference in New Issue
Block a user