|
|
|
|
@@ -20,8 +20,23 @@ import (
|
|
|
|
|
|
|
|
|
|
// Handler handles function-based SQL API requests
|
|
|
|
|
type Handler struct {
|
|
|
|
|
db common.Database
|
|
|
|
|
hooks *HookRegistry
|
|
|
|
|
db common.Database
|
|
|
|
|
hooks *HookRegistry
|
|
|
|
|
variablesCallback func(r *http.Request) map[string]interface{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type SqlQueryOptions struct {
|
|
|
|
|
NoCount bool
|
|
|
|
|
BlankParams bool
|
|
|
|
|
AllowFilter bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewSqlQueryOptions() SqlQueryOptions {
|
|
|
|
|
return SqlQueryOptions{
|
|
|
|
|
NoCount: false,
|
|
|
|
|
BlankParams: true,
|
|
|
|
|
AllowFilter: true,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewHandler creates a new function API handler
|
|
|
|
|
@@ -38,6 +53,14 @@ func (h *Handler) GetDatabase() common.Database {
|
|
|
|
|
return h.db
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *Handler) SetVariablesCallback(callback func(r *http.Request) map[string]interface{}) {
|
|
|
|
|
h.variablesCallback = callback
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (h *Handler) GetVariablesCallback() func(r *http.Request) map[string]interface{} {
|
|
|
|
|
return h.variablesCallback
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Hooks returns the hook registry for this handler
|
|
|
|
|
// Use this to register custom hooks for operations
|
|
|
|
|
func (h *Handler) Hooks() *HookRegistry {
|
|
|
|
|
@@ -48,7 +71,7 @@ func (h *Handler) Hooks() *HookRegistry {
|
|
|
|
|
type HTTPFuncType func(http.ResponseWriter, *http.Request)
|
|
|
|
|
|
|
|
|
|
// SqlQueryList creates an HTTP handler that executes a SQL query and returns a list with pagination
|
|
|
|
|
func (h *Handler) SqlQueryList(sqlquery string, pNoCount, pBlankparms, pAllowFilter bool) HTTPFuncType {
|
|
|
|
|
func (h *Handler) SqlQueryList(sqlquery string, options SqlQueryOptions) HTTPFuncType {
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
defer func() {
|
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
|
@@ -70,6 +93,7 @@ func (h *Handler) SqlQueryList(sqlquery string, pNoCount, pBlankparms, pAllowFil
|
|
|
|
|
inputvars := make([]string, 0)
|
|
|
|
|
metainfo := make(map[string]interface{})
|
|
|
|
|
variables := make(map[string]interface{})
|
|
|
|
|
|
|
|
|
|
complexAPI := false
|
|
|
|
|
|
|
|
|
|
// Get user context from security package
|
|
|
|
|
@@ -93,9 +117,9 @@ func (h *Handler) SqlQueryList(sqlquery string, pNoCount, pBlankparms, pAllowFil
|
|
|
|
|
MetaInfo: metainfo,
|
|
|
|
|
PropQry: propQry,
|
|
|
|
|
UserContext: userCtx,
|
|
|
|
|
NoCount: pNoCount,
|
|
|
|
|
BlankParams: pBlankparms,
|
|
|
|
|
AllowFilter: pAllowFilter,
|
|
|
|
|
NoCount: options.NoCount,
|
|
|
|
|
BlankParams: options.BlankParams,
|
|
|
|
|
AllowFilter: options.AllowFilter,
|
|
|
|
|
ComplexAPI: complexAPI,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -131,13 +155,13 @@ func (h *Handler) SqlQueryList(sqlquery string, pNoCount, pBlankparms, pAllowFil
|
|
|
|
|
complexAPI = reqParams.ComplexAPI
|
|
|
|
|
|
|
|
|
|
// Merge query string parameters
|
|
|
|
|
sqlquery = h.mergeQueryParams(r, sqlquery, variables, pAllowFilter, propQry)
|
|
|
|
|
sqlquery = h.mergeQueryParams(r, sqlquery, variables, options.AllowFilter, propQry)
|
|
|
|
|
|
|
|
|
|
// Merge header parameters
|
|
|
|
|
sqlquery = h.mergeHeaderParams(r, sqlquery, variables, propQry, &complexAPI)
|
|
|
|
|
|
|
|
|
|
// Apply filters from parsed parameters (if not already applied by pAllowFilter)
|
|
|
|
|
if !pAllowFilter {
|
|
|
|
|
if !options.AllowFilter {
|
|
|
|
|
sqlquery = h.ApplyFilters(sqlquery, reqParams)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -149,7 +173,7 @@ func (h *Handler) SqlQueryList(sqlquery string, pNoCount, pBlankparms, pAllowFil
|
|
|
|
|
|
|
|
|
|
// Override pNoCount if skipcount is specified
|
|
|
|
|
if reqParams.SkipCount {
|
|
|
|
|
pNoCount = true
|
|
|
|
|
options.NoCount = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Build metainfo
|
|
|
|
|
@@ -164,7 +188,7 @@ func (h *Handler) SqlQueryList(sqlquery string, pNoCount, pBlankparms, pAllowFil
|
|
|
|
|
sqlquery = h.replaceMetaVariables(sqlquery, r, userCtx, metainfo, variables)
|
|
|
|
|
|
|
|
|
|
// Remove unused input variables
|
|
|
|
|
if pBlankparms {
|
|
|
|
|
if options.BlankParams {
|
|
|
|
|
for _, kw := range inputvars {
|
|
|
|
|
replacement := getReplacementForBlankParam(sqlquery, kw)
|
|
|
|
|
sqlquery = strings.ReplaceAll(sqlquery, kw, replacement)
|
|
|
|
|
@@ -205,7 +229,7 @@ func (h *Handler) SqlQueryList(sqlquery string, pNoCount, pBlankparms, pAllowFil
|
|
|
|
|
sqlquery = fmt.Sprintf("%s \nORDER BY %s", sqlquery, ValidSQL(sortcols, "select"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !pNoCount {
|
|
|
|
|
if !options.NoCount {
|
|
|
|
|
if limit > 0 && offset > 0 {
|
|
|
|
|
sqlquery = fmt.Sprintf("%s \nLIMIT %d OFFSET %d", sqlquery, limit, offset)
|
|
|
|
|
} else if limit > 0 {
|
|
|
|
|
@@ -244,7 +268,7 @@ func (h *Handler) SqlQueryList(sqlquery string, pNoCount, pBlankparms, pAllowFil
|
|
|
|
|
// Normalize PostgreSQL types for proper JSON marshaling
|
|
|
|
|
dbobjlist = normalizePostgresTypesList(rows)
|
|
|
|
|
|
|
|
|
|
if pNoCount {
|
|
|
|
|
if options.NoCount {
|
|
|
|
|
total = int64(len(dbobjlist))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -386,7 +410,7 @@ func (h *Handler) SqlQueryList(sqlquery string, pNoCount, pBlankparms, pAllowFil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SqlQuery creates an HTTP handler that executes a SQL query and returns a single record
|
|
|
|
|
func (h *Handler) SqlQuery(sqlquery string, pBlankparms bool) HTTPFuncType {
|
|
|
|
|
func (h *Handler) SqlQuery(sqlquery string, options SqlQueryOptions) HTTPFuncType {
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
defer func() {
|
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
|
@@ -406,6 +430,7 @@ func (h *Handler) SqlQuery(sqlquery string, pBlankparms bool) HTTPFuncType {
|
|
|
|
|
inputvars := make([]string, 0)
|
|
|
|
|
metainfo := make(map[string]interface{})
|
|
|
|
|
variables := make(map[string]interface{})
|
|
|
|
|
|
|
|
|
|
dbobj := make(map[string]interface{})
|
|
|
|
|
complexAPI := false
|
|
|
|
|
|
|
|
|
|
@@ -430,7 +455,7 @@ func (h *Handler) SqlQuery(sqlquery string, pBlankparms bool) HTTPFuncType {
|
|
|
|
|
MetaInfo: metainfo,
|
|
|
|
|
PropQry: propQry,
|
|
|
|
|
UserContext: userCtx,
|
|
|
|
|
BlankParams: pBlankparms,
|
|
|
|
|
BlankParams: options.BlankParams,
|
|
|
|
|
ComplexAPI: complexAPI,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -507,7 +532,7 @@ func (h *Handler) SqlQuery(sqlquery string, pBlankparms bool) HTTPFuncType {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove unused input variables
|
|
|
|
|
if pBlankparms {
|
|
|
|
|
if options.BlankParams {
|
|
|
|
|
for _, kw := range inputvars {
|
|
|
|
|
replacement := getReplacementForBlankParam(sqlquery, kw)
|
|
|
|
|
sqlquery = strings.ReplaceAll(sqlquery, kw, replacement)
|
|
|
|
|
@@ -631,8 +656,18 @@ func (h *Handler) extractInputVariables(sqlquery string, inputvars *[]string) st
|
|
|
|
|
|
|
|
|
|
// mergePathParams merges URL path parameters into the SQL query
|
|
|
|
|
func (h *Handler) mergePathParams(r *http.Request, sqlquery string, variables map[string]interface{}) string {
|
|
|
|
|
// Note: Path parameters would typically come from a router like gorilla/mux
|
|
|
|
|
// For now, this is a placeholder for path parameter extraction
|
|
|
|
|
|
|
|
|
|
if h.GetVariablesCallback() != nil {
|
|
|
|
|
pathVars := h.GetVariablesCallback()(r)
|
|
|
|
|
for k, v := range pathVars {
|
|
|
|
|
kword := fmt.Sprintf("[%s]", k)
|
|
|
|
|
if strings.Contains(sqlquery, kword) {
|
|
|
|
|
sqlquery = strings.ReplaceAll(sqlquery, kword, fmt.Sprintf("%v", v))
|
|
|
|
|
}
|
|
|
|
|
variables[k] = v
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return sqlquery
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|