mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-03-13 08:56:51 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
613bf22cbd | ||
| d1ae4fe64e | |||
| 254102bfac | |||
| 6c27419dbc |
@@ -32,7 +32,8 @@ func GetCursorFilter(
|
|||||||
modelColumns []string,
|
modelColumns []string,
|
||||||
options common.RequestOptions,
|
options common.RequestOptions,
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
// Remove schema prefix if present
|
// Separate schema prefix from bare table name
|
||||||
|
fullTableName := tableName
|
||||||
if strings.Contains(tableName, ".") {
|
if strings.Contains(tableName, ".") {
|
||||||
tableName = strings.SplitN(tableName, ".", 2)[1]
|
tableName = strings.SplitN(tableName, ".", 2)[1]
|
||||||
}
|
}
|
||||||
@@ -115,7 +116,7 @@ func GetCursorFilter(
|
|||||||
WHERE cursor_select.%s = %s
|
WHERE cursor_select.%s = %s
|
||||||
AND (%s)
|
AND (%s)
|
||||||
)`,
|
)`,
|
||||||
tableName,
|
fullTableName,
|
||||||
pkName,
|
pkName,
|
||||||
cursorID,
|
cursorID,
|
||||||
orSQL,
|
orSQL,
|
||||||
|
|||||||
@@ -175,9 +175,9 @@ func TestGetCursorFilter_WithSchemaPrefix(t *testing.T) {
|
|||||||
t.Fatalf("GetCursorFilter failed: %v", err)
|
t.Fatalf("GetCursorFilter failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should handle schema prefix properly
|
// Should include full schema-qualified name in FROM clause
|
||||||
if !strings.Contains(filter, "users") {
|
if !strings.Contains(filter, "public.users") {
|
||||||
t.Errorf("Filter should reference table name users, got: %s", filter)
|
t.Errorf("Filter FROM clause should use schema-qualified name public.users, got: %s", filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("Generated cursor filter with schema: %s", filter)
|
t.Logf("Generated cursor filter with schema: %s", filter)
|
||||||
|
|||||||
@@ -1521,22 +1521,22 @@ func (h *Handler) buildFilterCondition(filter common.FilterOption) (conditionStr
|
|||||||
var args []interface{}
|
var args []interface{}
|
||||||
|
|
||||||
switch filter.Operator {
|
switch filter.Operator {
|
||||||
case "eq":
|
case "eq", "=":
|
||||||
condition = fmt.Sprintf("%s = ?", filter.Column)
|
condition = fmt.Sprintf("%s = ?", filter.Column)
|
||||||
args = []interface{}{filter.Value}
|
args = []interface{}{filter.Value}
|
||||||
case "neq":
|
case "neq", "!=", "<>":
|
||||||
condition = fmt.Sprintf("%s != ?", filter.Column)
|
condition = fmt.Sprintf("%s != ?", filter.Column)
|
||||||
args = []interface{}{filter.Value}
|
args = []interface{}{filter.Value}
|
||||||
case "gt":
|
case "gt", ">":
|
||||||
condition = fmt.Sprintf("%s > ?", filter.Column)
|
condition = fmt.Sprintf("%s > ?", filter.Column)
|
||||||
args = []interface{}{filter.Value}
|
args = []interface{}{filter.Value}
|
||||||
case "gte":
|
case "gte", ">=":
|
||||||
condition = fmt.Sprintf("%s >= ?", filter.Column)
|
condition = fmt.Sprintf("%s >= ?", filter.Column)
|
||||||
args = []interface{}{filter.Value}
|
args = []interface{}{filter.Value}
|
||||||
case "lt":
|
case "lt", "<":
|
||||||
condition = fmt.Sprintf("%s < ?", filter.Column)
|
condition = fmt.Sprintf("%s < ?", filter.Column)
|
||||||
args = []interface{}{filter.Value}
|
args = []interface{}{filter.Value}
|
||||||
case "lte":
|
case "lte", "<=":
|
||||||
condition = fmt.Sprintf("%s <= ?", filter.Column)
|
condition = fmt.Sprintf("%s <= ?", filter.Column)
|
||||||
args = []interface{}{filter.Value}
|
args = []interface{}{filter.Value}
|
||||||
case "like":
|
case "like":
|
||||||
@@ -1565,22 +1565,22 @@ func (h *Handler) applyFilter(query common.SelectQuery, filter common.FilterOpti
|
|||||||
var args []interface{}
|
var args []interface{}
|
||||||
|
|
||||||
switch filter.Operator {
|
switch filter.Operator {
|
||||||
case "eq":
|
case "eq", "=":
|
||||||
condition = fmt.Sprintf("%s = ?", filter.Column)
|
condition = fmt.Sprintf("%s = ?", filter.Column)
|
||||||
args = []interface{}{filter.Value}
|
args = []interface{}{filter.Value}
|
||||||
case "neq":
|
case "neq", "!=", "<>":
|
||||||
condition = fmt.Sprintf("%s != ?", filter.Column)
|
condition = fmt.Sprintf("%s != ?", filter.Column)
|
||||||
args = []interface{}{filter.Value}
|
args = []interface{}{filter.Value}
|
||||||
case "gt":
|
case "gt", ">":
|
||||||
condition = fmt.Sprintf("%s > ?", filter.Column)
|
condition = fmt.Sprintf("%s > ?", filter.Column)
|
||||||
args = []interface{}{filter.Value}
|
args = []interface{}{filter.Value}
|
||||||
case "gte":
|
case "gte", ">=":
|
||||||
condition = fmt.Sprintf("%s >= ?", filter.Column)
|
condition = fmt.Sprintf("%s >= ?", filter.Column)
|
||||||
args = []interface{}{filter.Value}
|
args = []interface{}{filter.Value}
|
||||||
case "lt":
|
case "lt", "<":
|
||||||
condition = fmt.Sprintf("%s < ?", filter.Column)
|
condition = fmt.Sprintf("%s < ?", filter.Column)
|
||||||
args = []interface{}{filter.Value}
|
args = []interface{}{filter.Value}
|
||||||
case "lte":
|
case "lte", "<=":
|
||||||
condition = fmt.Sprintf("%s <= ?", filter.Column)
|
condition = fmt.Sprintf("%s <= ?", filter.Column)
|
||||||
args = []interface{}{filter.Value}
|
args = []interface{}{filter.Value}
|
||||||
case "like":
|
case "like":
|
||||||
|
|||||||
@@ -70,17 +70,17 @@ func SetupMuxRoutes(muxRouter *mux.Router, handler *Handler, authMiddleware Midd
|
|||||||
entityWithIDPath := buildRoutePath(schema, entity) + "/{id}"
|
entityWithIDPath := buildRoutePath(schema, entity) + "/{id}"
|
||||||
|
|
||||||
// Create handler functions for this specific entity
|
// Create handler functions for this specific entity
|
||||||
postEntityHandler := createMuxHandler(handler, schema, entity, "")
|
var postEntityHandler http.Handler = createMuxHandler(handler, schema, entity, "")
|
||||||
postEntityWithIDHandler := createMuxHandler(handler, schema, entity, "id")
|
var postEntityWithIDHandler http.Handler = createMuxHandler(handler, schema, entity, "id")
|
||||||
getEntityHandler := createMuxGetHandler(handler, schema, entity, "")
|
var getEntityHandler http.Handler = createMuxGetHandler(handler, schema, entity, "")
|
||||||
optionsEntityHandler := createMuxOptionsHandler(handler, schema, entity, []string{"GET", "POST", "OPTIONS"})
|
optionsEntityHandler := createMuxOptionsHandler(handler, schema, entity, []string{"GET", "POST", "OPTIONS"})
|
||||||
optionsEntityWithIDHandler := createMuxOptionsHandler(handler, schema, entity, []string{"POST", "OPTIONS"})
|
optionsEntityWithIDHandler := createMuxOptionsHandler(handler, schema, entity, []string{"POST", "OPTIONS"})
|
||||||
|
|
||||||
// Apply authentication middleware if provided
|
// Apply authentication middleware if provided
|
||||||
if authMiddleware != nil {
|
if authMiddleware != nil {
|
||||||
postEntityHandler = authMiddleware(postEntityHandler).(http.HandlerFunc)
|
postEntityHandler = authMiddleware(postEntityHandler)
|
||||||
postEntityWithIDHandler = authMiddleware(postEntityWithIDHandler).(http.HandlerFunc)
|
postEntityWithIDHandler = authMiddleware(postEntityWithIDHandler)
|
||||||
getEntityHandler = authMiddleware(getEntityHandler).(http.HandlerFunc)
|
getEntityHandler = authMiddleware(getEntityHandler)
|
||||||
// Don't apply auth middleware to OPTIONS - CORS preflight must not require auth
|
// Don't apply auth middleware to OPTIONS - CORS preflight must not require auth
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,7 +225,11 @@ func wrapBunRouterHandler(handler bunrouter.HandlerFunc, authMiddleware Middlewa
|
|||||||
return func(w http.ResponseWriter, req bunrouter.Request) error {
|
return func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||||
// Create an http.Handler that calls the bunrouter handler
|
// Create an http.Handler that calls the bunrouter handler
|
||||||
httpHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
httpHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
_ = handler(w, req)
|
// Replace the embedded *http.Request with the middleware-enriched one
|
||||||
|
// so that auth context (user ID, etc.) is visible to the handler.
|
||||||
|
enrichedReq := req
|
||||||
|
enrichedReq.Request = r
|
||||||
|
_ = handler(w, enrichedReq)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Wrap with auth middleware and execute
|
// Wrap with auth middleware and execute
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ func (opts *ExtendedRequestOptions) GetCursorFilter(
|
|||||||
modelColumns []string, // optional: for validation
|
modelColumns []string, // optional: for validation
|
||||||
expandJoins map[string]string, // optional: alias → JOIN SQL
|
expandJoins map[string]string, // optional: alias → JOIN SQL
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
|
// Separate schema prefix from bare table name
|
||||||
|
fullTableName := tableName
|
||||||
if strings.Contains(tableName, ".") {
|
if strings.Contains(tableName, ".") {
|
||||||
tableName = strings.SplitN(tableName, ".", 2)[1]
|
tableName = strings.SplitN(tableName, ".", 2)[1]
|
||||||
}
|
}
|
||||||
@@ -127,7 +129,7 @@ func (opts *ExtendedRequestOptions) GetCursorFilter(
|
|||||||
WHERE cursor_select.%s = %s
|
WHERE cursor_select.%s = %s
|
||||||
AND (%s)
|
AND (%s)
|
||||||
)`,
|
)`,
|
||||||
tableName,
|
fullTableName,
|
||||||
joinSQL,
|
joinSQL,
|
||||||
pkName,
|
pkName,
|
||||||
cursorID,
|
cursorID,
|
||||||
|
|||||||
@@ -187,9 +187,9 @@ func TestGetCursorFilter_WithSchemaPrefix(t *testing.T) {
|
|||||||
t.Fatalf("GetCursorFilter failed: %v", err)
|
t.Fatalf("GetCursorFilter failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should handle schema prefix properly
|
// Should include full schema-qualified name in FROM clause
|
||||||
if !strings.Contains(filter, "users") {
|
if !strings.Contains(filter, "public.users") {
|
||||||
t.Errorf("Filter should reference table name users, got: %s", filter)
|
t.Errorf("Filter FROM clause should use schema-qualified name public.users, got: %s", filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("Generated cursor filter with schema: %s", filter)
|
t.Logf("Generated cursor filter with schema: %s", filter)
|
||||||
|
|||||||
@@ -2226,17 +2226,17 @@ func (h *Handler) applyOrFilterGroup(query common.SelectQuery, filters []*common
|
|||||||
// buildFilterCondition builds a single filter condition and returns the condition string and args
|
// buildFilterCondition builds a single filter condition and returns the condition string and args
|
||||||
func (h *Handler) buildFilterCondition(qualifiedColumn string, filter *common.FilterOption, tableName string) (filterStr string, filterInterface []interface{}) {
|
func (h *Handler) buildFilterCondition(qualifiedColumn string, filter *common.FilterOption, tableName string) (filterStr string, filterInterface []interface{}) {
|
||||||
switch strings.ToLower(filter.Operator) {
|
switch strings.ToLower(filter.Operator) {
|
||||||
case "eq", "equals":
|
case "eq", "equals", "=":
|
||||||
return fmt.Sprintf("%s = ?", qualifiedColumn), []interface{}{filter.Value}
|
return fmt.Sprintf("%s = ?", qualifiedColumn), []interface{}{filter.Value}
|
||||||
case "neq", "not_equals", "ne":
|
case "neq", "not_equals", "ne", "!=", "<>":
|
||||||
return fmt.Sprintf("%s != ?", qualifiedColumn), []interface{}{filter.Value}
|
return fmt.Sprintf("%s != ?", qualifiedColumn), []interface{}{filter.Value}
|
||||||
case "gt", "greater_than":
|
case "gt", "greater_than", ">":
|
||||||
return fmt.Sprintf("%s > ?", qualifiedColumn), []interface{}{filter.Value}
|
return fmt.Sprintf("%s > ?", qualifiedColumn), []interface{}{filter.Value}
|
||||||
case "gte", "greater_than_equals", "ge":
|
case "gte", "greater_than_equals", "ge", ">=":
|
||||||
return fmt.Sprintf("%s >= ?", qualifiedColumn), []interface{}{filter.Value}
|
return fmt.Sprintf("%s >= ?", qualifiedColumn), []interface{}{filter.Value}
|
||||||
case "lt", "less_than":
|
case "lt", "less_than", "<":
|
||||||
return fmt.Sprintf("%s < ?", qualifiedColumn), []interface{}{filter.Value}
|
return fmt.Sprintf("%s < ?", qualifiedColumn), []interface{}{filter.Value}
|
||||||
case "lte", "less_than_equals", "le":
|
case "lte", "less_than_equals", "le", "<=":
|
||||||
return fmt.Sprintf("%s <= ?", qualifiedColumn), []interface{}{filter.Value}
|
return fmt.Sprintf("%s <= ?", qualifiedColumn), []interface{}{filter.Value}
|
||||||
case "like":
|
case "like":
|
||||||
return fmt.Sprintf("%s LIKE ?", qualifiedColumn), []interface{}{filter.Value}
|
return fmt.Sprintf("%s LIKE ?", qualifiedColumn), []interface{}{filter.Value}
|
||||||
|
|||||||
@@ -125,17 +125,17 @@ func SetupMuxRoutes(muxRouter *mux.Router, handler *Handler, authMiddleware Midd
|
|||||||
metadataPath := buildRoutePath(schema, entity) + "/metadata"
|
metadataPath := buildRoutePath(schema, entity) + "/metadata"
|
||||||
|
|
||||||
// Create handler functions for this specific entity
|
// Create handler functions for this specific entity
|
||||||
entityHandler := createMuxHandler(handler, schema, entity, "")
|
var entityHandler http.Handler = createMuxHandler(handler, schema, entity, "")
|
||||||
entityWithIDHandler := createMuxHandler(handler, schema, entity, "id")
|
var entityWithIDHandler http.Handler = createMuxHandler(handler, schema, entity, "id")
|
||||||
metadataHandler := createMuxGetHandler(handler, schema, entity, "")
|
var metadataHandler http.Handler = createMuxGetHandler(handler, schema, entity, "")
|
||||||
optionsEntityHandler := createMuxOptionsHandler(handler, schema, entity, []string{"GET", "POST", "OPTIONS"})
|
optionsEntityHandler := createMuxOptionsHandler(handler, schema, entity, []string{"GET", "POST", "OPTIONS"})
|
||||||
optionsEntityWithIDHandler := createMuxOptionsHandler(handler, schema, entity, []string{"GET", "PUT", "PATCH", "DELETE", "POST", "OPTIONS"})
|
optionsEntityWithIDHandler := createMuxOptionsHandler(handler, schema, entity, []string{"GET", "PUT", "PATCH", "DELETE", "POST", "OPTIONS"})
|
||||||
|
|
||||||
// Apply authentication middleware if provided
|
// Apply authentication middleware if provided
|
||||||
if authMiddleware != nil {
|
if authMiddleware != nil {
|
||||||
entityHandler = authMiddleware(entityHandler).(http.HandlerFunc)
|
entityHandler = authMiddleware(entityHandler)
|
||||||
entityWithIDHandler = authMiddleware(entityWithIDHandler).(http.HandlerFunc)
|
entityWithIDHandler = authMiddleware(entityWithIDHandler)
|
||||||
metadataHandler = authMiddleware(metadataHandler).(http.HandlerFunc)
|
metadataHandler = authMiddleware(metadataHandler)
|
||||||
// Don't apply auth middleware to OPTIONS - CORS preflight must not require auth
|
// Don't apply auth middleware to OPTIONS - CORS preflight must not require auth
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,7 +289,11 @@ func wrapBunRouterHandler(handler bunrouter.HandlerFunc, authMiddleware Middlewa
|
|||||||
return func(w http.ResponseWriter, req bunrouter.Request) error {
|
return func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||||
// Create an http.Handler that calls the bunrouter handler
|
// Create an http.Handler that calls the bunrouter handler
|
||||||
httpHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
httpHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
_ = handler(w, req)
|
// Replace the embedded *http.Request with the middleware-enriched one
|
||||||
|
// so that auth context (user ID, etc.) is visible to the handler.
|
||||||
|
enrichedReq := req
|
||||||
|
enrichedReq.Request = r
|
||||||
|
_ = handler(w, enrichedReq)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Wrap with auth middleware and execute
|
// Wrap with auth middleware and execute
|
||||||
|
|||||||
Reference in New Issue
Block a user