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 @@
+
+
+
+
+
+
+
package main
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "beckn-onix/plugins/definitions"
+
+ "github.com/santhosh-tekuri/jsonschema/v6"
+)
+
+// TekuriValidator implements the Validator interface using the santhosh-tekuri/jsonschema package.
+type TekuriValidator struct {
+ schema *jsonschema.Schema
+}
+
+// TekuriValidatorProvider is responsible for managing and providing access to the JSON schema validators.
+type TekuriValidatorProvider struct {
+ SchemaCache map[string]*jsonschema.Schema
+}
+
+// Validate validates the given data against the schema.
+func (v *TekuriValidator) Validate(ctx context.Context, data []byte) error {
+ var jsonData interface{}
+ if err := json.Unmarshal(data, &jsonData); err != nil {
+ return err
+ }
+ err := v.schema.Validate(jsonData)
+ if err != nil {
+ // TODO: Integrate with the logging module once it is ready
+ fmt.Printf("Validation error: %v\n", err)
+ }
+
+ return err
+}
+
+// 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]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]definitions.Validator)
+ compiler := jsonschema.NewCompiler()
+
+ // 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 fmt.Errorf("failed to read directory: %v", 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)
+ }
+ // Ensure relativePath is not empty
+ if relativePath == "" {
+ return fmt.Errorf("relative path is empty for file: %s", path)
+ }
+ // 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}
+ }
+ }
+ return 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 _ definitions.ValidatorProvider = (*TekuriValidatorProvider)(nil)
+
+var providerInstance = &TekuriValidatorProvider{}
+
+// GetProvider returns the ValidatorProvider instance.
+func GetProvider() definitions.ValidatorProvider {
+ return providerInstance
+}
+
+
+
+
+
+
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