mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-06-09 07:13:44 +00:00
fix(handler): validate nested request structure for relations
* added checks for valid _request values in single and multiple relations * introduced isValidNestedRequest function to encapsulate validation logic fix(crud): expand operation handling for nested CUD * added "add" to insert operations and "modify" to update operations * included "remove" in delete operations
This commit is contained in:
@@ -113,7 +113,7 @@ func (p *NestedCUDProcessor) ProcessNestedCUD(
|
|||||||
|
|
||||||
// Process based on operation
|
// Process based on operation
|
||||||
switch strings.ToLower(operation) {
|
switch strings.ToLower(operation) {
|
||||||
case "insert", "create":
|
case "insert", "create", "add":
|
||||||
// Only perform insert if we have data to insert
|
// Only perform insert if we have data to insert
|
||||||
if hasData {
|
if hasData {
|
||||||
id, err := p.processInsert(ctx, regularData, tableName)
|
id, err := p.processInsert(ctx, regularData, tableName)
|
||||||
@@ -141,7 +141,7 @@ func (p *NestedCUDProcessor) ProcessNestedCUD(
|
|||||||
logger.Debug("Skipping insert for %s - no data columns besides _request", tableName)
|
logger.Debug("Skipping insert for %s - no data columns besides _request", tableName)
|
||||||
}
|
}
|
||||||
|
|
||||||
case "update", "change":
|
case "update", "change", "modify":
|
||||||
// Only perform update if we have data to update
|
// Only perform update if we have data to update
|
||||||
if reflection.IsEmptyValue(data[pkName]) {
|
if reflection.IsEmptyValue(data[pkName]) {
|
||||||
logger.Warn("Skipping update for %s - no primary key", tableName)
|
logger.Warn("Skipping update for %s - no primary key", tableName)
|
||||||
@@ -174,7 +174,7 @@ func (p *NestedCUDProcessor) ProcessNestedCUD(
|
|||||||
result.ID = data[pkName]
|
result.ID = data[pkName]
|
||||||
}
|
}
|
||||||
|
|
||||||
case "delete":
|
case "delete", "remove":
|
||||||
if reflection.IsEmptyValue(data[pkName]) {
|
if reflection.IsEmptyValue(data[pkName]) {
|
||||||
logger.Warn("Skipping delete for %s - no primary key", tableName)
|
logger.Warn("Skipping delete for %s - no primary key", tableName)
|
||||||
return result, nil
|
return result, nil
|
||||||
|
|||||||
@@ -2043,7 +2043,10 @@ func (h *Handler) processChildRelationsForField(
|
|||||||
// Process based on relation type and data structure
|
// Process based on relation type and data structure
|
||||||
switch v := relationValue.(type) {
|
switch v := relationValue.(type) {
|
||||||
case map[string]interface{}:
|
case map[string]interface{}:
|
||||||
// Single related object - add parent ID to foreign key field
|
if !isValidNestedRequest(v) {
|
||||||
|
logger.Debug("Skipping single relation %s - missing or invalid _request value", relationName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
// IMPORTANT: In recursive relationships, don't overwrite the primary key
|
// IMPORTANT: In recursive relationships, don't overwrite the primary key
|
||||||
if parentID != nil && foreignKeyFieldName != "" && foreignKeyFieldName != childPKFieldName {
|
if parentID != nil && foreignKeyFieldName != "" && foreignKeyFieldName != childPKFieldName {
|
||||||
v[foreignKeyFieldName] = parentID
|
v[foreignKeyFieldName] = parentID
|
||||||
@@ -2060,7 +2063,10 @@ func (h *Handler) processChildRelationsForField(
|
|||||||
// Multiple related objects
|
// Multiple related objects
|
||||||
for i, item := range v {
|
for i, item := range v {
|
||||||
if itemMap, ok := item.(map[string]interface{}); ok {
|
if itemMap, ok := item.(map[string]interface{}); ok {
|
||||||
// Add parent ID to foreign key field
|
if !isValidNestedRequest(itemMap) {
|
||||||
|
logger.Debug("Skipping relation array[%d] %s - missing or invalid _request value", i, relationName)
|
||||||
|
continue
|
||||||
|
}
|
||||||
// IMPORTANT: In recursive relationships, don't overwrite the primary key
|
// IMPORTANT: In recursive relationships, don't overwrite the primary key
|
||||||
if parentID != nil && foreignKeyFieldName != "" && foreignKeyFieldName != childPKFieldName {
|
if parentID != nil && foreignKeyFieldName != "" && foreignKeyFieldName != childPKFieldName {
|
||||||
itemMap[foreignKeyFieldName] = parentID
|
itemMap[foreignKeyFieldName] = parentID
|
||||||
@@ -2078,7 +2084,10 @@ func (h *Handler) processChildRelationsForField(
|
|||||||
case []map[string]interface{}:
|
case []map[string]interface{}:
|
||||||
// Multiple related objects (typed slice)
|
// Multiple related objects (typed slice)
|
||||||
for i, itemMap := range v {
|
for i, itemMap := range v {
|
||||||
// Add parent ID to foreign key field
|
if !isValidNestedRequest(itemMap) {
|
||||||
|
logger.Debug("Skipping relation typed array[%d] %s - missing or invalid _request value", i, relationName)
|
||||||
|
continue
|
||||||
|
}
|
||||||
// IMPORTANT: In recursive relationships, don't overwrite the primary key
|
// IMPORTANT: In recursive relationships, don't overwrite the primary key
|
||||||
if parentID != nil && foreignKeyFieldName != "" && foreignKeyFieldName != childPKFieldName {
|
if parentID != nil && foreignKeyFieldName != "" && foreignKeyFieldName != childPKFieldName {
|
||||||
itemMap[foreignKeyFieldName] = parentID
|
itemMap[foreignKeyFieldName] = parentID
|
||||||
@@ -2099,6 +2108,24 @@ func (h *Handler) processChildRelationsForField(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isValidNestedRequest returns true only when the item carries a _request key
|
||||||
|
// whose value is one of the recognised mutation verbs.
|
||||||
|
func isValidNestedRequest(item map[string]interface{}) bool {
|
||||||
|
raw, ok := item["_request"]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s, ok := raw.(string)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch strings.ToLower(strings.TrimSpace(s)) {
|
||||||
|
case "insert", "create", "add", "change", "update", "modify", "delete", "remove":
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// getTableNameForRelatedModel gets the table name for a related model.
|
// getTableNameForRelatedModel gets the table name for a related model.
|
||||||
// If the model's TableName() is schema-qualified (e.g. "public.users") the
|
// If the model's TableName() is schema-qualified (e.g. "public.users") the
|
||||||
// separator is adjusted for the active driver: underscore for SQLite, dot otherwise.
|
// separator is adjusted for the active driver: underscore for SQLite, dot otherwise.
|
||||||
|
|||||||
Reference in New Issue
Block a user