diff --git a/essential/visible/service/helper/retriever/retriever.go b/essential/visible/service/helper/retriever/retriever.go index e69de29..ef0ee1b 100644 --- a/essential/visible/service/helper/retriever/retriever.go +++ b/essential/visible/service/helper/retriever/retriever.go @@ -0,0 +1,400 @@ +/* +|-------------------------------------------------------------------------- +| Description +|-------------------------------------------------------------------------- +| +| Name: +| - Retriever +| +| Purpose: +| - Retrieve data from supported resource handlers through a unified +| retrieval interface. +| +|-------------------------------------------------------------------------- +*/ + +/* +|-------------------------------------------------------------------------- +| Instruction +|-------------------------------------------------------------------------- +| +| Guideline: +| - Use for retrieving data from registered JSON resource handlers. +| - Use type-based dispatching to route retrieval requests. +| - Use resource retrieval to obtain a complete JSON resource. +| - Use path retrieval to obtain a specific nested value. +| - Use fields retrieval to obtain multiple nested values. +| - Do not use path and fields retrieval together. +| +| Example: +| - ObrimRetriever("json", map[string]any{ +| "resource": "metadata", +| }) +| +| - ObrimRetriever("json", map[string]any{ +| "resource": "metadata", +| "path": "app.name", +| }) +| +| - ObrimRetriever("json", map[string]any{ +| "resource": "metadata", +| "fields": []any{"app.name", "app.version"}, +| }) +| +|-------------------------------------------------------------------------- +*/ + +/* +|-------------------------------------------------------------------------- +| Credit +|-------------------------------------------------------------------------- +| +| Contributor: +| - Rajon Ahmed +| - Blockonite +| +|-------------------------------------------------------------------------- +*/ + +package retriever + +import ( + "fmt" + "strings" + "sync" +) + +const ( + // Utility type identifiers define supported retriever input types. + obrimRetrieverTypeJSON = "json" + + // Retriever result codes define successful execution outcomes. + obrimRetrieverSuccessResource = "SUCCESS_RESOURCE_RETRIEVED" + obrimRetrieverSuccessPath = "SUCCESS_PATH_RETRIEVED" + obrimRetrieverSuccessFields = "SUCCESS_FIELDS_RETRIEVED" + + // Retriever result codes define failed execution outcomes. + obrimRetrieverFailureInvalidType = "FAILURE_INVALID_TYPE" + obrimRetrieverFailureInvalidConfig = "FAILURE_INVALID_CONFIG" + obrimRetrieverFailureResourceRequired = "FAILURE_RESOURCE_REQUIRED" + obrimRetrieverFailurePathAndFields = "FAILURE_PATH_AND_FIELDS" + obrimRetrieverFailureInvalidPath = "FAILURE_INVALID_PATH" + obrimRetrieverFailureInvalidFields = "FAILURE_INVALID_FIELDS" + obrimRetrieverFailureResourceNotFound = "FAILURE_RESOURCE_NOT_FOUND" + obrimRetrieverFailureHandlerFailed = "FAILURE_HANDLER_FAILED" + obrimRetrieverFailurePathNotFound = "FAILURE_PATH_NOT_FOUND" + obrimRetrieverFailureFieldNotFound = "FAILURE_FIELD_NOT_FOUND" + obrimRetrieverFailureUnsupportedConfig = "FAILURE_UNSUPPORTED_CONFIG" +) + +var ( + // obrimRetrieverJsonHandlers stores registered JSON resource handlers. + obrimRetrieverJsonHandlers = map[string]func() map[string]any{} + + // obrimRetrieverJsonHandlersMutex protects the JSON handler registry. + obrimRetrieverJsonHandlersMutex sync.RWMutex +) + +// ObrimRetrieverResult represents the standardized retriever execution output. +type ObrimRetrieverResult struct { + Status bool `json:"status"` + Code string `json:"code"` + Payload map[string]any `json:"payload"` +} + +// ObrimRetriever retrieves data from a supported resource handler. +func ObrimRetriever( + typ string, + config map[string]any, +) map[string]any { + if code := obrimRetrieverValidateInput(typ, config); code != "" { + return obrimRetrieverBuildOutput(false, code, nil) + } + + return obrimRetrieverRouteRequest(typ, config) +} + +// ObrimRetrieverJsonRegister registers a JSON resource handler. +func ObrimRetrieverJsonRegister( + resource string, + handler func() map[string]any, +) { + if strings.TrimSpace(resource) == "" || handler == nil { + return + } + + obrimRetrieverJsonHandlersMutex.Lock() + defer obrimRetrieverJsonHandlersMutex.Unlock() + + obrimRetrieverJsonHandlers[resource] = handler +} + +// obrimRetrieverValidateInput validates the retriever type and configuration. +func obrimRetrieverValidateInput( + typ string, + config map[string]any, +) string { + switch typ { + case obrimRetrieverTypeJSON: + return obrimRetrieverValidateJSONConfig(config) + default: + return obrimRetrieverFailureInvalidType + } +} + +// obrimRetrieverValidateJSONConfig validates JSON retriever configuration. +func obrimRetrieverValidateJSONConfig(config map[string]any) string { + if config == nil { + return obrimRetrieverFailureInvalidConfig + } + + resourceValue, exists := config["resource"] + if !exists { + return obrimRetrieverFailureResourceRequired + } + + resource, ok := resourceValue.(string) + if !ok || strings.TrimSpace(resource) == "" { + return obrimRetrieverFailureResourceRequired + } + + _, hasPath := config["path"] + _, hasFields := config["fields"] + + if hasPath && hasFields { + return obrimRetrieverFailurePathAndFields + } + + for key := range config { + switch key { + case "resource", "path", "fields": + default: + return obrimRetrieverFailureUnsupportedConfig + } + } + + if hasPath { + path, ok := config["path"].(string) + if !ok || strings.TrimSpace(path) == "" { + return obrimRetrieverFailureInvalidPath + } + } + + if hasFields { + fields, ok := config["fields"].([]string) + if !ok || len(fields) == 0 { + return obrimRetrieverFailureInvalidFields + } + + for _, field := range fields { + if strings.TrimSpace(field) == "" { + return obrimRetrieverFailureInvalidFields + } + } + } + + return "" +} + +// obrimRetrieverRouteRequest routes the request to its type-specific entry function. +func obrimRetrieverRouteRequest( + typ string, + config map[string]any, +) map[string]any { + switch typ { + case obrimRetrieverTypeJSON: + return obrimRetrieverJson(config) + default: + return obrimRetrieverBuildOutput( + false, + obrimRetrieverFailureInvalidType, + nil, + ) + } +} + +// obrimRetrieverBuildOutput builds the standardized retriever output. +func obrimRetrieverBuildOutput( + status bool, + code string, + payload map[string]any, +) map[string]any { + if !status { + payload = nil + } + + return map[string]any{ + "status": status, + "code": code, + "payload": payload, + } +} + +// obrimRetrieverJson processes a JSON retrieval request. +func obrimRetrieverJson(config map[string]any) map[string]any { + resource := config["resource"].(string) + + data, ok := obrimRetrieverJsonHandler(resource) + if !ok { + return obrimRetrieverBuildOutput( + false, + obrimRetrieverFailureResourceNotFound, + nil, + ) + } + + if pathValue, exists := config["path"]; exists { + path := pathValue.(string) + + value, ok := obrimRetrieverJsonPath(data, path) + if !ok { + return obrimRetrieverBuildOutput( + false, + obrimRetrieverFailurePathNotFound, + nil, + ) + } + + return obrimRetrieverBuildOutput( + true, + obrimRetrieverSuccessPath, + map[string]any{ + "data": value, + }, + ) + } + + if fieldsValue, exists := config["fields"]; exists { + fields := fieldsValue.([]string) + + values, ok := obrimRetrieverJsonFields(data, fields) + if !ok { + return obrimRetrieverBuildOutput( + false, + obrimRetrieverFailureFieldNotFound, + nil, + ) + } + + return obrimRetrieverBuildOutput( + true, + obrimRetrieverSuccessFields, + map[string]any{ + "data": values, + }, + ) + } + + return obrimRetrieverBuildOutput( + true, + obrimRetrieverSuccessResource, + map[string]any{ + "data": obrimRetrieverJsonResource(data), + }, + ) +} + +// obrimRetrieverJsonHandler locates and invokes a registered JSON handler. +func obrimRetrieverJsonHandler( + resource string, +) (map[string]any, bool) { + obrimRetrieverJsonHandlersMutex.RLock() + handler, exists := obrimRetrieverJsonHandlers[resource] + obrimRetrieverJsonHandlersMutex.RUnlock() + + if !exists || handler == nil { + return nil, false + } + + data := handler() + if data == nil { + return nil, false + } + + return data, true +} + +// obrimRetrieverJsonResource returns the complete JSON resource payload. +func obrimRetrieverJsonResource( + resource map[string]any, +) map[string]any { + return resource +} + +// obrimRetrieverJsonPath retrieves a value using a dot-notation path. +func obrimRetrieverJsonPath( + resource map[string]any, + path string, +) (any, bool) { + parts := strings.Split(path, ".") + if len(parts) == 0 { + return nil, false + } + + var current any = resource + + for _, part := range parts { + if part == "" { + return nil, false + } + + switch value := current.(type) { + case map[string]any: + next, exists := value[part] + if !exists { + return nil, false + } + + current = next + + case []any: + index := -1 + + for i, item := range value { + if part == strings.TrimSpace(part) { + var parsed int + if _, err := fmt.Sscanf(part, "%d", &parsed); err == nil { + index = parsed + } + } + + if index >= 0 { + _ = item + break + } + + break + } + + if index < 0 || index >= len(value) { + return nil, false + } + + current = value[index] + + default: + return nil, false + } + } + + return current, true +} + +// obrimRetrieverJsonFields retrieves multiple values using field selection. +func obrimRetrieverJsonFields( + resource map[string]any, + fields []string, +) (map[string]any, bool) { + values := make(map[string]any, len(fields)) + + for _, field := range fields { + value, ok := obrimRetrieverJsonPath(resource, field) + if !ok { + return nil, false + } + + values[field] = value + } + + return values, true +} \ No newline at end of file