diff --git a/plugins/config.yaml b/plugins/config.yaml index 1ddc057..8109736 100644 --- a/plugins/config.yaml +++ b/plugins/config.yaml @@ -2,5 +2,5 @@ plugins: validation_plugin: id: tekuriValidator config: - schema_dir: schemas/ - plugin_path: implementations/ # Path to the directory containing the .so files \ No newline at end of file + schema_dir: plugins/schemas/ + plugin_path: plugins/implementations/ # Path to the directory containing the .so files \ No newline at end of file diff --git a/plugins/implementations/plugin_impl.go b/plugins/implementations/plugin_impl.go index fd22a61..fbd670e 100644 --- a/plugins/implementations/plugin_impl.go +++ b/plugins/implementations/plugin_impl.go @@ -6,7 +6,7 @@ import ( "fmt" "io/ioutil" "path/filepath" - "strings" + "time" "beckn-onix/plugins/plugin_definition" @@ -19,131 +19,149 @@ type tekuriValidator struct { } type tekuriValidatorProvider struct { - compiledSchemas map[string]map[string]*jsonschema.Schema // Cache for compiled schemas + //schemaCache map[string]map[string]*jsonschema.Schema + schemaCache map[string]*jsonschema.Schema } // Validate validates the given data against the schema. func (v *tekuriValidator) Validate(ctx context.Context, data []byte) error { + start := time.Now() var jsonData interface{} if err := json.Unmarshal(data, &jsonData); err != nil { return err } - return v.schema.Validate(jsonData) + err := v.schema.Validate(jsonData) + if err != nil { + fmt.Printf("Validation error: %v\n", err) + } + fmt.Printf("validate executed in %s\n", time.Since(start)) + + return err } -func (vp *tekuriValidatorProvider) Initialize(schemaDir string) error { - // Check if vp is nil - if vp == nil { - return fmt.Errorf("tekuriValidatorProvider is not initialized") - } +//(Approach 2)(all json files) - // Initialize compiledSchemas - vp.compiledSchemas = make(map[string]map[string]*jsonschema.Schema) +// //Initialize reads all .json files from the given schema directory, validates them using JSON Schema, and prints the result. +// func (vp *tekuriValidatorProvider) Initialize(schemaDir string) (map[string]plugin_definition.Validator, error) { +// start := time.Now() +// // Initialize the cache +// vp.schemaCache = make(map[string]map[string]*jsonschema.Schema) +// validatorCache := make(map[string]plugin_definition.Validator) - compiler := jsonschema.NewCompiler() - if compiler == nil { - return fmt.Errorf("jsonschema compiler is not initialized") - } +// // Read the directory +// files, err := ioutil.ReadDir(schemaDir) +// if err != nil { +// return nil, fmt.Errorf("failed to read schema directory: %v", err) +// } + +// for _, file := range files { +// if filepath.Ext(file.Name()) == ".json" { +// // Read the JSON file +// filePath := filepath.Join(schemaDir, file.Name()) +// compiler := jsonschema.NewCompiler() +// compiledSchema, err := compiler.Compile(filePath) +// if err != nil { +// return nil, fmt.Errorf("failed to compile JSON schema from file %s: %v", file.Name(), err) +// } +// if compiledSchema == nil { +// return nil, fmt.Errorf("compiled schema is nil for file %s", file.Name()) +// } +// // Extract directory and filename to use in the nested map +// dir := filepath.Base(filepath.Dir(filePath)) +// if vp.schemaCache[dir] == nil { +// vp.schemaCache[dir] = make(map[string]*jsonschema.Schema) +// } +// // Store the compiled schema in the nested cache +// vp.schemaCache[dir][file.Name()] = compiledSchema + +// validatorCache[file.Name()] = &tekuriValidator{schema: compiledSchema} +// } +// } +// fmt.Printf("initialize executed in %s\n", time.Since(start)) + +// return validatorCache, nil +// } + +// func (vp *tekuriValidatorProvider) Get(schemaKey string) (plugin_definition.Validator, error) { +// // Extract domain, version, and defKey from the schemaKey +// fmt.Println("before printing key :", schemaKey) +// schemaKey = strings.Replace(schemaKey, ":", "_", -1) + +// fmt.Println("after printing key :", schemaKey) + +// parts := strings.Split(schemaKey, "_") +// if len(parts) != 3 { +// return nil, fmt.Errorf("invalid schema key format: %s", schemaKey) +// } +// domain := parts[0] + "_" + parts[1] +// defKey := parts[2] + +// // Look up the compiled schema in the nested map +// if domainMap, ok := vp.schemaCache[domain]; ok { +// if schema, ok := domainMap[defKey]; ok { +// return &tekuriValidator{schema: schema}, nil +// } +// } +// return nil, fmt.Errorf("schema not found: %s", schemaKey) +// } + +// (Approach 1) +func (vp *tekuriValidatorProvider) Initialize(schemaDir string) (map[string]plugin_definition.Validator, error) { + start := time.Now() + + vp.schemaCache = make(map[string]*jsonschema.Schema) + validatorCache := make(map[string]plugin_definition.Validator) files, err := ioutil.ReadDir(schemaDir) if err != nil { - return fmt.Errorf("failed to read schema directory: %w", err) + return nil, fmt.Errorf("failed to read schema directory: %w", err) } - for _, file := range files { - if !file.IsDir() && strings.HasSuffix(file.Name(), ".json") { - schemaPath := filepath.Join(schemaDir, file.Name()) - absSchemaPath, _ := filepath.Abs(schemaPath) - fmt.Println("path:", absSchemaPath) - fmt.Printf("Reading schema file: %s\n", schemaPath) + if filepath.Ext(file.Name()) == ".json" { + filePath := filepath.Join(schemaDir, file.Name()) - fileContent, err := ioutil.ReadFile(schemaPath) + fmt.Println("Compiling filePath:", filePath) + + // Read the file content + content, err := ioutil.ReadFile(filePath) if err != nil { - return fmt.Errorf("failed to read schema file %s: %w", schemaPath, err) + return nil, fmt.Errorf("failed to read file %s: %v", filePath, err) } - var schemaData map[string]interface{} - if err := json.Unmarshal(fileContent, &schemaData); err != nil { - return fmt.Errorf("failed to unmarshal schema file %s: %w", schemaPath, err) + var schemaDoc map[string]interface{} + if err := json.Unmarshal(content, &schemaDoc); err != nil { + return nil, fmt.Errorf("failed to unmarshal JSON schema from file %s: %v", filePath, err) } - if defs, ok := schemaData["$defs"].(map[string]interface{}); ok { - nameParts := strings.Split(strings.TrimSuffix(file.Name(), ".json"), "_") - if len(nameParts) != 3 { - return fmt.Errorf("invalid schema file name format: %s", file.Name()) - } - domain := strings.ReplaceAll(strings.ToLower(nameParts[0]+"_"+nameParts[1]), ":", "_") - version := strings.ToLower(nameParts[2]) - fmt.Printf("Domain: %s, Version: %s\n", domain, version) + if defs, exists := schemaDoc["$defs"]; exists { + defsMap := defs.(map[string]interface{}) - for defKey, defValue := range defs { - fmt.Println("value :", defValue) - tempSchema := map[string]interface{}{ - "$id": fmt.Sprintf("file://%s", absSchemaPath), - "$schema": schemaData["$schema"], - "$defs": map[string]interface{}{ - defKey: defValue, - }, - } - tempSchemaData, err := json.Marshal(tempSchema) + for name, defSchema := range defsMap { + _, err := json.Marshal(defSchema) if err != nil { - return fmt.Errorf("failed to marshal temporary schema for $defs.%s: %w", defKey, err) + return nil, fmt.Errorf("failed to marshal schema definition %s: %v", name, err) } - fmt.Println(" ") - // Use a unique ID for the resource to avoid conflict - resourceId := fmt.Sprintf("file://%s/%s", absSchemaPath, defKey) - fmt.Println("resource Id :", resourceId) - compiler.AddResource(resourceId, strings.NewReader(string(tempSchemaData))) + compiler := jsonschema.NewCompiler() + if err := compiler.AddResource(name, filepath.Dir(filePath)); err != nil { + return nil, fmt.Errorf("failed to add resource for schema definition %s: %v", name, err) + } - // Compile the specific $defs section - defSchemaPath := fmt.Sprintf("file://%s", absSchemaPath) - - fmt.Println("def schema path printing ::: ", defSchemaPath) - - compiledSchema, err := compiler.Compile(defSchemaPath) + compiledSchema, err := compiler.Compile(filePath) if err != nil { - fmt.Printf("Failed to compile $defs.%s in schema file: %s\nError: %v\n", defKey, schemaPath, err) - continue + return nil, fmt.Errorf("failed to compile schema definition: %v", err) } - fmt.Println("schema :", compiledSchema) - - // Initialize nested map if not already initialized - if _, exists := vp.compiledSchemas[domain]; !exists { - vp.compiledSchemas[domain] = make(map[string]*jsonschema.Schema) - } - - cacheKey := fmt.Sprintf("%s_%s_%s", domain, version, defKey) - fmt.Println("key :", cacheKey) - vp.compiledSchemas[domain][cacheKey] = compiledSchema - fmt.Printf("Compiled and cached $defs.%s schema: %s\n", defKey, cacheKey) + schemaKey := fmt.Sprintf("%s.%s", file.Name(), name) + vp.schemaCache[schemaKey] = compiledSchema + validatorCache[schemaKey] = &tekuriValidator{schema: compiledSchema} } } } } + fmt.Printf("Initialize executed in %s\n", time.Since(start)) + return validatorCache, nil - return nil -} - -func (vp *tekuriValidatorProvider) Get(schemaKey string) (plugin_definition.Validator, error) { - // Extract domain, version, and defKey from the schemaKey - schemaKey = strings.Replace(schemaKey, ":", "_", -1) - - parts := strings.Split(schemaKey, "_") - if len(parts) != 3 { - return nil, fmt.Errorf("invalid schema key format: %s", schemaKey) - } - domain := parts[0] + "_" + parts[1] - defKey := parts[2] - - // Look up the compiled schema in the nested map - if domainMap, ok := vp.compiledSchemas[domain]; ok { - if schema, ok := domainMap[defKey]; ok { - return &tekuriValidator{schema: schema}, nil - } - } - return nil, fmt.Errorf("schema not found: %s", schemaKey) } // Ensure tekuriValidatorProvider implements ValidatorProvider diff --git a/plugins/implementations/tekuriValidator.so b/plugins/implementations/tekuriValidator.so index 5bdd5f5..d1035a9 100644 Binary files a/plugins/implementations/tekuriValidator.so and b/plugins/implementations/tekuriValidator.so differ diff --git a/plugins/manager.go b/plugins/manager.go index d29e97a..a6c4797 100644 --- a/plugins/manager.go +++ b/plugins/manager.go @@ -1,14 +1,12 @@ -package main +package plugins import ( "beckn-onix/plugins/plugin_definition" - "context" - "encoding/json" "fmt" "io/ioutil" "log" "plugin" - "strings" + "time" "gopkg.in/yaml.v2" ) @@ -31,56 +29,55 @@ type PluginDetails struct { Schema string `yaml:"schema_dir"` } -type Payload struct { - Context struct { - Domain string `json:"domain"` - Version string `json:"version"` - } `json:"context"` -} - // PluginManager manages the loading and execution of plugins. type PluginManager struct { validatorProvider plugin_definition.ValidatorProvider } // NewValidatorProvider initializes the PluginManager with the given configuration. -func NewValidatorProvider(pluginsConfig PluginConfig) (*PluginManager, error) { +func NewValidatorProvider(pluginsConfig PluginConfig) (*PluginManager, map[string]plugin_definition.Validator, error) { + start := time.Now() validationPlugin := pluginsConfig.Plugins.ValidationPlugin - if validationPlugin.ID == "" { - return nil, fmt.Errorf("validation_plugin ID is empty") + return nil, nil, fmt.Errorf("validation_plugin ID is empty") } pluginPath := validationPlugin.PluginPath + validationPlugin.ID + ".so" // Check if the plugin path is empty if pluginPath == "" { - return nil, fmt.Errorf("plugin path is empty") + return nil, nil, fmt.Errorf("plugin path is empty") } // Load the plugin p, err := plugin.Open(pluginPath) if err != nil { - return nil, fmt.Errorf("failed to open plugin: %v", err) + return nil, nil, fmt.Errorf("failed to open plugin: %v", err) } vpSymbol, err := p.Lookup("GetProvider") if err != nil { - return nil, err + return nil, nil, err } - getProviderFunc, ok := vpSymbol.(func() plugin_definition.ValidatorProvider) if !ok { - return nil, fmt.Errorf("failed to cast to *plugin_definition.ValidatorProvider") + return nil, nil, fmt.Errorf("failed to cast to *plugin_definition.ValidatorProvider") } - validatorProvider := getProviderFunc() - return &PluginManager{validatorProvider: validatorProvider}, nil + schemaDir := pluginsConfig.Plugins.ValidationPlugin.Config.Schema + validator, err := validatorProvider.Initialize(schemaDir) + if err != nil { + log.Fatalf("Failed to initialize validator provider: %v", err) + } + fmt.Println("validators are :", validator) + fmt.Printf("plugin boot-up executed in %s\n", time.Since(start)) + return &PluginManager{validatorProvider: validatorProvider}, validator, nil } // loadPluginsConfig loads the plugins configuration from a YAML file. -func loadPluginsConfig(filePath string) (PluginConfig, error) { +func LoadPluginsConfig(filePath string) (PluginConfig, error) { + start := time.Now() data, err := ioutil.ReadFile(filePath) if err != nil { return PluginConfig{}, err @@ -91,52 +88,7 @@ func loadPluginsConfig(filePath string) (PluginConfig, error) { if err != nil { return PluginConfig{}, err } + fmt.Printf("loadconfig executed in %s\n", time.Since(start)) return config, nil } - -func main() { - pluginsConfig, err := loadPluginsConfig("config.yaml") - if err != nil { - log.Fatalf("Failed to load plugins configuration: %v", err) - } - - pm, err := NewValidatorProvider(pluginsConfig) - if err != nil { - log.Fatalf("Failed to create PluginManager: %v", err) - } - - schemaDir := pluginsConfig.Plugins.ValidationPlugin.Config.Schema - - err = pm.validatorProvider.Initialize(schemaDir) - if err != nil { - log.Fatalf("Failed to initialize validator provider: %v", err) - } - - payloadData, err := ioutil.ReadFile("test/payload.json") - if err != nil { - log.Fatalf("Failed to read payload data: %v", err) - } - - var payload Payload - if err := json.Unmarshal(payloadData, &payload); err != nil { - log.Fatalf("Failed to unmarshal payload: %v", err) - } - - // Construct the schema file name based on domain and version - schemaFileName := fmt.Sprintf("%s_%s.json", strings.ToLower(payload.Context.Domain), strings.ToLower(payload.Context.Version)) - - // Get the validator for the specific schema - validator, err := pm.validatorProvider.Get(schemaFileName) - if err != nil { - log.Fatalf("Failed to get validator: %v", err) - } - fmt.Println("printing validator :", validator) - - // Validate the payload against the schema - if err := validator.Validate(context.Background(), payloadData); err != nil { - log.Printf("Validation failed: %v", err) - } else { - log.Println("Validation succeeded!") - } -} diff --git a/plugins/manager_test.go b/plugins/manager_test.go deleted file mode 100644 index 81bacf1..0000000 --- a/plugins/manager_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - "testing" -) - -func TestLoadPluginsConfig(t *testing.T) { - // Test loading a valid configuration - config, err := loadPluginsConfig("config.yaml") - if err != nil { - t.Fatalf("Expected no error, got %v", err) - } - - if config.Plugins.ValidationPlugin.ID == "" { - t.Fatal("Expected validation_plugin ID to be set") - } -} - -// func TestNewPluginManager(t *testing.T) { -// // Load the configuration -// config, err := loadPluginsConfig("config.yaml") -// if err != nil { -// t.Fatalf("Failed to load plugins configuration: %v", err) -// } - -// // Create a new PluginManager -// pm, err := New(config) -// if err != nil { -// t.Fatalf("Failed to create PluginManager: %v", err) -// } - -// if pm == nil { -// t.Fatal("Expected PluginManager to be created") -// } -// } diff --git a/plugins/plugin_definition/plugin.go b/plugins/plugin_definition/plugin.go index 698ad46..65acc90 100644 --- a/plugins/plugin_definition/plugin.go +++ b/plugins/plugin_definition/plugin.go @@ -9,6 +9,6 @@ type Validator interface { // ValidatorProvider interface for creating validators type ValidatorProvider interface { - Get(p string) (Validator, error) - Initialize(schemaDir string) error + //Get(p string) (Validator, error) + Initialize(schemaDir string) (map[string]Validator, error) } diff --git a/plugins/plugins b/plugins/plugins new file mode 100755 index 0000000..bca8122 Binary files /dev/null and b/plugins/plugins differ diff --git a/plugins/schema/ondc_trv10_2.0.0/search.json b/plugins/schema/ondc_trv10_2.0.0/search.json new file mode 100644 index 0000000..2c17490 --- /dev/null +++ b/plugins/schema/ondc_trv10_2.0.0/search.json @@ -0,0 +1,217 @@ +{ + "$id": "https://example.com/ondc/trv10/2.0.0", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$defs": { + "search": { + "$id": "search#", + "type": "object", + "properties": { + "context": { + "type": "object", + "properties": { + "domain": { + "type": "string" + }, + "location": { + "type": "object", + "properties": { + "city": { + "type": "object", + "properties": { + "code": { + "type": "string" + } + }, + "required": ["code"] + }, + "country": { + "type": "object", + "properties": { + "code": { + "type": "string", + "enum": ["IND"] + } + }, + "required": ["code"] + } + } + } + }, + "required": [ + "domain", + "location", + "action", + "bap_id", + "bap_uri", + "transaction_id", + "message_id", + "timestamp", + "ttl" + ] + }, + "message": { + "type": "object", + "properties": { + "intent": { + "type": "object", + "properties": { + "fulfillment": { + "type": "object", + "properties": { + "stops": { + "type": "array", + "items": { + "type": "object", + "properties": { + "location": { + "type": "object", + "properties": { + "gps": { + "type": "string" + } + }, + "required": ["gps"] + }, + "type": { + "type": "string", + "enum": ["START", "END"] + } + }, + "required": ["location", "type"] + }, + "minItems": 2, + "maxItems": 2 + } + }, + "required": ["stops"] + }, + "payment": { + "type": "object", + "properties": { + "collected_by": { + "type": "string", + "enum": ["BPP", "BAP"] + }, + "tags": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "uniqueItems": true, + "items": { + "type": "object", + "properties": { + "descriptor": { + "type": "object", + "properties": { + "code": { + "type": "string", + "enum": ["SETTLEMENT_TERMS", "BUYER_FINDER_FEES"] + } + }, + "required": ["code"] + } + }, + "allOf": [ + { + "if": { + "properties": { + "descriptor": { + "properties": { + "code": { + "const": "SETTLEMENT_TERMS" + } + }, + "required": ["code"] + } + } + }, + "then": { + "properties": { + "list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "descriptor": { + "type": "object", + "properties": { + "code": { + "type": "string", + "enum": [ + "SETTLEMENT_BASIS", + "SETTLEMENT_WINDOW", + "STATIC_TERMS", + "SETTLEMENT_TYPE", + "DELAY_INTEREST" + ] + } + }, + "required": ["code"] + }, + "value": { + "type": "string" + } + }, + "required": ["descriptor", "value"] + } + } + } + } + }, + { + "if": { + "properties": { + "descriptor": { + "properties": { + "code": { + "const": "BUYER_FINDER_FEES" + } + }, + "required": ["code"] + } + } + }, + "then": { + "properties": { + "list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "descriptor": { + "type": "object", + "properties": { + "code": { + "enum": ["BUYER_FINDER_FEES_PERCENTAGE"] + } + }, + "required": ["code"] + }, + "value": { + "type": "string", + "pattern": "^-?\\d+(\\.\\d+)?$" + } + }, + "required": ["descriptor", "value"] + } + } + } + } + } + ], + "required": ["descriptor"] + } + } + }, + "required": ["collected_by", "tags"] + } + }, + "required": ["fulfillment", "payment"] + } + }, + "required": ["intent"] + } + } + } + } +} diff --git a/plugins/schema/ondc_trv10_2.0.0/select.json b/plugins/schema/ondc_trv10_2.0.0/select.json new file mode 100644 index 0000000..6f27ceb --- /dev/null +++ b/plugins/schema/ondc_trv10_2.0.0/select.json @@ -0,0 +1,218 @@ +{ + "$id": "https://example.com/ondc/trv10/2.0.0", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$defs": { + "select": { + "$id": "select#", + "type": "object", + "properties": { + "context": { + "type": "object", + "properties": { + "domain": { + "type": "string" + }, + "location": { + "type": "object", + "properties": { + "city": { + "type": "object", + "properties": { + "code": { + "type": "string" + } + }, + "required": ["code"] + }, + "country": { + "type": "object", + "properties": { + "code": { + "type": "string", + "enum": ["IND"] + } + }, + "required": ["code"] + } + } + } + }, + "required": [ + "domain", + "location", + "action", + "bap_id", + "bap_uri", + "transaction_id", + "message_id", + "timestamp", + "ttl" + ] + }, + "message": { + "type": "object", + "properties": { + "intent": { + "type": "object", + "properties": { + "fulfillment": { + "type": "object", + "properties": { + "stops": { + "type": "array", + "items": { + "type": "object", + "properties": { + "location": { + "type": "object", + "properties": { + "gps": { + "type": "string" + } + }, + "required": ["gps"] + }, + "type": { + "type": "string", + "enum": ["START", "END"] + } + }, + "required": ["location", "type"] + }, + "minItems": 2, + "maxItems": 2 + } + }, + "required": ["stops"] + }, + "payment": { + "type": "object", + "properties": { + "collected_by": { + "type": "string", + "enum": ["BPP", "BAP"] + }, + "tags": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "uniqueItems": true, + "items": { + "type": "object", + "properties": { + "descriptor": { + "type": "object", + "properties": { + "code": { + "type": "string", + "enum": ["SETTLEMENT_TERMS", "BUYER_FINDER_FEES"] + } + }, + "required": ["code"] + } + }, + "allOf": [ + { + "if": { + "properties": { + "descriptor": { + "properties": { + "code": { + "const": "SETTLEMENT_TERMS" + } + }, + "required": ["code"] + } + } + }, + "then": { + "properties": { + "list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "descriptor": { + "type": "object", + "properties": { + "code": { + "type": "string", + "enum": [ + "SETTLEMENT_BASIS", + "SETTLEMENT_WINDOW", + "STATIC_TERMS", + "SETTLEMENT_TYPE", + "DELAY_INTEREST" + ] + } + }, + "required": ["code"] + }, + "value": { + "type": "string" + } + }, + "required": ["descriptor", "value"] + } + } + } + } + }, + { + "if": { + "properties": { + "descriptor": { + "properties": { + "code": { + "const": "BUYER_FINDER_FEES" + } + }, + "required": ["code"] + } + } + }, + "then": { + "properties": { + "list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "descriptor": { + "type": "object", + "properties": { + "code": { + "enum": ["BUYER_FINDER_FEES_PERCENTAGE"] + } + }, + "required": ["code"] + }, + "value": { + "type": "string", + "pattern": "^-?\\d+(\\.\\d+)?$" + } + }, + "required": ["descriptor", "value"] + } + } + } + } + } + ], + "required": ["descriptor"] + } + } + }, + "required": ["collected_by", "tags"] + } + }, + "required": ["fulfillment", "payment"] + } + }, + "required": ["intent"] + } + } + } + } + } + \ No newline at end of file diff --git a/plugins/schemas/ondc_trv10_2.0.0.json b/plugins/schemas/ondc_trv10_2.0.0.json index 01beefb..c55f639 100644 --- a/plugins/schemas/ondc_trv10_2.0.0.json +++ b/plugins/schemas/ondc_trv10_2.0.0.json @@ -4,7 +4,499 @@ "$defs": { "search": { - "$id": "https://example.com/ondc/trv10/2.0.0/search", + "$id": "search#", + "type": "object", + "properties": { + "context": { + "type": "object", + "properties": { + "domain": { + "type": "string" + }, + "location": { + "type": "object", + "properties": { + "city": { + "type": "object", + "properties": { + "code": { + "type": "string" + } + }, + "required": ["code"] + }, + "country": { + "type": "object", + "properties": { + "code": { + "type": "string", + "enum": ["IND"] + } + }, + "required": ["code"] + } + }, + "required": ["city", "country"] + }, + "action": { + "type": "string", + "enum": ["search"] + }, + "bap_id": { + "type": "string" + }, + "bap_uri": { + "type": "string", + "format": "uri" + }, + "bpp_id": { + "type": "string" + }, + "bpp_uri": { + "type": "string", + "format": "uri" + }, + "transaction_id": { + "type": "string", + "format": "uuid" + }, + "message_id": { + "type": "string", + "format": "uuid" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "ttl": { + "type": "string", + "format": "duration" + } + }, + "required": [ + "domain", + "location", + "action", + "bap_id", + "bap_uri", + "transaction_id", + "message_id", + "timestamp", + "ttl" + ] + }, + "message": { + "type": "object", + "properties": { + "intent": { + "type": "object", + "properties": { + "fulfillment": { + "type": "object", + "properties": { + "stops": { + "type": "array", + "items": { + "type": "object", + "properties": { + "location": { + "type": "object", + "properties": { + "gps": { + "type": "string" + } + }, + "required": ["gps"] + }, + "type": { + "type": "string", + "enum": ["START", "END"] + } + }, + "required": ["location", "type"] + }, + "minItems": 2, + "maxItems": 2 + } + }, + "required": ["stops"] + }, + "payment": { + "type": "object", + "properties": { + "collected_by": { + "type": "string", + "enum": ["BPP", "BAP"] + }, + "tags": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "uniqueItems": true, + "items": { + "type": "object", + "properties": { + "descriptor": { + "type": "object", + "properties": { + "code": { + "type": "string", + "enum": ["SETTLEMENT_TERMS", "BUYER_FINDER_FEES"] + } + }, + "required": ["code"] + } + }, + "allOf": [ + { + "if": { + "properties": { + "descriptor": { + "properties": { + "code": { + "const": "SETTLEMENT_TERMS" + } + }, + "required": ["code"] + } + } + }, + "then": { + "properties": { + "list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "descriptor": { + "type": "object", + "properties": { + "code": { + "type": "string", + "enum": [ + "SETTLEMENT_BASIS", + "SETTLEMENT_WINDOW", + "STATIC_TERMS", + "SETTLEMENT_TYPE", + "DELAY_INTEREST" + ] + } + }, + "required": ["code"] + }, + "value": { + "type": "string" + } + }, + "required": ["descriptor", "value"] + } + } + } + } + }, + { + "if": { + "properties": { + "descriptor": { + "properties": { + "code": { + "const": "BUYER_FINDER_FEES" + } + }, + "required": ["code"] + } + } + }, + "then": { + "properties": { + "list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "descriptor": { + "type": "object", + "properties": { + "code": { + "enum": ["BUYER_FINDER_FEES_PERCENTAGE"] + } + }, + "required": ["code"] + }, + "value": { + "type": "string", + "pattern": "^-?\\d+(\\.\\d+)?$" + } + }, + "required": ["descriptor", "value"] + } + } + } + } + } + ], + "required": ["descriptor"] + } + } + }, + "required": ["collected_by", "tags"] + } + }, + "required": ["fulfillment", "payment"] + } + }, + "required": ["intent"] + } + } + }, + "select": { + "$id": "select#", + "type": "object", + "properties": { + "context": { + "type": "object", + "properties": { + "domain": { + "type": "string" + }, + "location": { + "type": "object", + "properties": { + "city": { + "type": "object", + "properties": { + "code": { + "type": "string" + } + }, + "required": ["code"] + }, + "country": { + "type": "object", + "properties": { + "code": { + "type": "string", + "enum": ["IND"] + } + }, + "required": ["code"] + } + }, + "required": ["city", "country"] + }, + "action": { + "type": "string", + "enum": ["search"] + }, + "bap_id": { + "type": "string" + }, + "bap_uri": { + "type": "string", + "format": "uri" + }, + "bpp_id": { + "type": "string" + }, + "bpp_uri": { + "type": "string", + "format": "uri" + }, + "transaction_id": { + "type": "string", + "format": "uuid" + }, + "message_id": { + "type": "string", + "format": "uuid" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "ttl": { + "type": "string", + "format": "duration" + } + }, + "required": [ + "domain", + "location", + "action", + "bap_id", + "bap_uri", + "transaction_id", + "message_id", + "timestamp", + "ttl" + ] + }, + "message": { + "type": "object", + "properties": { + "intent": { + "type": "object", + "properties": { + "fulfillment": { + "type": "object", + "properties": { + "stops": { + "type": "array", + "items": { + "type": "object", + "properties": { + "location": { + "type": "object", + "properties": { + "gps": { + "type": "string" + } + }, + "required": ["gps"] + }, + "type": { + "type": "string", + "enum": ["START", "END"] + } + }, + "required": ["location", "type"] + }, + "minItems": 2, + "maxItems": 2 + } + }, + "required": ["stops"] + }, + "payment": { + "type": "object", + "properties": { + "collected_by": { + "type": "string", + "enum": ["BPP", "BAP"] + }, + "tags": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "uniqueItems": true, + "items": { + "type": "object", + "properties": { + "descriptor": { + "type": "object", + "properties": { + "code": { + "type": "string", + "enum": ["SETTLEMENT_TERMS", "BUYER_FINDER_FEES"] + } + }, + "required": ["code"] + } + }, + "allOf": [ + { + "if": { + "properties": { + "descriptor": { + "properties": { + "code": { + "const": "SETTLEMENT_TERMS" + } + }, + "required": ["code"] + } + } + }, + "then": { + "properties": { + "list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "descriptor": { + "type": "object", + "properties": { + "code": { + "type": "string", + "enum": [ + "SETTLEMENT_BASIS", + "SETTLEMENT_WINDOW", + "STATIC_TERMS", + "SETTLEMENT_TYPE", + "DELAY_INTEREST" + ] + } + }, + "required": ["code"] + }, + "value": { + "type": "string" + } + }, + "required": ["descriptor", "value"] + } + } + } + } + }, + { + "if": { + "properties": { + "descriptor": { + "properties": { + "code": { + "const": "BUYER_FINDER_FEES" + } + }, + "required": ["code"] + } + } + }, + "then": { + "properties": { + "list": { + "type": "array", + "items": { + "type": "object", + "properties": { + "descriptor": { + "type": "object", + "properties": { + "code": { + "enum": ["BUYER_FINDER_FEES_PERCENTAGE"] + } + }, + "required": ["code"] + }, + "value": { + "type": "string", + "pattern": "^-?\\d+(\\.\\d+)?$" + } + }, + "required": ["descriptor", "value"] + } + } + } + } + } + ], + "required": ["descriptor"] + } + } + }, + "required": ["collected_by", "tags"] + } + }, + "required": ["fulfillment", "payment"] + } + }, + "required": ["intent"] + } + } + }, + "on_init": { + "$id": "on_init#", "type": "object", "properties": { "context": { diff --git a/plugins/test/payload.json b/plugins/test/payload.json index 644fb4e..6e5454a 100644 --- a/plugins/test/payload.json +++ b/plugins/test/payload.json @@ -1,80 +1,80 @@ { - "context": { - "action": "search", - "bap_id": "example-bap.com", - "bap_uri": "https://example-bap.com/prod/trv10", - "domain": "ONDC:TRV10", - "location": { - "city": { - "code": "std:080" - }, - "country": { - "code": "IND" - } + "context": { + "action": "search", + "bap_id": "example-bap.com", + "bap_uri": "https://example-bap.com/prod/trv10", + "domain": "ONDC:TRV10", + "location": { + "city": { + "code": "std:080" }, - "message_id": "40963dc1-e402-4f4d-ae70-7c5864ca682c", - "timestamp": "2023-12-09T13:39:56.645Z", - "transaction_id": "870782be-6757-43f1-945c-8eeaf9536259", - "ttl": "PT30S", - "version": "2.0.0" + "country": { + "code": "IND" + } }, - "message": { - "intent": { - "fulfillment": { - "stops": [ - { - "location": { - "gps": "13.0089, 77.644408" - }, - "type": "START" + "message_id": "40963dc1-e402-4f4d-ae70-7c5864ca682c", + "timestamp": "2023-12-09T13:39:56.645Z", + "transaction_id": "870782be-6757-43f1-945c-8eeaf9536259", + "ttl": "PT30S", + "version": "2.0.0" + }, + "message": { + "intent": { + "fulfillment": { + "stops": [ + { + "location": { + "gps": "13.0089, 77.644408" }, - { - "location": { - "gps": "12.971186, 77.586812" - }, - "type": "END" - } - ] - }, - "payment": { - "collected_by": "BPP", - "tags": [ - { - "descriptor": { - "code": "BUYER_FINDER_FEES" - }, - "display": false, - "list": [ - { - "descriptor": { - "code": "BUYER_FINDER_FEES_PERCENTAGE" - }, - "value": "1" - } - ] + "type": "START" + }, + { + "location": { + "gps": "12.971186, 77.586812" }, - { - "descriptor": { - "code": "SETTLEMENT_TERMS" - }, - "display": false, - "list": [ - { - "descriptor": { - "code": "DELAY_INTEREST" - }, - "value": "5" + "type": "END" + } + ] + }, + "payment": { + "collected_by": "BPP", + "tags": [ + { + "descriptor": { + "code": "BUYER_FINDER_FEES" + }, + "display": false, + "list": [ + { + "descriptor": { + "code": "BUYER_FINDER_FEES_PERCENTAGE" }, - { - "descriptor": { - "code": "STATIC_TERMS" - }, - "value": "example-test-bap.com/static-terms.txt" - } - ] - } - ] - } + "value": 1 + } + ] + }, + { + "descriptor": { + "code": "SETTLEMENT_TERMS" + }, + "display": false, + "list": [ + { + "descriptor": { + "code": "DELAY_INTEREST" + }, + "value": 5 + }, + { + "descriptor": { + "code": "STATIC_TERMS" + }, + "value": "example-test-bap.com/static-terms.txt" + } + ] + } + ] } } - } \ No newline at end of file + } +} \ No newline at end of file diff --git a/test.go b/test.go new file mode 100644 index 0000000..e71111b --- /dev/null +++ b/test.go @@ -0,0 +1,73 @@ +package main + +import ( + "beckn-onix/plugins" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/url" + "strings" +) + +type Payload struct { + Context struct { + Domain string `json:"domain"` + Version string `json:"version"` + } `json:"context"` +} + +func main() { + + pluginsConfig, err := plugins.LoadPluginsConfig("plugins/config.yaml") + if err != nil { + log.Fatalf("Failed to load plugins configuration: %v", err) + } + + _, validators, err := plugins.NewValidatorProvider(pluginsConfig) + if err != nil { + log.Fatalf("Failed to create PluginManager: %v", err) + } + + for fileName, validator := range validators { + fmt.Printf("%s: %v\n", fileName, validator) + } + requestURL := "http://example.com/select" + + // Extract endpoint from request URL + u, err := url.Parse(requestURL) + if err != nil { + log.Fatalf("Failed to parse request URL: %v", err) + } + //schemaFileName := fmt.Sprintf("%s.json", strings.Trim(u.Path, "/")) + + //approch 1 start + endpoint := strings.Trim(u.Path, "/") + + payloadData, err := ioutil.ReadFile("plugins/test/payload.json") + if err != nil { + log.Fatalf("Failed to read payload data: %v", err) + } + var payload Payload + if err := json.Unmarshal(payloadData, &payload); err != nil { + log.Fatalf("Failed to unmarshal payload: %v", err) + } + domain := strings.Replace(strings.ToLower(payload.Context.Domain), ":", "_", -1) + schemaFileName := fmt.Sprintf("%s_%s.json.%s", domain, + strings.ToLower(payload.Context.Version), endpoint) + + //end + + validator, exists := validators[schemaFileName] + if !exists { + log.Fatalf("Validator not found for %s", schemaFileName) + } + ctx := context.Background() + err = validator.Validate(ctx, payloadData) + if err != nil { + fmt.Printf("Document validation failed: %v\n", err) + } else { + fmt.Println("Document validation succeeded!") + } +}