Compare commits

...

3 Commits

Author SHA1 Message Date
Hein
7ef1d6424a Better handling for variables callback
Some checks are pending
Build , Vet Test, and Lint / Run Vet Tests (1.23.x) (push) Waiting to run
Build , Vet Test, and Lint / Run Vet Tests (1.24.x) (push) Waiting to run
Build , Vet Test, and Lint / Lint Code (push) Waiting to run
Build , Vet Test, and Lint / Build (push) Waiting to run
Tests / Unit Tests (push) Waiting to run
Tests / Integration Tests (push) Waiting to run
2025-12-11 15:57:01 +02:00
Hein
c50eeac5bf Change the SqlQuery functions parameters on Function Spec 2025-12-11 15:42:00 +02:00
Hein
6d88f2668a Updated login interface with meta 2025-12-11 14:05:27 +02:00
4 changed files with 60 additions and 24 deletions

View File

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

View File

@@ -532,7 +532,7 @@ func TestSqlQuery(t *testing.T) {
req := createTestRequest("GET", "/test", tt.queryParams, tt.headers, nil) req := createTestRequest("GET", "/test", tt.queryParams, tt.headers, nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handlerFunc := handler.SqlQuery(tt.sqlQuery, tt.blankParams) handlerFunc := handler.SqlQuery(tt.sqlQuery, SqlQueryOptions{BlankParams: tt.blankParams})
handlerFunc(w, req) handlerFunc(w, req)
if w.Code != tt.expectedStatus { if w.Code != tt.expectedStatus {
@@ -655,7 +655,7 @@ func TestSqlQueryList(t *testing.T) {
req := createTestRequest("GET", "/test", tt.queryParams, tt.headers, nil) req := createTestRequest("GET", "/test", tt.queryParams, tt.headers, nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handlerFunc := handler.SqlQueryList(tt.sqlQuery, tt.noCount, tt.blankParams, tt.allowFilter) handlerFunc := handler.SqlQueryList(tt.sqlQuery, SqlQueryOptions{NoCount: tt.noCount, BlankParams: tt.blankParams, AllowFilter: tt.allowFilter})
handlerFunc(w, req) handlerFunc(w, req)
if w.Code != tt.expectedStatus { if w.Code != tt.expectedStatus {

View File

@@ -576,7 +576,7 @@ func TestHookIntegrationWithHandler(t *testing.T) {
req := createTestRequest("GET", "/test", nil, nil, nil) req := createTestRequest("GET", "/test", nil, nil, nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
handlerFunc := handler.SqlQuery("SELECT * FROM users WHERE id = 1", false) handlerFunc := handler.SqlQuery("SELECT * FROM users WHERE id = 1", SqlQueryOptions{})
handlerFunc(w, req) handlerFunc(w, req)
if !hookCalled { if !hookCalled {

View File

@@ -33,6 +33,7 @@ type LoginResponse struct {
RefreshToken string `json:"refresh_token"` RefreshToken string `json:"refresh_token"`
User *UserContext `json:"user"` User *UserContext `json:"user"`
ExpiresIn int64 `json:"expires_in"` // Token expiration in seconds ExpiresIn int64 `json:"expires_in"` // Token expiration in seconds
Meta map[string]any `json:"meta"` // Additional metadata to be set on user context
} }
// LogoutRequest contains information for logout // LogoutRequest contains information for logout