From 950faee42b07f355cfd15ced2e64bde19ff35de1 Mon Sep 17 00:00:00 2001 From: Evan Han Date: Tue, 12 Jan 2021 10:59:25 +0800 Subject: [PATCH] Merge PR: add backend rest api (#549) * add backend rest api * add timestamp params --- x/backend/client/rest/rest.go | 71 +++++++++++++++++++++++++++++++++++ x/backend/keeper/querier.go | 49 ++++++++++++++++++++++++ x/backend/orm/orm.go | 18 +++++++++ x/backend/types/errors.go | 7 ++++ x/backend/types/keys.go | 2 + x/backend/types/params.go | 24 ++++++++++++ 6 files changed, 171 insertions(+) diff --git a/x/backend/client/rest/rest.go b/x/backend/client/rest/rest.go index 30ce8fd10d..451e7f3f77 100644 --- a/x/backend/client/rest/rest.go +++ b/x/backend/client/rest/rest.go @@ -24,6 +24,8 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { r.HandleFunc("/deals", dealHandler(cliCtx)).Methods("GET") r.HandleFunc("/fees", feeDetailListHandler(cliCtx)).Methods("GET") r.HandleFunc("/order/list/{openOrClosed}", orderListHandler(cliCtx)).Methods("GET") + r.HandleFunc("/orders/{orderID}", orderHandler(cliCtx)).Methods("GET") + r.HandleFunc("/accounts/{address}/orders", accountOrdersHandler(cliCtx)).Methods("GET") r.HandleFunc("/block_tx_hashes/{blockHeight}", blockTxHashesHandler(cliCtx)).Methods("GET") r.HandleFunc("/transactions", txListHandler(cliCtx)).Methods("GET") r.HandleFunc("/latestheight", latestHeightHandler(cliCtx)).Methods("GET") @@ -330,6 +332,75 @@ func orderListHandler(cliCtx context.CLIContext) http.HandlerFunc { } } +func orderHandler(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + orderID := vars["orderID"] + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/backend/%s/%s", types.QueryOrderByID, orderID), nil) + if err != nil { + sdkErr := common.ParseSDKError(err.Error()) + common.HandleErrorMsg(w, cliCtx, sdkErr.Code, sdkErr.Message) + return + } + + rest.PostProcessResponse(w, cliCtx, res) + } +} + +func accountOrdersHandler(cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + address := vars["address"] + pageStr := r.URL.Query().Get("page") + perPageStr := r.URL.Query().Get("per_page") + startStr := r.URL.Query().Get("start") + endStr := r.URL.Query().Get("end") + + // validate request + if address == "" { + common.HandleErrorMsg(w, cliCtx, types.CodeAddressIsRequired, "bad request: address is required") + return + } + + page, perPage, err := common.Paginate(pageStr, perPageStr) + if err != nil { + common.HandleErrorMsg(w, cliCtx, common.CodeStrconvFailed, err.Error()) + return + } + + var start, end int64 + if startStr == "" { + startStr = "0" + } + if endStr == "" { + endStr = "0" + } + start, errStart := strconv.ParseInt(startStr, 10, 64) + end, errEnd := strconv.ParseInt(endStr, 10, 64) + mErr := types.NewErrorsMerged(errStart, errEnd) + if mErr != nil { + common.HandleErrorMsg(w, cliCtx, common.CodeStrconvFailed, mErr.Error()) + return + } + + params := types.NewQueryAccountOrdersParams(address, start, end, page, perPage) + bz, err := cliCtx.Codec.MarshalJSON(params) + if err != nil { + common.HandleErrorMsg(w, cliCtx, common.CodeMarshalJSONFailed, err.Error()) + return + } + + res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/backend/%s", types.QueryAccountOrders), bz) + if err != nil { + sdkErr := common.ParseSDKError(err.Error()) + common.HandleErrorMsg(w, cliCtx, sdkErr.Code, sdkErr.Message) + return + } + + rest.PostProcessResponse(w, cliCtx, res) + } +} + func txListHandler(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { addr := r.URL.Query().Get("address") diff --git a/x/backend/keeper/querier.go b/x/backend/keeper/querier.go index 972787c4c8..532bebc926 100644 --- a/x/backend/keeper/querier.go +++ b/x/backend/keeper/querier.go @@ -51,6 +51,10 @@ func NewQuerier(keeper Keeper) sdk.Querier { res, err = queryFeeDetails(ctx, path[1:], req, keeper) case types.QueryOrderList: res, err = queryOrderList(ctx, path[1:], req, keeper) + case types.QueryOrderByID: + res, err = queryOrderByID(ctx, path[1:], req, keeper) + case types.QueryAccountOrders: + res, err = queryAccountOrders(ctx, path[1:], req, keeper) case types.QueryTxList: res, err = queryTxList(ctx, path[1:], req, keeper) case types.QueryCandleList: @@ -443,6 +447,51 @@ func queryOrderList(ctx sdk.Context, path []string, req abci.RequestQuery, keepe return bz, nil } +func queryOrderByID(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + if len(path) == 0 { + return nil, types.ErrOrderIdIsRequired() + } + orderID := path[0] + order := keeper.Orm.GetOrderByID(orderID) + response := common.GetBaseResponse(order) + bz, err := json.Marshal(response) + if err != nil { + return nil, common.ErrMarshalJSONFailed(err.Error()) + } + return bz, nil +} + +func queryAccountOrders(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { + var params types.QueryAccountOrdersParams + err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, common.ErrUnMarshalJSONFailed(err.Error()) + } + if params.Address != "" { + _, err := sdk.AccAddressFromBech32(params.Address) + if err != nil { + return nil, common.ErrCreateAddrFromBech32Failed(params.Address, err.Error()) + } + } + if params.Page < 0 || params.PerPage < 0 { + return nil, common.ErrInvalidPaginateParam(params.Page, params.PerPage) + } + + offset, limit := common.GetPage(params.Page, params.PerPage) + orders, total := keeper.Orm.GetAccountOrders(params.Address, params.Start, params.End, offset, limit) + var response *common.ListResponse + if len(orders) > 0 { + response = common.GetListResponse(total, params.Page, params.PerPage, orders) + } else { + response = common.GetEmptyListResponse(total, params.Page, params.PerPage) + } + bz, err := json.Marshal(response) + if err != nil { + return nil, common.ErrMarshalJSONFailed(err.Error()) + } + return bz, nil +} + func queryTxList(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) ([]byte, sdk.Error) { var params types.QueryTxListParams err := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) diff --git a/x/backend/orm/orm.go b/x/backend/orm/orm.go index fb44d456a1..350c61fe5c 100644 --- a/x/backend/orm/orm.go +++ b/x/backend/orm/orm.go @@ -1169,6 +1169,24 @@ func (orm *ORM) GetOrderList(address, product, side string, open bool, offset, l return orders, total } +func (orm *ORM) GetAccountOrders(address string, startTS, endTS int64, offset, limit int) ([]types.Order, int) { + var orders []types.Order + + if endTS == 0 { + endTS = time.Now().Unix() + } + + query := orm.db.Model(types.Order{}).Where("sender = ? AND timestamp >= ? AND timestamp < ?", address, startTS, endTS) + var total int + query.Count(&total) + if offset >= total { + return orders, total + } + + query.Order("timestamp desc").Offset(offset).Limit(limit).Find(&orders) + return orders, total +} + // AddTransactions insert into transactions, return count func (orm *ORM) AddTransactions(transactions []*types.Transaction) (addedCnt int, err error) { orm.singleEntryLock.Lock() diff --git a/x/backend/types/errors.go b/x/backend/types/errors.go index 02719ff989..a545c933e9 100644 --- a/x/backend/types/errors.go +++ b/x/backend/types/errors.go @@ -2,6 +2,7 @@ package types import ( "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) @@ -29,6 +30,7 @@ const ( CodeMarketkeeperNotInitialized uint32 = 62016 CodeGetInvalidateGranularity uint32 = 62017 CodeGetInvalidTickerByProducts uint32 = 62018 + CodeOrderIdIsRequired uint32 = 62019 ) // invalid param side, must be buy or sell @@ -73,3 +75,8 @@ func ErrGetInvalidateGranularity(msg string, key string, field string) sdk.Envel func ErrGetInvalidTickerByProducts(key string) sdk.EnvelopedErr { return sdk.EnvelopedErr{Err: sdkerrors.New(DefaultCodespace, CodeGetInvalidTickerByProducts, fmt.Sprintf("No value found for key: %s", key))} } + +// orderId is required +func ErrOrderIdIsRequired() sdk.EnvelopedErr { + return sdk.EnvelopedErr{Err: sdkerrors.New(DefaultCodespace, CodeOrderIdIsRequired, "invalid params: orderId is required")} +} diff --git a/x/backend/types/keys.go b/x/backend/types/keys.go index 622323878c..e53423cd72 100644 --- a/x/backend/types/keys.go +++ b/x/backend/types/keys.go @@ -14,6 +14,8 @@ const ( QueryDealList = "deals" QueryFeeDetails = "fees" QueryOrderList = "orders" + QueryOrderByID = "orderByID" + QueryAccountOrders = "accountOrders" QueryTxList = "txs" QueryCandleList = "candles" QueryTickerList = "tickers" diff --git a/x/backend/types/params.go b/x/backend/types/params.go index c201fac09f..2f5a486f82 100644 --- a/x/backend/types/params.go +++ b/x/backend/types/params.go @@ -137,6 +137,30 @@ func NewQueryOrderListParams(addr, product, side string, page, perPage int, star } } +// nolint +type QueryAccountOrdersParams struct { + Address string + Start int64 + End int64 + Page int + PerPage int +} + +// NewQueryAccountOrdersParams creates a new instance of QueryAccountOrdersParams +func NewQueryAccountOrdersParams(address string, start int64, end int64, page, perPage int) QueryAccountOrdersParams { + if page == 0 && perPage == 0 { + page = DefaultPage + perPage = DefaultPerPage + } + return QueryAccountOrdersParams{ + Address: address, + Start: start, + End: end, + Page: page, + PerPage: perPage, + } +} + // nolint type QueryTxListParams struct { Address string