diff --git a/packages/api/dbfind.go b/packages/api/dbfind.go new file mode 100644 index 000000000..1d0f37e9f --- /dev/null +++ b/packages/api/dbfind.go @@ -0,0 +1,107 @@ +// Copyright (C) 2017, 2018, 2019 EGAAS S.A. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or (at +// your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +package api + +import ( + "encoding/json" + "net/http" + "strings" + + "github.com/AplaProject/go-apla/packages/conf" + "github.com/AplaProject/go-apla/packages/converter" + "github.com/AplaProject/go-apla/packages/model" + "github.com/AplaProject/go-apla/packages/smart" + "github.com/AplaProject/go-apla/packages/types" + "github.com/AplaProject/go-apla/packages/utils/tx" + "github.com/gorilla/mux" +) + +type dbFindResult struct { + List []interface{} `json:"list"` +} + +type dbfindForm struct { + ID int64 `schema:"id"` + Order string `schema:"order"` + Columns string `schema:"columns"` + paginatorForm +} + +func (f *dbfindForm) Validate(r *http.Request) error { + if err := f.paginatorForm.Validate(r); err != nil { + return err + } + + if len(f.Columns) > 0 { + f.Columns = converter.EscapeName(f.Columns) + } + + return nil +} + +func getDbFindHandler(w http.ResponseWriter, r *http.Request) { + form := &dbfindForm{} + if err := parseForm(r, form); err != nil { + errorResponse(w, err, http.StatusBadRequest) + return + } + + params := mux.Vars(r) + client := getClient(r) + tableName := strings.ToLower(params["table"]) + sc := smart.SmartContract{ + OBS: conf.Config.IsSupportingOBS(), + VM: smart.GetVM(), + TxSmart: tx.SmartContract{ + Header: tx.Header{ + EcosystemID: client.EcosystemID, + KeyID: client.KeyID, + NetworkID: conf.Config.NetworkID, + }, + }, + } + + // Check table existence + prefix := client.Prefix() + table := &model.Table{} + table.SetTablePrefix(prefix) + if found, err := table.Get(nil, tableName); !found || nil != err { + errorResponse(w, errTableNotFound.Errorf(tableName)) + return + } + + // Unmarshall where clause if there is any + var formWhere map[string]interface{} + if whereValue := r.FormValue("where"); 0 < len(whereValue) { + if err := json.Unmarshal([]byte(r.FormValue("where")), &formWhere); err != nil { + errorResponse(w, err, http.StatusBadRequest) + return + } + } + whereClause := types.LoadMap(formWhere) + + // Perform the actual request + _, ret, err := smart.DBSelect(&sc, tableName, form.Columns, form.ID, form.Order, form.Offset, form.Limit, whereClause) + if err != nil { + errorResponse(w, err) + return + } + + result := new(dbFindResult) + result.List = ret + jsonResponse(w, result) +} diff --git a/packages/api/dbfind_test.go b/packages/api/dbfind_test.go new file mode 100644 index 000000000..75fa693dc --- /dev/null +++ b/packages/api/dbfind_test.go @@ -0,0 +1,62 @@ +// Copyright (C) 2017, 2018, 2019 EGAAS S.A. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or (at +// your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +package api + +import ( + "fmt" + "net/url" + "testing" +) + +func TestDbFind(t *testing.T) { + if err := keyLogin(1); err != nil { + t.Error(err) + return + } + var ret dbFindResult + + // Query table that is known to be existing + if err := sendPost("dbfind/keys", &url.Values{}, &ret); nil != err { + t.Error(err) + return + } + if length := len(ret.List); 0 == length { + t.Error(fmt.Errorf(`The number of records %d = 0`, length)) + return + } + + // Query table that doesn't exist + if err := sendPost("dbfind/QA_not_existing_table", &url.Values{}, &ret); nil != err { + if err.Error() != `404 {"error":"E_TABLENOTFOUND","msg":"Table qa_not_existing_table has not been found"}` { + t.Error(err) + return + } + } + + // Query table with specified columns + if err := sendPost("dbfind/keys", &url.Values{"Columns": {"id, account"}}, &ret); nil != err { + t.Error(err) + } + + // Query table with specified columns that are known to be missing + if err := sendPost("dbfind/keys", &url.Values{"Columns": {"id,account,id_non_existing"}}, &ret); nil != err { + if err.Error() != `400 {"error":"E_SERVER","msg":"column id_non_existing doesn't exist"}` { + t.Error(err) + return + } + } +} diff --git a/packages/api/route.go b/packages/api/route.go index acd6de62a..9e406a66c 100644 --- a/packages/api/route.go +++ b/packages/api/route.go @@ -69,6 +69,7 @@ func (m Mode) SetCommonRoutes(r Router) { api.HandleFunc("/interface/block/{name}", authRequire(getBlockInterfaceRowHandler)).Methods("GET") api.HandleFunc("/table/{name}", authRequire(getTableHandler)).Methods("GET") api.HandleFunc("/tables", authRequire(getTablesHandler)).Methods("GET") + api.HandleFunc("/dbfind/{table}", authRequire(getDbFindHandler)).Methods("POST") api.HandleFunc("/test/{name}", getTestHandler).Methods("GET", "POST") api.HandleFunc("/version", getVersionHandler).Methods("GET") api.HandleFunc("/config/{option}", getConfigOptionHandler).Methods("GET") diff --git a/packages/smart/smart.go b/packages/smart/smart.go index 29405f0fc..5dd2a774c 100644 --- a/packages/smart/smart.go +++ b/packages/smart/smart.go @@ -723,6 +723,10 @@ func (sc *SmartContract) AccessColumns(table string, columns *[]string, update b colname := converter.Sanitize(col, `->`) if strings.Contains(colname, `->`) { colname = colname[:strings.Index(colname, `->`)] + } else if _, ok := cols[colname]; "id" != colname && !ok { + err = fmt.Errorf(eColumnNotExist, colname) + logger.WithFields(log.Fields{"type": consts.DBError, "error": err}).Errorf("getting column") + return err } colList[i] = colname }