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
import (
"encoding/json"
"fmt"
"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
// TODO: Make the error generic enough with HTTP status codes and messages as well
func HandleAPIError(w http.ResponseWriter, r *http.Request, err error) {
// Write content-type, statuscode, payload
w.Header().Set("Content-Type", "application/json")
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
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
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
// 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
}
@ -76,6 +78,7 @@ func runMigrations() {
&models.User{},
&models.Role{},
&models.Page{},
&models.Query{},
)
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
const ComponentURL = "/components"
const QueryURL = "/query"