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:
parent
b38df1c07d
commit
ce34ff4a83
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
||||||
29
app/server/src/models/query.go
Normal file
29
app/server/src/models/query.go
Normal 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"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
54
app/server/src/services/query.go
Normal file
54
app/server/src/services/query.go
Normal 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
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
package url
|
package url
|
||||||
|
|
||||||
const ComponentURL = "/components"
|
const ComponentURL = "/components"
|
||||||
|
const QueryURL = "/query"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user