diff --git a/go.mod b/go.mod index 7ea3206..340096e 100644 --- a/go.mod +++ b/go.mod @@ -7,4 +7,13 @@ require ( gopkg.in/yaml.v2 v2.4.0 ) -require golang.org/x/text v0.14.0 // indirect +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +require ( + github.com/stretchr/testify v1.10.0 + golang.org/x/text v0.14.0 // indirect +) diff --git a/go.sum b/go.sum index 2c8aa28..25d9a0b 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,18 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw= github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/plugins/config.yaml b/plugins/config.yaml index a636264..bbf0ca6 100644 --- a/plugins/config.yaml +++ b/plugins/config.yaml @@ -2,5 +2,5 @@ plugins: validation_plugin: id: tekuriValidator config: - schema_dir: + schema_dir: plugin_path: plugins/implementations/ \ No newline at end of file diff --git a/plugins/plugin.go b/plugins/definitions/plugin.go similarity index 94% rename from plugins/plugin.go rename to plugins/definitions/plugin.go index ce6c96b..3f31bd6 100644 --- a/plugins/plugin.go +++ b/plugins/definitions/plugin.go @@ -1,4 +1,4 @@ -package plugins +package definitions import "context" diff --git a/plugins/implementations/coverage.html b/plugins/implementations/coverage.html new file mode 100644 index 0000000..92dae5f --- /dev/null +++ b/plugins/implementations/coverage.html @@ -0,0 +1,246 @@ + + + + + + implementations: Go Coverage Report + + + +
+ +
+ not tracked + + not covered + covered + +
+
+
+ + + +
+ + + diff --git a/plugins/implementations/plugin_impl.go b/plugins/implementations/plugin_impl.go index 752322e..d6a36eb 100644 --- a/plugins/implementations/plugin_impl.go +++ b/plugins/implementations/plugin_impl.go @@ -8,7 +8,7 @@ import ( "path/filepath" "strings" - "beckn-onix/plugins" + "beckn-onix/plugins/definitions" "github.com/santhosh-tekuri/jsonschema/v6" ) @@ -41,69 +41,99 @@ func (v *TekuriValidator) Validate(ctx context.Context, data []byte) error { // Initialize initializes the validator provider by compiling all the JSON schema files // from the specified directory and storing them in a cache. It returns a map of validators // indexed by their schema filenames. -func (vp *TekuriValidatorProvider) Initialize(schemaDir string) (map[string]plugins.Validator, error) { - // Initialize the SchemaCache map to store the compiled schemas using a unique key (domain/version/schema). - vp.SchemaCache = make(map[string]*jsonschema.Schema) +func (vp *TekuriValidatorProvider) Initialize(schemaDir string) (map[string]definitions.Validator, error) { + // Initialize SchemaCache if it's nil + if vp.SchemaCache == nil { + vp.SchemaCache = make(map[string]*jsonschema.Schema) + } + // Check if the directory exists and is accessible + info, err := os.Stat(schemaDir) + if err != nil { + if os.IsNotExist(err) { + return nil, fmt.Errorf("schema directory does not exist: %s", schemaDir) + } + return nil, fmt.Errorf("failed to access schema directory: %v", err) + } + if !info.IsDir() { + return nil, fmt.Errorf("provided schema path is not a directory: %s", schemaDir) + } + // Initialize the validatorCache map to store the Validator instances associated with each schema. - validatorCache := make(map[string]plugins.Validator) + validatorCache := make(map[string]definitions.Validator) compiler := jsonschema.NewCompiler() - // Walk through the schema directory and process each file. - err := filepath.Walk(schemaDir, func(path string, info os.FileInfo, err error) error { + // Helper function to process directories recursively + var processDir func(dir string) error + processDir = func(dir string) error { + entries, err := os.ReadDir(dir) if err != nil { - return err + return fmt.Errorf("failed to read directory: %v", err) } - // Only process files (ignore directories) and ensure the file has a ".json" extension. - if !info.IsDir() && filepath.Ext(info.Name()) == ".json" { - compiledSchema, err := compiler.Compile(path) - if err != nil { - return fmt.Errorf("failed to compile JSON schema from file %s: %v", info.Name(), err) + for _, entry := range entries { + path := filepath.Join(dir, entry.Name()) + if entry.IsDir() { + // Recursively process subdirectories + if err := processDir(path); err != nil { + return err + } + } else if filepath.Ext(entry.Name()) == ".json" { + // Process JSON files + compiledSchema, err := compiler.Compile(path) + if err != nil { + return fmt.Errorf("failed to compile JSON schema from file %s: %v", entry.Name(), err) + } + if compiledSchema == nil { + return fmt.Errorf("compiled schema is nil for file %s", entry.Name()) + } + + // Use relative path from schemaDir to avoid absolute paths and make schema keys domain/version specific. + relativePath, err := filepath.Rel(schemaDir, path) + if err != nil { + return fmt.Errorf("failed to get relative path for file %s: %v", entry.Name(), err) + } + // Split the relative path to get domain, version, and schema. + parts := strings.Split(relativePath, string(os.PathSeparator)) + + // Ensure that the file path has at least 3 parts: domain, version, and schema file. + if len(parts) < 3 { + return fmt.Errorf("invalid schema file structure, expected domain/version/schema.json but got: %s", relativePath) + } + + // Extract domain, version, and schema filename from the parts. + // Validate that the extracted parts are non-empty + domain := strings.TrimSpace(parts[0]) + version := strings.TrimSpace(parts[1]) + schemaFileName := strings.TrimSpace(parts[2]) + + if domain == "" || version == "" || schemaFileName == "" { + return fmt.Errorf("invalid schema file structure, one or more components are empty. Relative path: %s", relativePath) + } + + // Construct a unique key combining domain, version, and schema name (e.g., ondc_trv10/v2.0.0/schema.json). + uniqueKey := fmt.Sprintf("%s/%s/%s", domain, version, schemaFileName) + // Store the compiled schema in the SchemaCache using the unique key. + vp.SchemaCache[uniqueKey] = compiledSchema + // Store the corresponding validator in the validatorCache using the same unique key. + validatorCache[uniqueKey] = &TekuriValidator{schema: compiledSchema} } - if compiledSchema == nil { - return fmt.Errorf("compiled schema is nil for file %s", info.Name()) - } - - // Use relative path from schemaDir to avoid absolute paths and make schema keys domain/version specific. - relativePath, err := filepath.Rel(schemaDir, path) - if err != nil { - return fmt.Errorf("failed to get relative path for file %s: %v", info.Name(), err) - } - // Split the relative path to get domain, version, and schema. - parts := strings.Split(relativePath, string(os.PathSeparator)) - - // Ensure that the file path has at least 3 parts: domain, version, and schema file. - if len(parts) < 3 { - return fmt.Errorf("invalid schema file structure, expected domain/version/schema.json but got: %s", relativePath) - } - - // Extract domain, version, and schema filename from the parts. - domain := parts[0] - version := parts[1] - schemaFileName := parts[2] - - // Construct a unique key combining domain, version, and schema name (e.g., ondc_trv10/v2.0.0/schema.json). - uniqueKey := fmt.Sprintf("%s/%s/%s", domain, version, schemaFileName) - // Store the compiled schema in the SchemaCache using the unique key. - vp.SchemaCache[uniqueKey] = compiledSchema - // Store the corresponding validator in the validatorCache using the same unique key. - validatorCache[uniqueKey] = &TekuriValidator{schema: compiledSchema} - } return nil - }) - if err != nil { + } + + // Start processing from the root schema directory + if err := processDir(schemaDir); err != nil { return nil, fmt.Errorf("failed to read schema directory: %v", err) } return validatorCache, nil } -var _ plugins.ValidatorProvider = (*TekuriValidatorProvider)(nil) +var _ definitions.ValidatorProvider = (*TekuriValidatorProvider)(nil) var providerInstance = &TekuriValidatorProvider{} // GetProvider returns the ValidatorProvider instance. -func GetProvider() plugins.ValidatorProvider { +func GetProvider() definitions.ValidatorProvider { return providerInstance } diff --git a/plugins/implementations/plugin_impl_test.go b/plugins/implementations/plugin_impl_test.go index 8ce7210..7ad610f 100644 --- a/plugins/implementations/plugin_impl_test.go +++ b/plugins/implementations/plugin_impl_test.go @@ -6,6 +6,8 @@ import ( "fmt" "log" "os" + "path/filepath" + "strings" "testing" ) @@ -15,10 +17,9 @@ type Payload struct { } type Context struct{} - type Message struct{} -func TestInitializeValidDirectory(t *testing.T) { +func TestValidDirectory(t *testing.T) { provider := &TekuriValidatorProvider{} schemaDir := "../testData/schema_valid/" _, err := provider.Initialize(schemaDir) @@ -27,46 +28,7 @@ func TestInitializeValidDirectory(t *testing.T) { } } -func TestInitializeInValidDirectory(t *testing.T) { - provider := &TekuriValidatorProvider{} - schemaDir := "../testData/schema/ondc_trv10/" - _, err := provider.Initialize(schemaDir) - if err != nil { - t.Fatalf("failed to read schema directory: %v", err) - } -} - -func TestInvalidCompileFile(t *testing.T) { - schemaDir := "../testData/invalid_compile_schema/" - if _, err := os.Stat(schemaDir); os.IsNotExist(err) { - t.Fatalf("Schema directory does not exist: %v", schemaDir) - } - provider := &TekuriValidatorProvider{} - compiledSchema, err := provider.Initialize(schemaDir) - if err != nil { - t.Fatalf("failed to compile JSON schema : %v", err) - } - if compiledSchema == nil { - t.Fatalf("compiled schema is nil : ") - - } -} - -func TestInvalidCompileSchema(t *testing.T) { - schemaDir := "../testData/invalid_schemas/" - if _, err := os.Stat(schemaDir); os.IsNotExist(err) { - t.Fatalf("Schema directory does not exist: %v", schemaDir) - } - provider := &TekuriValidatorProvider{} - compiledSchema, _ := provider.Initialize(schemaDir) - fmt.Println(compiledSchema) - if compiledSchema == nil { - t.Fatalf("compiled schema is nil : ") - - } -} - -func TestValidateData(t *testing.T) { +func TestValidPayload(t *testing.T) { schemaDir := "../testData/schema_valid/" if _, err := os.Stat(schemaDir); os.IsNotExist(err) { t.Fatalf("Schema directory does not exist: %v", schemaDir) @@ -88,7 +50,7 @@ func TestValidateData(t *testing.T) { t.Fatalf("No validators found in the map") } - payloadFilePath := "../testData/cancel.json" + payloadFilePath := "../testData/payloads/search.json" payloadData, err := os.ReadFile(payloadFilePath) if err != nil { t.Fatalf("Failed to read payload data: %v", err) @@ -106,13 +68,127 @@ func TestValidateData(t *testing.T) { } } -func TestInValidateData(t *testing.T) { +func TestInValidDirectory(t *testing.T) { + provider := &TekuriValidatorProvider{} + schemaDir := "../testData/schema/ondc_trv10/" + _, err := provider.Initialize(schemaDir) + if err != nil { + t.Fatalf("failed to read schema directory: %v", err) + } +} + +func TestInvalidCompileFile(t *testing.T) { + schemaDir := "../testData/invalid_compile_schema/" + // if _, err := os.Stat(schemaDir); os.IsNotExist(err) { + // t.Fatalf("Schema directory does not exist: %v", schemaDir) + // } + provider := &TekuriValidatorProvider{} + _, err := provider.Initialize(schemaDir) + if err != nil { + t.Fatalf("failed to compile JSON schema : %v", err) + } +} + +func TestInvalidCompileSchema(t *testing.T) { + schemaDir := "../testData/invalid_schemas/" + if _, err := os.Stat(schemaDir); os.IsNotExist(err) { + t.Fatalf("Schema directory does not exist: %v", schemaDir) + } + provider := &TekuriValidatorProvider{} + compiledSchema, _ := provider.Initialize(schemaDir) + fmt.Println(compiledSchema) + if compiledSchema == nil { + t.Fatalf("compiled schema is nil : ") + + } +} + +func TestPayloadWithExtraFields(t *testing.T) { + schemaDir := "../testData/schema_valid/" + if _, err := os.Stat(schemaDir); os.IsNotExist(err) { + t.Fatalf("Schema directory does not exist: %v", schemaDir) + } + provider := &TekuriValidatorProvider{} + validators, err := provider.Initialize(schemaDir) + if err != nil { + t.Fatalf("Failed to initialize schema provider: %v", err) + } + var validator *TekuriValidator + for _, v := range validators { + var ok bool + validator, ok = v.(*TekuriValidator) + if ok { + break + } + } + if validator == nil { + t.Fatalf("No validators found in the map") + } + payloadFilePath := "../testData/payloads/search_extraField.json" + payloadData, err := os.ReadFile(payloadFilePath) + if err != nil { + t.Fatalf("Failed to read payload data: %v", err) + } + var payload Payload + err = json.Unmarshal(payloadData, &payload) + if err != nil { + log.Fatalf("Failed to unmarshal payload data: %v", err) + } + err = validator.Validate(context.Background(), payloadData) + if err != nil { + t.Errorf("Validation failed due to extra fields: %v", err) + } else { + fmt.Println("Validation succeeded.") + } + +} + +func TestPayloadWithMissingFields(t *testing.T) { + schemaDir := "../testData/schema_valid/" + if _, err := os.Stat(schemaDir); os.IsNotExist(err) { + t.Fatalf("Schema directory does not exist: %v", schemaDir) + } + provider := &TekuriValidatorProvider{} + validators, err := provider.Initialize(schemaDir) + if err != nil { + t.Fatalf("Failed to initialize schema provider: %v", err) + } + var validator *TekuriValidator + for _, v := range validators { + var ok bool + validator, ok = v.(*TekuriValidator) + if ok { + break + } + } + if validator == nil { + t.Fatalf("No validators found in the map") + } + payloadFilePath := "../testData/payloads/search_missingField.json" + payloadData, err := os.ReadFile(payloadFilePath) + if err != nil { + t.Fatalf("Failed to read payload data: %v", err) + } + var payload Payload + err = json.Unmarshal(payloadData, &payload) + if err != nil { + log.Fatalf("Failed to unmarshal payload data: %v", err) + } + err = validator.Validate(context.Background(), payloadData) + if err != nil { + t.Errorf("Validation failed with missing fields: %v", err) + } else { + fmt.Println("Validation succeeded.") + } + +} + +func TestInValidPayload(t *testing.T) { schemaDir := "../testData/schema_valid/" if _, err := os.Stat(schemaDir); os.IsNotExist(err) { t.Fatalf("Schema directory does not exist: %v", schemaDir) } - provider := &TekuriValidatorProvider{} validators, err := provider.Initialize(schemaDir) if err != nil { @@ -132,7 +208,7 @@ func TestInValidateData(t *testing.T) { invalidPayloadData := []byte(`"invalid": "data"}`) err = validator.Validate(context.Background(), invalidPayloadData) if err != nil { - t.Errorf("Validation failed: %v", err) + t.Fatalf("Validation failed: %v", err) } } @@ -176,3 +252,52 @@ func TestGetProvider(t *testing.T) { t.Logf("GetProvider returned the expected providerInstance") } } + +func TestInitialize_SchemaPathNotDirectory(t *testing.T) { + vp := &TekuriValidatorProvider{} + filePath := "../testdata/directory.json" + + _, err := vp.Initialize(filePath) + + if err == nil || !strings.Contains(err.Error(), "provided schema path is not a directory") { + t.Errorf("Expected error about non-directory schema path, got: %v", err) + } +} + +func TestInitialize_InvalidSchemaFileStructure(t *testing.T) { + schemaDir := "../testData/invalid_structure" + provider := &TekuriValidatorProvider{} + + _, err := provider.Initialize(schemaDir) + if err == nil || !strings.Contains(err.Error(), "invalid schema file structure") { + t.Fatalf("Expected error for invalid schema file structure, got: %v", err) + } + +} + +func TestInitialize_FailedToGetRelativePath(t *testing.T) { + schemaDir := "../testData/valid_schemas" + provider := &TekuriValidatorProvider{} + + // Use an absolute path for schemaDir and a relative path for the file to simulate the error + absSchemaDir, err := filepath.Abs(schemaDir) + if err != nil { + t.Fatalf("Failed to get absolute path for schema directory: %v", err) + } + + // Temporarily change the working directory to simulate different volumes + originalWd, err := os.Getwd() + if err != nil { + t.Fatalf("Failed to get current working directory: %v", err) + } + defer os.Chdir(originalWd) // Restore the original working directory after the test + + // Change to a different directory + os.Chdir("/tmp") + + _, err = provider.Initialize(absSchemaDir) + if err == nil || !strings.Contains(err.Error(), "failed to get relative path for file") { + t.Fatalf("Expected error for failing to get relative path, got: %v", err) + } + +} diff --git a/plugins/implementations/tekuriValidator.so b/plugins/implementations/tekuriValidator.so index bb02825..5394a38 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 82e04f3..f99f0f3 100644 --- a/plugins/manager.go +++ b/plugins/manager.go @@ -2,11 +2,12 @@ package plugins import ( //"beckn-onix/plugins/plugin_definition" + "beckn-onix/plugins/definitions" "fmt" - "io/ioutil" "log" + "os" + "path/filepath" "plugin" - "runtime" "time" "gopkg.in/yaml.v2" @@ -36,22 +37,24 @@ type PluginDetails struct { // PluginManager manages the loading and execution of plugins. type PluginManager struct { - validatorProvider ValidatorProvider + validatorProvider definitions.ValidatorProvider } // NewValidatorProvider initializes the PluginManager with the given configuration. -func NewValidatorProvider(pluginsConfig PluginConfig) (*PluginManager, map[string]Validator, error) { +func NewValidatorProvider(pluginsConfig PluginConfig, debug bool) (*PluginManager, map[string]definitions.Validator, error) { start := time.Now() - - var memStatsBefore runtime.MemStats - runtime.ReadMemStats(&memStatsBefore) - + // var memStatsBefore runtime.MemStats + // Only capture metrics in debug mode + // if debug { + // start = time.Now() + // runtime.ReadMemStats(&memStatsBefore) + // } validationPlugin := pluginsConfig.Plugins.ValidationPlugin if validationPlugin.ID == "" { return nil, nil, fmt.Errorf("validation_plugin ID is empty") } - pluginPath := validationPlugin.PluginPath + validationPlugin.ID + ".so" + pluginPath := filepath.Join(validationPlugin.PluginPath, validationPlugin.ID+".so") // Check if the plugin path is empty if pluginPath == "" { @@ -68,7 +71,7 @@ func NewValidatorProvider(pluginsConfig PluginConfig) (*PluginManager, map[strin if err != nil { return nil, nil, err } - getProviderFunc, ok := vpSymbol.(func() ValidatorProvider) + getProviderFunc, ok := vpSymbol.(func() definitions.ValidatorProvider) if !ok { return nil, nil, fmt.Errorf("failed to cast to *plugins.ValidatorProvider") } @@ -80,10 +83,13 @@ func NewValidatorProvider(pluginsConfig PluginConfig) (*PluginManager, map[strin log.Fatalf("Failed to initialize validator provider: %v", err) } - var memStatsAfter runtime.MemStats - runtime.ReadMemStats(&memStatsAfter) - fmt.Printf("Memory allocated during plugin boot-up: %v MiB", (memStatsAfter.Alloc-memStatsBefore.Alloc)/1024/1024) - fmt.Println(" ") + // Log metrics if in debug mode + // if debug { + // var memStatsAfter runtime.MemStats + // runtime.ReadMemStats(&memStatsAfter) + // fmt.Printf("Memory allocated during plugin boot-up: %v MiB\n", (memStatsAfter.Alloc-memStatsBefore.Alloc)/1024/1024) + // fmt.Printf("Plugin boot-up executed in %s\n", time.Since(start)) + // } fmt.Printf("plugin boot-up executed in %s\n", time.Since(start)) return &PluginManager{validatorProvider: validatorProvider}, validator, nil @@ -93,7 +99,7 @@ func NewValidatorProvider(pluginsConfig PluginConfig) (*PluginManager, map[strin func LoadPluginsConfig(filePath string) (PluginConfig, error) { // start := time.Now() - data, err := ioutil.ReadFile(filePath) + data, err := os.ReadFile(filePath) if err != nil { return PluginConfig{}, err } diff --git a/plugins/schemas.zip b/plugins/schemas.zip deleted file mode 100644 index 0c9583d..0000000 Binary files a/plugins/schemas.zip and /dev/null differ diff --git a/plugins/testData/invalid_schemas/init.json b/plugins/testData/directory.json similarity index 100% rename from plugins/testData/invalid_schemas/init.json rename to plugins/testData/directory.json diff --git a/plugins/testData/invalid_compile_schema/select.json b/plugins/testData/invalid_compile_schema/select.json deleted file mode 100644 index ad4d49d..0000000 --- a/plugins/testData/invalid_compile_schema/select.json +++ /dev/null @@ -1,208 +0,0 @@ -{ - "$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": { - "location": { - "type": "value", - "properties": { - "city": { - "type": "value", - "properties": { - }, - "required": ["code"] - }, - "country": { - "type": "object", - "properties": { - }, - "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/testData/on_status.json b/plugins/testData/invalid_schemas/ondc_trv10/init.json similarity index 100% rename from plugins/testData/on_status.json rename to plugins/testData/invalid_schemas/ondc_trv10/init.json diff --git a/plugins/testData/invalid_structure/v2.0.0/schema.json b/plugins/testData/invalid_structure/v2.0.0/schema.json new file mode 100644 index 0000000..3b7396d --- /dev/null +++ b/plugins/testData/invalid_structure/v2.0.0/schema.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Example Schema", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "age": { + "type": "integer" + } + }, + "required": ["name", "age"] +} \ No newline at end of file diff --git a/plugins/testData/cancel.json b/plugins/testData/payloads/cancel.json similarity index 100% rename from plugins/testData/cancel.json rename to plugins/testData/payloads/cancel.json diff --git a/plugins/testData/confirm.json b/plugins/testData/payloads/confirm.json similarity index 100% rename from plugins/testData/confirm.json rename to plugins/testData/payloads/confirm.json diff --git a/plugins/testData/search.json b/plugins/testData/payloads/search.json similarity index 100% rename from plugins/testData/search.json rename to plugins/testData/payloads/search.json diff --git a/plugins/testData/payloads/search_extraField.json b/plugins/testData/payloads/search_extraField.json new file mode 100644 index 0000000..ef8ad94 --- /dev/null +++ b/plugins/testData/payloads/search_extraField.json @@ -0,0 +1,63 @@ +{ + "context": { + "action": "search", + "bap_id": "bap.example.com", + "bap_uri": "https://example-bap.com/prod/trv10", + "domain": "example-domain", + "location": { + "city": { + "code": "PUN" + }, + "country": { + "code": "IND" + } + }, + "message_id": "123e4567-e89b-12d3-a456-426614174000", + "timestamp": "2025-03-06T12:00:00Z", + "transaction_id": "123e4567-e89b-12d3-a456-426614174001", + "ttl": "PT30M", + "version": "1.0.0", + "extra_field": "unexpected_value" + }, + "message": { + "intent": { + "fulfillment": { + "stops": [ + { + "location": { + "gps": "19.0760,72.8777", + "created_at": "unexpected_value" + }, + "type": "START" + }, + { + "location": { + "gps": "18.5204,73.8567" + }, + "type": "END" + } + ] + }, + "payment": { + "collected_by": "BPP", + "tags": [ + { + "descriptor": { + "code": "some-code" + }, + "display": true, + "list": [ + { + "descriptor": { + "code": "list-code" + }, + "value": "list-value" + } + ] + } + ] + } + } + } + } + \ No newline at end of file diff --git a/plugins/testData/payloads/search_missingField.json b/plugins/testData/payloads/search_missingField.json new file mode 100644 index 0000000..792e533 --- /dev/null +++ b/plugins/testData/payloads/search_missingField.json @@ -0,0 +1,65 @@ +{ + "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" + } + }, + "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": { + "payment": { + "collected_by": "BPP", + "tags": [ + { + "descriptor": { + "code": "BUYER_FINDER_FEES" + }, + "display": false, + "list": [ + { + "descriptor": { + "code": "BUYER_FINDER_FEES_PERCENTAGE" + }, + "value": "1" + } + ] + }, + { + "descriptor": { + "code": "SETTLEMENT_TERMS" + }, + "display": false, + "list": [ + { + "descriptor": { + "code": "DELAY_INTEREST" + }, + "value": "5" + }, + { + "descriptor": { + "code": "STATIC_TERMS" + }, + "value": "https://example-test-bap.com/static-terms.txt" + } + ] + } + ] + } + } + } + } + \ No newline at end of file diff --git a/plugins/testData/select.json b/plugins/testData/payloads/select.json similarity index 100% rename from plugins/testData/select.json rename to plugins/testData/payloads/select.json diff --git a/plugins/testData/valid_schemas/schema.json b/plugins/testData/valid_schemas/schema.json new file mode 100644 index 0000000..3b7396d --- /dev/null +++ b/plugins/testData/valid_schemas/schema.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Example Schema", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "age": { + "type": "integer" + } + }, + "required": ["name", "age"] +} \ No newline at end of file diff --git a/test.go b/test.go index de64fdc..5f3eecd 100644 --- a/test.go +++ b/test.go @@ -67,17 +67,15 @@ func validateHandler(w http.ResponseWriter, r *http.Request) { domain = strings.ReplaceAll(domain, ":", "_") schemaFileName := fmt.Sprintf("%s/%s/%s.json", domain, version, endpoint) - fmt.Println("printing schema file name : ", schemaFileName) - - // schemaFileName := fmt.Sprintf("%s.json", endpoint) pluginsConfig, err := plugins.LoadPluginsConfig("plugins/config.yaml") if err != nil { http.Error(w, "Failed to load plugins configuration", http.StatusInternalServerError) return } + debug := true - _, validators, err := plugins.NewValidatorProvider(pluginsConfig) + _, validators, err := plugins.NewValidatorProvider(pluginsConfig, debug) if err != nil { http.Error(w, "Failed to create PluginManager", http.StatusInternalServerError) return