Completing the CRUD for the query API. Also being able to execute a saved query.

TODO: Handle parameters while executing a saved query.
This commit is contained in:
Arpit Mohan 2019-02-28 09:41:35 +05:30
parent b38df1c07d
commit ce34ff4a83
6 changed files with 182 additions and 2 deletions

View File

@ -2,15 +2,31 @@
package api package api
import ( import (
"encoding/json"
"fmt" "fmt"
"net/http" "net/http"
) )
type ApiError struct {
Code int64 `json:"code,omitempty"`
Msg string `json:"msg,omitempty"`
}
// HandleAPIError handles any error that bubbles up to the controller layer // HandleAPIError handles any error that bubbles up to the controller layer
// TODO: Make the error generic enough with HTTP status codes and messages as well // TODO: Make the error generic enough with HTTP status codes and messages as well
func HandleAPIError(w http.ResponseWriter, r *http.Request, err error) { func HandleAPIError(w http.ResponseWriter, r *http.Request, err error) {
// Write content-type, statuscode, payload // Write content-type, statuscode, payload
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.WriteHeader(500) w.WriteHeader(500)
fmt.Fprintf(w, "%s", err) fmt.Fprintf(w, "%s", createErrorBody(err))
}
func createErrorBody(err error) []byte {
apiError := ApiError{
Code: -1,
Msg: err.Error(),
}
errorJSON, _ := json.Marshal(apiError)
return errorJSON
} }

View File

@ -1,6 +1,83 @@
package api package api
import (
"encoding/json"
"fmt"
"internal-tools-server/models"
"internal-tools-server/services"
"net/http"
"github.com/julienschmidt/httprouter"
)
/* /*
This file contains the APIs for the client to invoke in order to fetch data or perform an action This file contains the APIs for the client to invoke in order to fetch data or perform an action
on the middleware server on the middleware server
*/ */
// PostQuery executes a custom sql query on the client database
func PostQuery(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
queryBody := models.ExecQuery{}
err := json.NewDecoder(r.Body).Decode(&queryBody)
if err != nil {
HandleAPIError(w, r, err)
return
}
var mapArray []map[string]interface{}
mapArray, err = services.ExecuteQuery(queryBody)
if err != nil {
HandleAPIError(w, r, err)
return
}
// Write content-type, statuscode, payload
mapJSON, _ := json.Marshal(mapArray)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
fmt.Fprintf(w, "%s", mapJSON)
}
// CreateQuery creates a new query for the user in the table
func CreateQuery(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
queryBody := models.Query{}
err := json.NewDecoder(r.Body).Decode(&queryBody)
if err != nil {
HandleAPIError(w, r, err)
return
}
queryBody, err = services.CreateQuery(queryBody)
if err != nil {
HandleAPIError(w, r, err)
return
}
// Write content-type, statuscode, payload
queryJSON, _ := json.Marshal(queryBody)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
fmt.Fprintf(w, "%s", queryJSON)
}
// UpdateQuery updates a given query in the database for a given account
func UpdateQuery(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
queryBody := models.Query{}
err := json.NewDecoder(r.Body).Decode(&queryBody)
if err != nil {
HandleAPIError(w, r, err)
return
}
queryBody, err = services.UpdateQuery(queryBody)
if err != nil {
HandleAPIError(w, r, err)
return
}
// Write content-type, statuscode, payload
queryJSON, _ := json.Marshal(queryBody)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
fmt.Fprintf(w, "%s", queryJSON)
}

View File

@ -0,0 +1,29 @@
package models
type (
Query struct {
ID int64 `json:"id" sql:"id"`
Name string `json:"name" sql:"name"`
QueryType string `json:"query_type,omitempty" sql:"query_type"`
Executable string `json:"executable,omitempty" sql:"executable"`
ResourceName string `json:"resource_name,omitempty" sql:"resource_name"`
ConfirmationMsg string `json:"confirmation_msg,omitempty" sql:"confirmation_msg"`
}
ExecQuery struct {
QueryType string `json:"query_type,omitempty"`
Name string `json:"name"`
Params Params `json:"params,omitempty"`
}
Params struct {
QueryParams []KeyValue `json:"query_params,omitempty"`
HeaderParams []KeyValue `json:"header_params,omitempty"`
CookieParams []KeyValue `json:"cookie_params,omitempty"`
}
KeyValue struct {
Key string `json:"key"`
Value string `json:"value"`
}
)

View File

@ -47,7 +47,9 @@ func intializeServer() *httprouter.Router {
// Page CRUD Endpoints // Page CRUD Endpoints
// Query CRUD Endpoints // Query CRUD Endpoints
router.POST(baseURL+apiVersion+url.QueryURL+"/execute", api.PostQuery)
router.POST(baseURL+apiVersion+url.QueryURL, api.CreateQuery)
router.PUT(baseURL+apiVersion+url.QueryURL, api.UpdateQuery)
return router return router
} }
@ -76,6 +78,7 @@ func runMigrations() {
&models.User{}, &models.User{},
&models.Role{}, &models.Role{},
&models.Page{}, &models.Page{},
&models.Query{},
) )
log.Println("Successfully run all migrations") log.Println("Successfully run all migrations")
} }

View File

@ -0,0 +1,54 @@
package services
import (
"fmt"
"internal-tools-server/models"
"internal-tools-server/storage"
"github.com/jinzhu/gorm"
)
// ExecuteQuery runs a custom SQL query on the client database
func ExecuteQuery(queryBody models.ExecQuery) ([]map[string]interface{}, error) {
if queryBody.QueryType == "sql" {
// Get the actual query from the DB
datastore := storage.StorageEngine.GetDatastore()
queryDAO := &models.Query{}
if err := datastore.Where("name = ?", queryBody.Name).First(queryDAO).Error; gorm.IsRecordNotFoundError(err) {
return nil, fmt.Errorf("Invalid queryName: %s provided", queryBody.Name)
}
queryStr := queryDAO.Executable
mapArray, err := storage.StorageEngine.ExecuteQuery(queryStr)
if err != nil {
return nil, err
}
return mapArray, nil
}
return nil, fmt.Errorf("QueryType: %s not supported", queryBody.QueryType)
}
// CreateQuery creates a new query that can be executed by name at runtime
func CreateQuery(queryBody models.Query) (models.Query, error) {
datastore := storage.StorageEngine.GetDatastore()
if err := datastore.Create(&queryBody).Error; err != nil {
return models.Query{}, err
}
return queryBody, nil
}
// UpdateQuery updates an existing query in the database
func UpdateQuery(query models.Query) (models.Query, error) {
datastore := storage.StorageEngine.GetDatastore()
// Update only the non-nil values in the struct
datastore.Model(&query).Updates(query)
// Select the updated record to return back to the client
datastore.First(&query)
return query, nil
}

View File

@ -1,3 +1,4 @@
package url package url
const ComponentURL = "/components" const ComponentURL = "/components"
const QueryURL = "/query"