mirror of
				https://github.com/oarkflow/mq.git
				synced 2025-11-01 01:52:35 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			344 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			344 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package handlers
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/oarkflow/json"
 | |
| 	"github.com/oarkflow/mq"
 | |
| 	"github.com/oarkflow/mq/dag"
 | |
| )
 | |
| 
 | |
| // FieldHandler handles field manipulation operations (filter, add, remove, rename, etc.)
 | |
| type FieldHandler struct {
 | |
| 	dag.Operation
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) ProcessTask(ctx context.Context, task *mq.Task) mq.Result {
 | |
| 	data, err := dag.UnmarshalPayload[map[string]any](ctx, task.Payload)
 | |
| 	if err != nil {
 | |
| 		return mq.Result{Error: fmt.Errorf("failed to unmarshal task payload: %w", err), Ctx: ctx}
 | |
| 	}
 | |
| 
 | |
| 	operation, ok := h.Payload.Data["operation"].(string)
 | |
| 	if !ok {
 | |
| 		return mq.Result{Error: fmt.Errorf("operation not specified")}
 | |
| 	}
 | |
| 
 | |
| 	var result map[string]any
 | |
| 	switch operation {
 | |
| 	case "filter", "select":
 | |
| 		result = h.filterFields(data)
 | |
| 	case "exclude", "remove":
 | |
| 		result = h.excludeFields(data)
 | |
| 	case "rename":
 | |
| 		result = h.renameFields(data)
 | |
| 	case "add", "set":
 | |
| 		result = h.addFields(data)
 | |
| 	case "copy":
 | |
| 		result = h.copyFields(data)
 | |
| 	case "merge":
 | |
| 		result = h.mergeFields(data)
 | |
| 	case "prefix":
 | |
| 		result = h.prefixFields(data)
 | |
| 	case "suffix":
 | |
| 		result = h.suffixFields(data)
 | |
| 	case "transform_keys":
 | |
| 		result = h.transformKeys(data)
 | |
| 	default:
 | |
| 		return mq.Result{Error: fmt.Errorf("unsupported operation: %s", operation)}
 | |
| 	}
 | |
| 
 | |
| 	resultPayload, err := json.Marshal(result)
 | |
| 	if err != nil {
 | |
| 		return mq.Result{Error: fmt.Errorf("failed to marshal result: %w", err)}
 | |
| 	}
 | |
| 
 | |
| 	return mq.Result{Payload: resultPayload, Ctx: ctx}
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) filterFields(data map[string]any) map[string]any {
 | |
| 	result := make(map[string]any)
 | |
| 	allowedFields := h.getTargetFields()
 | |
| 
 | |
| 	if len(allowedFields) == 0 {
 | |
| 		return data // If no fields specified, return all
 | |
| 	}
 | |
| 
 | |
| 	for _, field := range allowedFields {
 | |
| 		if val, ok := data[field]; ok {
 | |
| 			result[field] = val
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) excludeFields(data map[string]any) map[string]any {
 | |
| 	result := make(map[string]any)
 | |
| 	excludeFields := h.getTargetFields()
 | |
| 
 | |
| 	// Copy all fields except excluded ones
 | |
| 	for key, value := range data {
 | |
| 		if !contains(excludeFields, key) {
 | |
| 			result[key] = value
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) renameFields(data map[string]any) map[string]any {
 | |
| 	result := make(map[string]any)
 | |
| 	renameMap := h.getFieldMapping()
 | |
| 
 | |
| 	// Copy all fields, renaming as specified
 | |
| 	for key, value := range data {
 | |
| 		if newKey, ok := renameMap[key]; ok {
 | |
| 			result[newKey] = value
 | |
| 		} else {
 | |
| 			result[key] = value
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) addFields(data map[string]any) map[string]any {
 | |
| 	result := make(map[string]any)
 | |
| 	newFields := h.getNewFields()
 | |
| 
 | |
| 	// Copy all original data
 | |
| 	for key, value := range data {
 | |
| 		result[key] = value
 | |
| 	}
 | |
| 
 | |
| 	// Add new fields
 | |
| 	for key, value := range newFields {
 | |
| 		result[key] = value
 | |
| 	}
 | |
| 
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) copyFields(data map[string]any) map[string]any {
 | |
| 	result := make(map[string]any)
 | |
| 	copyMap := h.getFieldMapping()
 | |
| 
 | |
| 	// Copy all original data
 | |
| 	for key, value := range data {
 | |
| 		result[key] = value
 | |
| 	}
 | |
| 
 | |
| 	// Copy fields to new names
 | |
| 	for sourceKey, targetKey := range copyMap {
 | |
| 		if val, ok := data[sourceKey]; ok {
 | |
| 			result[targetKey] = val
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) mergeFields(data map[string]any) map[string]any {
 | |
| 	result := make(map[string]any)
 | |
| 	mergeConfig := h.getMergeConfig()
 | |
| 
 | |
| 	// Copy all original data
 | |
| 	for key, value := range data {
 | |
| 		result[key] = value
 | |
| 	}
 | |
| 
 | |
| 	// Merge fields
 | |
| 	for targetField, config := range mergeConfig {
 | |
| 		sourceFields := config["fields"].([]string)
 | |
| 		separator := config["separator"].(string)
 | |
| 
 | |
| 		var values []string
 | |
| 		for _, field := range sourceFields {
 | |
| 			if val, ok := data[field]; ok && val != nil {
 | |
| 				values = append(values, fmt.Sprintf("%v", val))
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if len(values) > 0 {
 | |
| 			result[targetField] = strings.Join(values, separator)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) prefixFields(data map[string]any) map[string]any {
 | |
| 	result := make(map[string]any)
 | |
| 	prefix := h.getPrefix()
 | |
| 	targetFields := h.getTargetFields()
 | |
| 
 | |
| 	for key, value := range data {
 | |
| 		if len(targetFields) == 0 || contains(targetFields, key) {
 | |
| 			result[prefix+key] = value
 | |
| 		} else {
 | |
| 			result[key] = value
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) suffixFields(data map[string]any) map[string]any {
 | |
| 	result := make(map[string]any)
 | |
| 	suffix := h.getSuffix()
 | |
| 	targetFields := h.getTargetFields()
 | |
| 
 | |
| 	for key, value := range data {
 | |
| 		if len(targetFields) == 0 || contains(targetFields, key) {
 | |
| 			result[key+suffix] = value
 | |
| 		} else {
 | |
| 			result[key] = value
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) transformKeys(data map[string]any) map[string]any {
 | |
| 	result := make(map[string]any)
 | |
| 	transformation := h.getKeyTransformation()
 | |
| 
 | |
| 	for key, value := range data {
 | |
| 		newKey := h.applyKeyTransformation(key, transformation)
 | |
| 		result[newKey] = value
 | |
| 	}
 | |
| 
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) applyKeyTransformation(key string, transformation string) string {
 | |
| 	switch transformation {
 | |
| 	case "lowercase":
 | |
| 		return strings.ToLower(key)
 | |
| 	case "uppercase":
 | |
| 		return strings.ToUpper(key)
 | |
| 	case "snake_case":
 | |
| 		return h.toSnakeCase(key)
 | |
| 	case "camel_case":
 | |
| 		return h.toCamelCase(key)
 | |
| 	case "kebab_case":
 | |
| 		return h.toKebabCase(key)
 | |
| 	case "pascal_case":
 | |
| 		return h.toPascalCase(key)
 | |
| 	default:
 | |
| 		return key
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) toSnakeCase(s string) string {
 | |
| 	result := strings.ReplaceAll(s, " ", "_")
 | |
| 	result = strings.ReplaceAll(result, "-", "_")
 | |
| 	return strings.ToLower(result)
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) toCamelCase(s string) string {
 | |
| 	parts := strings.FieldsFunc(s, func(c rune) bool {
 | |
| 		return c == ' ' || c == '_' || c == '-'
 | |
| 	})
 | |
| 
 | |
| 	if len(parts) == 0 {
 | |
| 		return s
 | |
| 	}
 | |
| 
 | |
| 	result := strings.ToLower(parts[0])
 | |
| 	for _, part := range parts[1:] {
 | |
| 		if len(part) > 0 {
 | |
| 			result += strings.ToUpper(part[:1]) + strings.ToLower(part[1:])
 | |
| 		}
 | |
| 	}
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) toKebabCase(s string) string {
 | |
| 	result := strings.ReplaceAll(s, " ", "-")
 | |
| 	result = strings.ReplaceAll(result, "_", "-")
 | |
| 	return strings.ToLower(result)
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) toPascalCase(s string) string {
 | |
| 	camel := h.toCamelCase(s)
 | |
| 	if len(camel) > 0 {
 | |
| 		return strings.ToUpper(camel[:1]) + camel[1:]
 | |
| 	}
 | |
| 	return camel
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) getTargetFields() []string {
 | |
| 	if fields, ok := h.Payload.Data["fields"].([]any); ok {
 | |
| 		var result []string
 | |
| 		for _, field := range fields {
 | |
| 			if str, ok := field.(string); ok {
 | |
| 				result = append(result, str)
 | |
| 			}
 | |
| 		}
 | |
| 		return result
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) getFieldMapping() map[string]string {
 | |
| 	result := make(map[string]string)
 | |
| 	if mapping, ok := h.Payload.Data["mapping"].(map[string]any); ok {
 | |
| 		for key, value := range mapping {
 | |
| 			if str, ok := value.(string); ok {
 | |
| 				result[key] = str
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) getNewFields() map[string]any {
 | |
| 	if fields, ok := h.Payload.Data["new_fields"].(map[string]any); ok {
 | |
| 		return fields
 | |
| 	}
 | |
| 	return make(map[string]any)
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) getMergeConfig() map[string]map[string]any {
 | |
| 	result := make(map[string]map[string]any)
 | |
| 	if config, ok := h.Payload.Data["merge_config"].(map[string]any); ok {
 | |
| 		for key, value := range config {
 | |
| 			if configMap, ok := value.(map[string]any); ok {
 | |
| 				result[key] = configMap
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) getPrefix() string {
 | |
| 	if prefix, ok := h.Payload.Data["prefix"].(string); ok {
 | |
| 		return prefix
 | |
| 	}
 | |
| 	return ""
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) getSuffix() string {
 | |
| 	if suffix, ok := h.Payload.Data["suffix"].(string); ok {
 | |
| 		return suffix
 | |
| 	}
 | |
| 	return ""
 | |
| }
 | |
| 
 | |
| func (h *FieldHandler) getKeyTransformation() string {
 | |
| 	if transform, ok := h.Payload.Data["transformation"].(string); ok {
 | |
| 		return transform
 | |
| 	}
 | |
| 	return ""
 | |
| }
 | |
| 
 | |
| func NewFieldHandler(id string) *FieldHandler {
 | |
| 	return &FieldHandler{
 | |
| 		Operation: dag.Operation{ID: id, Key: "field", Type: dag.Function, Tags: []string{"data", "transformation", "field"}},
 | |
| 	}
 | |
| }
 | 
