diff --git a/go.mod b/go.mod
index 340096e..7ea3206 100644
--- a/go.mod
+++ b/go.mod
@@ -7,13 +7,4 @@ require (
gopkg.in/yaml.v2 v2.4.0
)
-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
-)
+require golang.org/x/text v0.14.0 // indirect
diff --git a/go.sum b/go.sum
index 25d9a0b..2c8aa28 100644
--- a/go.sum
+++ b/go.sum
@@ -1,18 +1,10 @@
-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
deleted file mode 100644
index bbf0ca6..0000000
--- a/plugins/config.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
-plugins:
- validation_plugin:
- id: tekuriValidator
- config:
- schema_dir:
- plugin_path: plugins/implementations/
\ No newline at end of file
diff --git a/plugins/definitions/plugin.go b/plugins/definitions/plugin.go
deleted file mode 100644
index 3f31bd6..0000000
--- a/plugins/definitions/plugin.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package definitions
-
-import "context"
-
-// Validator interface for schema validation
-type Validator interface {
- Validate(ctx context.Context, b []byte) error
-}
-
-// ValidatorProvider interface for creating validators
-type ValidatorProvider interface {
- //Get(p string) (Validator, error)
- Initialize(schemaDir string) (map[string]Validator, error)
-}
diff --git a/plugins/implementations/coverage.html b/plugins/implementations/coverage.html
deleted file mode 100644
index 92dae5f..0000000
--- a/plugins/implementations/coverage.html
+++ /dev/null
@@ -1,246 +0,0 @@
-
-
-
-
-
-
-
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_test.go b/plugins/implementations/plugin_impl_test.go
deleted file mode 100644
index 7ad610f..0000000
--- a/plugins/implementations/plugin_impl_test.go
+++ /dev/null
@@ -1,303 +0,0 @@
-package main
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "log"
- "os"
- "path/filepath"
- "strings"
- "testing"
-)
-
-type Payload struct {
- Context Context `json:"context"`
- Message Message `json:"message"`
-}
-
-type Context struct{}
-type Message struct{}
-
-func TestValidDirectory(t *testing.T) {
- provider := &TekuriValidatorProvider{}
- schemaDir := "../testData/schema_valid/"
- _, err := provider.Initialize(schemaDir)
- if err != nil {
- t.Fatalf("expected no error, got %v", err)
- }
-}
-
-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)
- }
- 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.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: %v", err)
- } else {
- fmt.Println("Validation succeeded.")
- }
-}
-
-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 {
- 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")
- }
- invalidPayloadData := []byte(`"invalid": "data"}`)
- err = validator.Validate(context.Background(), invalidPayloadData)
- if err != nil {
- t.Fatalf("Validation failed: %v", err)
- }
-}
-
-func TestInValidateUnmarshalData(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")
- }
- invalidPayloadData := []byte(`{"invalid": "data`)
- err = validator.Validate(context.Background(), invalidPayloadData)
- if err != nil {
- t.Errorf("Error while unmarshaling the data: %v", err)
- }
-}
-
-func TestGetProvider(t *testing.T) {
- expected := providerInstance
- actual := GetProvider()
-
- if actual != expected {
- t.Fatalf("expected %v, got %v", expected, actual)
- } else {
- 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/manager.go b/plugins/manager.go
deleted file mode 100644
index f99f0f3..0000000
--- a/plugins/manager.go
+++ /dev/null
@@ -1,115 +0,0 @@
-package plugins
-
-import (
- //"beckn-onix/plugins/plugin_definition"
- "beckn-onix/plugins/definitions"
- "fmt"
- "log"
- "os"
- "path/filepath"
- "plugin"
- "time"
-
- "gopkg.in/yaml.v2"
-)
-
-// PluginConfig represents the configuration for plugins, including the plugins themselves.
-type PluginConfig struct {
- Plugins Plugins `yaml:"plugins"`
-}
-
-// Plugins holds the various plugin types used in the configuration.
-type Plugins struct {
- ValidationPlugin ValidationPlugin `yaml:"validation_plugin"`
-}
-
-// ValidationPlugin represents a plugin with an ID, configuration, and the path to the plugin.
-type ValidationPlugin struct {
- ID string `yaml:"id"`
- Config PluginDetails `yaml:"config"`
- PluginPath string `yaml:"plugin_path"`
-}
-
-// PluginDetails contains information about the plugin schema directory.
-type PluginDetails struct {
- Schema string `yaml:"schema_dir"`
-}
-
-// PluginManager manages the loading and execution of plugins.
-type PluginManager struct {
- validatorProvider definitions.ValidatorProvider
-}
-
-// NewValidatorProvider initializes the PluginManager with the given configuration.
-func NewValidatorProvider(pluginsConfig PluginConfig, debug bool) (*PluginManager, map[string]definitions.Validator, error) {
- start := time.Now()
- // 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 := filepath.Join(validationPlugin.PluginPath, validationPlugin.ID+".so")
-
- // Check if the plugin path is empty
- if pluginPath == "" {
- return nil, nil, fmt.Errorf("plugin path is empty")
- }
-
- // Load the plugin
- p, err := plugin.Open(pluginPath)
- if err != nil {
- return nil, nil, fmt.Errorf("failed to open plugin: %v", err)
- }
-
- vpSymbol, err := p.Lookup("GetProvider")
- if err != nil {
- return nil, nil, err
- }
- getProviderFunc, ok := vpSymbol.(func() definitions.ValidatorProvider)
- if !ok {
- return nil, nil, fmt.Errorf("failed to cast to *plugins.ValidatorProvider")
- }
- validatorProvider := getProviderFunc()
-
- schemaDir := pluginsConfig.Plugins.ValidationPlugin.Config.Schema
- validator, err := validatorProvider.Initialize(schemaDir)
- if err != nil {
- log.Fatalf("Failed to initialize validator provider: %v", err)
- }
-
- // 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
-}
-
-// LoadPluginsConfig loads the plugins configuration from a YAML file.
-func LoadPluginsConfig(filePath string) (PluginConfig, error) {
- // start := time.Now()
-
- data, err := os.ReadFile(filePath)
- if err != nil {
- return PluginConfig{}, err
- }
-
- var config PluginConfig
- err = yaml.Unmarshal(data, &config)
- if err != nil {
- return PluginConfig{}, err
- }
- // fmt.Printf("loadconfig executed in %s\n", time.Since(start))
-
- return config, nil
-}
diff --git a/plugins/testData/invalid_schemas/ondc_trv10/init.json b/plugins/testData/invalid_schemas/ondc_trv10/init.json
deleted file mode 100644
index e69de29..0000000
diff --git a/plugins/testData/invalid_structure/v2.0.0/schema.json b/plugins/testData/invalid_structure/v2.0.0/schema.json
deleted file mode 100644
index 3b7396d..0000000
--- a/plugins/testData/invalid_structure/v2.0.0/schema.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "$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/valid_schemas/schema.json b/plugins/testData/valid_schemas/schema.json
deleted file mode 100644
index 3b7396d..0000000
--- a/plugins/testData/valid_schemas/schema.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "$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/shared/plugin/definition/validator.go b/shared/plugin/definition/validator.go
new file mode 100644
index 0000000..776029f
--- /dev/null
+++ b/shared/plugin/definition/validator.go
@@ -0,0 +1,16 @@
+package definition
+
+import (
+ "context"
+ "net/url"
+)
+
+// Validator interface for schema validation
+type Validator interface {
+ Validate(ctx context.Context, url url.URL, payload []byte) (bool, error)
+}
+
+// ValidatorProvider interface for creating validators
+type ValidatorProvider interface {
+ New(ctx context.Context, config map[string]string) (map[string]Validator, error)
+}
diff --git a/plugins/implementations/tekuriValidator.so b/shared/plugin/implementations/validator.so
similarity index 55%
rename from plugins/implementations/tekuriValidator.so
rename to shared/plugin/implementations/validator.so
index 5394a38..5b01f75 100644
Binary files a/plugins/implementations/tekuriValidator.so and b/shared/plugin/implementations/validator.so differ
diff --git a/shared/plugin/implementations/validator/cmd/plugin.go b/shared/plugin/implementations/validator/cmd/plugin.go
new file mode 100644
index 0000000..0b3b930
--- /dev/null
+++ b/shared/plugin/implementations/validator/cmd/plugin.go
@@ -0,0 +1,31 @@
+package main
+
+import (
+ "context"
+
+ "beckn-onix/shared/plugin/definition"
+ "beckn-onix/shared/plugin/implementations/validator"
+)
+
+// ValidatorProvider provides instances of Validator.
+type ValidatorProvider struct{}
+
+// New initializes a new Verifier instance.
+func (vp ValidatorProvider) New(ctx context.Context, config map[string]string) (map[string]definition.Validator, error) {
+ // Create a new Validator instance with the provided configuration
+ validators, err := validator.New(ctx, config)
+ if err != nil {
+ return nil, err
+ }
+
+ // Convert the map to the expected type
+ result := make(map[string]definition.Validator)
+ for key, val := range validators {
+ result[key] = val
+ }
+
+ return result, nil
+}
+
+// Provider is the exported symbol that the plugin manager will look for.
+var Provider definition.ValidatorProvider = ValidatorProvider{}
diff --git a/shared/plugin/implementations/validator/cmd/plugin_test.go b/shared/plugin/implementations/validator/cmd/plugin_test.go
new file mode 100644
index 0000000..fc73ff2
--- /dev/null
+++ b/shared/plugin/implementations/validator/cmd/plugin_test.go
@@ -0,0 +1,85 @@
+package main
+
+import (
+ "context"
+ "errors"
+ "net/url"
+ "testing"
+
+ "beckn-onix/shared/plugin/definition"
+)
+
+// MockValidator is a mock implementation of the Validator interface for testing.
+type MockValidator struct{}
+
+func (m *MockValidator) Validate(ctx context.Context, u url.URL, data []byte) (bool, error) {
+ return true, nil
+}
+
+// Mock New function for testing
+func MockNew(ctx context.Context, config map[string]string) (map[string]definition.Validator, error) {
+ if config["error"] == "true" {
+ return nil, errors.New("mock error")
+ }
+
+ return map[string]definition.Validator{
+ "validator1": &MockValidator{},
+ "validator2": &MockValidator{},
+ }, nil
+}
+
+func TestValidatorProvider_New(t *testing.T) {
+ tests := []struct {
+ name string
+ config map[string]string
+ expectedError string
+ expectedCount int
+ }{
+ {
+ name: "Successful initialization",
+ config: map[string]string{"some_key": "some_value"},
+ expectedError: "",
+ expectedCount: 2, // Expecting 2 mock validators
+ },
+ {
+ name: "Error during initialization (mock error)",
+ config: map[string]string{"error": "true"},
+ expectedError: "mock error",
+ expectedCount: 0,
+ },
+ {
+ name: "Empty config map",
+ config: map[string]string{},
+ expectedError: "",
+ expectedCount: 2, // Expecting 2 mock validators
+ },
+ {
+ name: "Non-empty config with invalid key",
+ config: map[string]string{"invalid_key": "invalid_value"},
+ expectedError: "",
+ expectedCount: 2, // Expecting 2 mock validators
+ },
+ }
+
+ // Using the mock New function directly for testing
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ // Create a ValidatorProvider with the mock New function
+ vp := &ValidatorProvider{}
+ validators, err := vp.New(context.Background(), tt.config)
+
+ // Check for expected error
+ if tt.expectedError != "" {
+ if err == nil || err.Error() != tt.expectedError {
+ t.Errorf("expected error %q, got %v", tt.expectedError, err)
+ }
+ return
+ }
+
+ // Check for expected number of validators
+ if len(validators) != tt.expectedCount {
+ t.Errorf("expected %d validators, got %d", tt.expectedCount, len(validators))
+ }
+ })
+ }
+}
diff --git a/plugins/implementations/plugin_impl.go b/shared/plugin/implementations/validator/validator.go
similarity index 58%
rename from plugins/implementations/plugin_impl.go
rename to shared/plugin/implementations/validator/validator.go
index d6a36eb..35ab969 100644
--- a/plugins/implementations/plugin_impl.go
+++ b/shared/plugin/implementations/validator/validator.go
@@ -1,51 +1,88 @@
-package main
+package validator
import (
"context"
"encoding/json"
"fmt"
+ "net/url"
"os"
+ "path"
"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 {
+// Validator implements the Validator interface.
+type Validator struct {
+ config map[string]string
+ schema *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 {
- var jsonData interface{}
- if err := json.Unmarshal(data, &jsonData); err != nil {
- return err
- }
- err := v.schema.Validate(jsonData)
+// New creates a new Verifier instance.
+func New(ctx context.Context, config map[string]string) (map[string]*Validator, error) {
+ v := &Validator{config: config}
+ // Call Initialise function to load schemas and get validators
+ validators, err := v.Initialise()
if err != nil {
- // TODO: Integrate with the logging module once it is ready
- fmt.Printf("Validation error: %v\n", err)
+ return nil, fmt.Errorf("failed to initialise validators: %v", err)
}
-
- return err
+ return validators, nil
}
-// Initialize initializes the validator provider by compiling all the JSON schema files
+// Payload represents the structure of the data payload with context information.
+type Payload struct {
+ Context struct {
+ Domain string `json:"domain"`
+ Version string `json:"version"`
+ } `json:"context"`
+}
+
+// ValidatorProvider provides instances of Validator.
+type ValidatorProvider struct{}
+
+// Validate validates the given data against the schema.
+func (v *Validator) Validate(ctx context.Context, url url.URL, payload []byte) (bool, error) {
+ var payloadData Payload
+ err := json.Unmarshal(payload, &payloadData)
+ if err != nil {
+ return false, fmt.Errorf("failed to parse JSON payload: %v", err)
+ }
+
+ // Extract domain, version, and endpoint from the payload and uri
+ domain := payloadData.Context.Domain
+ version := payloadData.Context.Version
+ version = fmt.Sprintf("v%s", version)
+
+ endpoint := path.Base(url.String())
+ fmt.Println("Handling request for endpoint:", endpoint)
+ domain = strings.ToLower(domain)
+ domain = strings.ReplaceAll(domain, ":", "_")
+
+ //schemaFileName := fmt.Sprintf("%s_%s_%s", domain, version, endpoint)
+ var jsonData interface{}
+ if err := json.Unmarshal(payload, &jsonData); err != nil {
+ return false, err
+ }
+ err = v.schema.Validate(jsonData)
+ if err != nil {
+ // TODO: Integrate with the logging module once it is ready
+ return false, fmt.Errorf("Validation failed: %v", err)
+ }
+
+ return true, nil
+}
+
+// Initialise initialises 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)
+func (v *Validator) Initialise() (map[string]*Validator, error) {
+ // Initialize SchemaCache as an empty Map if it's nil
+ if v.SchemaCache == nil {
+ v.SchemaCache = make(map[string]*jsonschema.Schema)
}
+ schemaDir := v.config["schema_dir"]
// Check if the directory exists and is accessible
info, err := os.Stat(schemaDir)
if err != nil {
@@ -59,7 +96,7 @@ func (vp *TekuriValidatorProvider) Initialize(schemaDir string) (map[string]defi
}
// Initialize the validatorCache map to store the Validator instances associated with each schema.
- validatorCache := make(map[string]definitions.Validator)
+ validatorCache := make(map[string]*Validator)
compiler := jsonschema.NewCompiler()
// Helper function to process directories recursively
@@ -83,9 +120,6 @@ func (vp *TekuriValidatorProvider) Initialize(schemaDir string) (map[string]defi
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)
@@ -105,17 +139,18 @@ func (vp *TekuriValidatorProvider) Initialize(schemaDir string) (map[string]defi
domain := strings.TrimSpace(parts[0])
version := strings.TrimSpace(parts[1])
schemaFileName := strings.TrimSpace(parts[2])
+ schemaFileName = strings.TrimSuffix(schemaFileName, ".json")
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)
+ // Construct a unique key combining domain, version, and schema name (e.g., ondc_trv10_v2.0.0_schema).
+ uniqueKey := fmt.Sprintf("%s_%s_%s", domain, version, schemaFileName)
// Store the compiled schema in the SchemaCache using the unique key.
- vp.SchemaCache[uniqueKey] = compiledSchema
+ v.SchemaCache[uniqueKey] = compiledSchema
// Store the corresponding validator in the validatorCache using the same unique key.
- validatorCache[uniqueKey] = &TekuriValidator{schema: compiledSchema}
+ validatorCache[uniqueKey] = &Validator{schema: compiledSchema}
}
}
return nil
@@ -128,12 +163,3 @@ func (vp *TekuriValidatorProvider) Initialize(schemaDir string) (map[string]defi
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/shared/plugin/implementations/validator/validator_test.go b/shared/plugin/implementations/validator/validator_test.go
new file mode 100644
index 0000000..9ae3c3a
--- /dev/null
+++ b/shared/plugin/implementations/validator/validator_test.go
@@ -0,0 +1,291 @@
+package validator
+
+import (
+ "context"
+ "net/url"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+func TestValidator_Validate(t *testing.T) {
+ tests := []struct {
+ name string
+ url string
+ payload string
+ wantValid bool
+ wantErr string
+ }{
+ {
+ name: "Valid payload",
+ url: "http://example.com/endpoint",
+ payload: `{"context": {"domain": "example", "version": "1.0"}}`,
+ wantValid: true,
+ wantErr: "",
+ },
+ {
+ name: "Invalid JSON payload",
+ url: "http://example.com/endpoint",
+ payload: `{"context": {"domain": "example", "version": "1.0"`,
+ wantValid: false,
+ wantErr: "failed to parse JSON payload",
+ },
+ {
+ name: "Schema validation failure",
+ url: "http://example.com/endpoint",
+ payload: `{"context": {"domain": "invalid", "version": "1.0"}}`,
+ wantValid: false,
+ wantErr: "Validation failed",
+ },
+ }
+
+ // Setup a temporary schema directory for testing
+ schemaDir := filepath.Join(os.TempDir(), "schemas")
+ defer os.RemoveAll(schemaDir)
+ os.MkdirAll(schemaDir, 0755)
+
+ // Create a sample schema file
+ schemaContent := `{
+ "type": "object",
+ "properties": {
+ "context": {
+ "type": "object",
+ "properties": {
+ "domain": {"type": "string"},
+ "version": {"type": "string"}
+ },
+ "required": ["domain", "version"]
+ }
+ },
+ "required": ["context"]
+ }`
+ schemaFile := filepath.Join(schemaDir, "example", "1.0", "endpoint.json")
+ os.MkdirAll(filepath.Dir(schemaFile), 0755)
+ os.WriteFile(schemaFile, []byte(schemaContent), 0644)
+
+ config := map[string]string{"schema_dir": schemaDir}
+ v, err := New(context.Background(), config)
+ if err != nil {
+ t.Fatalf("Failed to create validator: %v", err)
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ u, _ := url.Parse(tt.url)
+ valid, err := v["example_1.0_endpoint"].Validate(context.Background(), *u, []byte(tt.payload))
+ if (err != nil && !strings.Contains(err.Error(), tt.wantErr)) || (err == nil && tt.wantErr != "") {
+ t.Errorf("Error: Validate() returned error = %v, expected error = %v", err, tt.wantErr)
+ return
+ }
+ if valid != tt.wantValid {
+ t.Errorf("Error: Validate() returned valid = %v, expected valid = %v", valid, tt.wantValid)
+ } else {
+ t.Logf("Test %s passed: valid = %v", tt.name, valid)
+ }
+ })
+ }
+}
+
+func TestValidator_Initialise(t *testing.T) {
+ tests := []struct {
+ name string
+ setupFunc func(schemaDir string) error
+ wantErr string
+ }{
+ {
+ name: "Schema directory does not exist",
+ setupFunc: func(schemaDir string) error {
+ // Do not create the schema directory
+ return nil
+ },
+ wantErr: "schema directory does not exist",
+ },
+ {
+ name: "Schema path is not a directory",
+ setupFunc: func(schemaDir string) error {
+ // Create a file instead of a directory
+ return os.WriteFile(schemaDir, []byte{}, 0644)
+ },
+ wantErr: "provided schema path is not a directory",
+ },
+ {
+ name: "Invalid schema file structure",
+ setupFunc: func(schemaDir string) error {
+ // Create an invalid schema file structure
+ invalidSchemaFile := filepath.Join(schemaDir, "invalid_schema.json")
+ os.MkdirAll(filepath.Dir(invalidSchemaFile), 0755)
+ return os.WriteFile(invalidSchemaFile, []byte(`{}`), 0644)
+ },
+ wantErr: "invalid schema file structure",
+ },
+ {
+ name: "Failed to compile JSON schema",
+ setupFunc: func(schemaDir string) error {
+ // Create a schema file with invalid JSON
+ invalidSchemaFile := filepath.Join(schemaDir, "example", "1.0", "endpoint.json")
+ os.MkdirAll(filepath.Dir(invalidSchemaFile), 0755)
+ return os.WriteFile(invalidSchemaFile, []byte(`{invalid json}`), 0644)
+ },
+ wantErr: "failed to compile JSON schema",
+ },
+ {
+ name: "Invalid schema file structure with empty components",
+ setupFunc: func(schemaDir string) error {
+ // Create a schema file with empty domain, version, or schema name
+ invalidSchemaFile := filepath.Join(schemaDir, "", "1.0", "endpoint.json")
+ os.MkdirAll(filepath.Dir(invalidSchemaFile), 0755)
+ return os.WriteFile(invalidSchemaFile, []byte(`{
+ "type": "object",
+ "properties": {
+ "context": {
+ "type": "object",
+ "properties": {
+ "domain": {"type": "string"},
+ "version": {"type": "string"}
+ },
+ "required": ["domain", "version"]
+ }
+ },
+ "required": ["context"]
+ }`), 0644)
+ },
+ wantErr: "invalid schema file structure, one or more components are empty",
+ },
+ {
+ name: "Failed to read directory",
+ setupFunc: func(schemaDir string) error {
+ // Create a directory and remove read permissions
+ os.MkdirAll(schemaDir, 0000)
+ return nil
+ },
+ wantErr: "failed to read directory",
+ },
+ {
+ name: "Failed to access schema directory",
+ setupFunc: func(schemaDir string) error {
+ // Create a directory and remove access permissions
+ os.MkdirAll(schemaDir, 0000)
+ return nil
+ },
+ wantErr: "failed to access schema directory",
+ },
+ {
+ name: "Valid schema directory",
+ setupFunc: func(schemaDir string) error {
+ // Create a valid schema file
+ validSchemaFile := filepath.Join(schemaDir, "example", "1.0", "endpoint.json")
+ os.MkdirAll(filepath.Dir(validSchemaFile), 0755)
+ return os.WriteFile(validSchemaFile, []byte(`{
+ "type": "object",
+ "properties": {
+ "context": {
+ "type": "object",
+ "properties": {
+ "domain": {"type": "string"},
+ "version": {"type": "string"}
+ },
+ "required": ["domain", "version"]
+ }
+ },
+ "required": ["context"]
+ }`), 0644)
+ },
+ wantErr: "",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ // Setup a temporary schema directory for testing
+ schemaDir := filepath.Join(os.TempDir(), "schemas")
+ defer os.RemoveAll(schemaDir)
+
+ // Run the setup function to prepare the test case
+ if err := tt.setupFunc(schemaDir); err != nil {
+ t.Fatalf("setupFunc() error = %v", err)
+ }
+
+ config := map[string]string{"schema_dir": schemaDir}
+ v := &Validator{config: config}
+
+ _, err := v.Initialise()
+ if (err != nil && !strings.Contains(err.Error(), tt.wantErr)) || (err == nil && tt.wantErr != "") {
+ t.Errorf("Error: Initialise() returned error = %v, expected error = %v", err, tt.wantErr)
+ } else if err == nil {
+ t.Logf("Test %s passed: validator initialized successfully", tt.name)
+ } else {
+ t.Logf("Test %s passed with expected error: %v", tt.name, err)
+ }
+ })
+ }
+}
+
+func TestValidator_New(t *testing.T) {
+ tests := []struct {
+ name string
+ config map[string]string
+ setupFunc func(schemaDir string) error
+ wantErr string
+ }{
+ {
+ name: "Failed to initialise validators",
+ config: map[string]string{
+ "schema_dir": "/invalid/path",
+ },
+ setupFunc: func(schemaDir string) error {
+ // Do not create the schema directory
+ return nil
+ },
+ wantErr: "failed to initialise validators",
+ },
+ {
+ name: "Valid initialisation",
+ config: map[string]string{
+ "schema_dir": "/valid/path",
+ },
+ setupFunc: func(schemaDir string) error {
+ // Create a valid schema directory and file
+ validSchemaFile := filepath.Join(schemaDir, "example", "1.0", "endpoint.json")
+ os.MkdirAll(filepath.Dir(validSchemaFile), 0755)
+ return os.WriteFile(validSchemaFile, []byte(`{
+ "type": "object",
+ "properties": {
+ "context": {
+ "type": "object",
+ "properties": {
+ "domain": {"type": "string"},
+ "version": {"type": "string"}
+ },
+ "required": ["domain", "version"]
+ }
+ },
+ "required": ["context"]
+ }`), 0644)
+ },
+ wantErr: "",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ // Setup a temporary schema directory for testing
+ schemaDir := tt.config["schema_dir"]
+ defer os.RemoveAll(schemaDir)
+
+ // Run the setup function to prepare the test case
+ if err := tt.setupFunc(schemaDir); err != nil {
+ t.Fatalf("setupFunc() error = %v", err)
+ }
+
+ _, err := New(context.Background(), tt.config)
+ if (err != nil && !strings.Contains(err.Error(), tt.wantErr)) || (err == nil && tt.wantErr != "") {
+ t.Errorf("Error: New() returned error = %v, expected error = %v", err, tt.wantErr)
+ } else if err == nil {
+ t.Logf("Test %s passed: validator initialized successfully", tt.name)
+ } else {
+ t.Logf("Test %s passed with expected error: %v", tt.name, err)
+ }
+ })
+ }
+}
diff --git a/shared/plugin/manager.go b/shared/plugin/manager.go
new file mode 100644
index 0000000..8b70b54
--- /dev/null
+++ b/shared/plugin/manager.go
@@ -0,0 +1,137 @@
+package plugin
+
+import (
+ "beckn-onix/shared/plugin/definition"
+ "context"
+ "fmt"
+ "os"
+ "path/filepath"
+ "plugin"
+ "strings"
+
+ "gopkg.in/yaml.v2"
+)
+
+// ValidationPluginConfig represents configuration details for a plugin.
+type ValidationPluginConfig struct {
+ ID string `yaml:"id"`
+ Schema SchemaDetails `yaml:"config"`
+ PluginPath string `yaml:"plugin_path"`
+}
+
+// SchemaDetails contains information about the plugin schema directory.
+type SchemaDetails struct {
+ SchemaDir string `yaml:"schema_dir"`
+}
+
+// Config represents the configuration for the application, including plugin configurations.
+type Config struct {
+ Plugins struct {
+ ValidationPlugin ValidationPluginConfig `yaml:"validation_plugin"`
+ } `yaml:"plugins"`
+}
+
+// Manager handles dynamic plugin loading and management.
+type Manager struct {
+ vp definition.ValidatorProvider
+ validators map[string]definition.Validator
+ cfg *Config
+}
+
+// NewManager initializes a new Manager with the given configuration file.
+func NewManager(ctx context.Context, cfg *Config) (*Manager, error) {
+ if cfg == nil {
+ return nil, fmt.Errorf("configuration cannot be nil")
+ }
+
+ // Load validator plugin
+ vp, err := provider[definition.ValidatorProvider](cfg.Plugins.ValidationPlugin.PluginPath, cfg.Plugins.ValidationPlugin.ID)
+ if err != nil {
+ return nil, fmt.Errorf("failed to load validator plugin: %w", err)
+ }
+ if vp == nil {
+ return nil, fmt.Errorf("validator provider is nil")
+ }
+
+ // Initialize validator
+ validatorMap, err := vp.New(ctx, map[string]string{
+ "schema_dir": cfg.Plugins.ValidationPlugin.Schema.SchemaDir,
+ })
+ if err != nil {
+ return nil, fmt.Errorf("failed to initialize validator: %w", err)
+ }
+
+ // Initialize the validators map
+ validators := make(map[string]definition.Validator)
+ for key, validator := range validatorMap {
+ validators[key] = validator
+ }
+ fmt.Println("validators : ", validators)
+
+ return &Manager{vp: vp, validators: validators, cfg: cfg}, nil
+}
+
+// provider loads a plugin dynamically and retrieves its provider instance.
+func provider[T any](path string, id string) (T, error) {
+ var zero T
+ if len(strings.TrimSpace(id)) == 0 {
+ return zero, nil
+ }
+
+ p, err := plugin.Open(pluginPath(path, id))
+ if err != nil {
+ return zero, fmt.Errorf("failed to open plugin %s: %w", id, err)
+ }
+
+ symbol, err := p.Lookup("Provider")
+ if err != nil {
+ return zero, fmt.Errorf("failed to find Provider symbol in plugin %s: %w", id, err)
+ }
+
+ // Ensure the symbol is of the correct type
+ prov, ok := symbol.(*T)
+ if !ok {
+ return zero, fmt.Errorf("failed to cast Provider for %s", id)
+ }
+
+ return *prov, nil
+}
+
+// pluginPath constructs the path to the plugin shared object file.
+func pluginPath(path, id string) string {
+ return filepath.Join(path, id+".so")
+}
+
+// Validators retrieves the validation plugin instances.
+func (m *Manager) Validators(ctx context.Context) (map[string]definition.Validator, error) {
+ if m.vp == nil {
+ return nil, fmt.Errorf("validator plugin provider not loaded")
+ }
+
+ configMap := map[string]string{
+ "schema_dir": m.cfg.Plugins.ValidationPlugin.Schema.SchemaDir,
+ }
+ _, err := m.vp.New(ctx, configMap)
+ if err != nil {
+ return nil, fmt.Errorf("failed to initialize validator: %w", err)
+ }
+
+ return m.validators, nil
+}
+
+// LoadConfig loads the configuration from a YAML file.
+func LoadConfig(path string) (*Config, error) {
+ file, err := os.Open(path)
+ if err != nil {
+ return nil, fmt.Errorf("failed to open config file: %w", err)
+ }
+ defer file.Close()
+
+ var cfg Config
+ decoder := yaml.NewDecoder(file)
+ if err := decoder.Decode(&cfg); err != nil {
+ return nil, fmt.Errorf("failed to decode config file: %w", err)
+ }
+
+ return &cfg, nil
+}
diff --git a/shared/plugin/plugin.yaml b/shared/plugin/plugin.yaml
new file mode 100644
index 0000000..291b0ca
--- /dev/null
+++ b/shared/plugin/plugin.yaml
@@ -0,0 +1,6 @@
+plugins:
+ validation_plugin:
+ id: validator
+ config:
+ schema_dir: #Directory where the schema files are stored
+ plugin_path: shared/plugin/implementations/
\ No newline at end of file
diff --git a/plugins/testData/directory.json b/shared/plugin/testData/directory.json
similarity index 100%
rename from plugins/testData/directory.json
rename to shared/plugin/testData/directory.json
diff --git a/plugins/testData/payloads/cancel.json b/shared/plugin/testData/payloads/cancel.json
similarity index 100%
rename from plugins/testData/payloads/cancel.json
rename to shared/plugin/testData/payloads/cancel.json
diff --git a/plugins/testData/payloads/confirm.json b/shared/plugin/testData/payloads/confirm.json
similarity index 100%
rename from plugins/testData/payloads/confirm.json
rename to shared/plugin/testData/payloads/confirm.json
diff --git a/plugins/testData/payloads/search.json b/shared/plugin/testData/payloads/search.json
similarity index 100%
rename from plugins/testData/payloads/search.json
rename to shared/plugin/testData/payloads/search.json
diff --git a/plugins/testData/payloads/search_extraField.json b/shared/plugin/testData/payloads/search_extraField.json
similarity index 100%
rename from plugins/testData/payloads/search_extraField.json
rename to shared/plugin/testData/payloads/search_extraField.json
diff --git a/plugins/testData/payloads/search_missingField.json b/shared/plugin/testData/payloads/search_missingField.json
similarity index 100%
rename from plugins/testData/payloads/search_missingField.json
rename to shared/plugin/testData/payloads/search_missingField.json
diff --git a/plugins/testData/payloads/select.json b/shared/plugin/testData/payloads/select.json
similarity index 100%
rename from plugins/testData/payloads/select.json
rename to shared/plugin/testData/payloads/select.json
diff --git a/plugins/testData/schema_valid/ondc_trv10/v2.0.0/search.json b/shared/plugin/testData/schema_valid/ondc_trv10/v2.0.0/search.json
similarity index 100%
rename from plugins/testData/schema_valid/ondc_trv10/v2.0.0/search.json
rename to shared/plugin/testData/schema_valid/ondc_trv10/v2.0.0/search.json
diff --git a/test.go b/test.go
index 5f3eecd..c3d8cb3 100644
--- a/test.go
+++ b/test.go
@@ -1,34 +1,86 @@
package main
import (
- "beckn-onix/plugins"
+ "beckn-onix/shared/plugin"
+ "beckn-onix/shared/plugin/definition"
+ "context"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
- "strings"
)
-// Payload represents the structure of the data payload with context information.
+var (
+ manager *plugin.Manager
+ validators map[string]definition.Validator
+)
+
+// Payload represents the structure of the payload with context information.
type Payload struct {
Context struct {
- Domain string `json:"domain"`
- Version string `json:"version"`
+ Action string `json:"action"`
+ BapID string `json:"bap_id"`
+ BapURI string `json:"bap_uri"`
+ BppID string `json:"bpp_id"`
+ BppURI string `json:"bpp_uri"`
+ Domain string `json:"domain"`
+ Location struct {
+ City struct {
+ Code string `json:"code"`
+ } `json:"city"`
+ Country struct {
+ Code string `json:"code"`
+ } `json:"country"`
+ } `json:"location"`
+ MessageID string `json:"message_id"`
+ Timestamp string `json:"timestamp"`
+ TransactionID string `json:"transaction_id"`
+ TTL string `json:"ttl"`
+ Version string `json:"version"`
} `json:"context"`
+ Message struct {
+ CancellationReasonID string `json:"cancellation_reason_id"`
+ Descriptor struct {
+ Code string `json:"code"`
+ Name string `json:"name"`
+ } `json:"descriptor"`
+ OrderID string `json:"order_id"`
+ } `json:"message"`
}
func main() {
- http.HandleFunc("/", validateHandler)
+ var err error
+ // Load the configuration
+ config, err := plugin.LoadConfig("shared/plugin/plugin.yaml")
+ if err != nil {
+ log.Fatalf("Failed to load plugins configuration: %v", err)
+ }
+
+ // Initialize the plugin manager
+ manager, err = plugin.NewManager(context.Background(), config)
+ if err != nil {
+ log.Fatalf("Failed to create PluginManager: %v", err)
+ }
+
+ // Get the validators map
+ validators, err := manager.Validators(context.Background())
+ if err != nil {
+ log.Fatalf("Failed to get validators: %v", err)
+ }
+
+ http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+ validateHandler(w, r, validators)
+ })
fmt.Println("Starting server on port 8084...")
- err := http.ListenAndServe(":8084", nil)
+ err = http.ListenAndServe(":8084", nil)
if err != nil {
log.Fatalf("Server failed to start: %v", err)
}
}
-func validateHandler(w http.ResponseWriter, r *http.Request) {
+func validateHandler(w http.ResponseWriter, r *http.Request, validators map[string]definition.Validator) {
if r.Method != http.MethodPost {
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
return
@@ -47,39 +99,22 @@ func validateHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Failed to read payload data", http.StatusInternalServerError)
return
}
+ fmt.Println("printing payload data", string(payloadData))
- // Initialize an instance of Payload struct
var payload Payload
- err1 := json.Unmarshal(payloadData, &payload)
- if err1 != nil {
- http.Error(w, "Failed to parse JSON payload", http.StatusBadRequest)
- return
- }
-
- // Extract the domain, version, and endpoint from the payload and URL
- domain := payload.Context.Domain
- version := payload.Context.Version
- version = fmt.Sprintf("v%s", version)
-
- endpoint := strings.Trim(u.Path, "/")
- fmt.Println("Handling request for endpoint:", endpoint)
- domain = strings.ToLower(domain)
- domain = strings.ReplaceAll(domain, ":", "_")
-
- schemaFileName := fmt.Sprintf("%s/%s/%s.json", domain, version, endpoint)
-
- pluginsConfig, err := plugins.LoadPluginsConfig("plugins/config.yaml")
+ err = json.Unmarshal(payloadData, &payload)
if err != nil {
- http.Error(w, "Failed to load plugins configuration", http.StatusInternalServerError)
+ log.Printf("Failed to parse JSON payload: %v", err)
+ http.Error(w, fmt.Sprintf("Failed to parse JSON payload: %v", err), http.StatusBadRequest)
return
}
- debug := true
- _, validators, err := plugins.NewValidatorProvider(pluginsConfig, debug)
- if err != nil {
- http.Error(w, "Failed to create PluginManager", http.StatusInternalServerError)
+ // Validate that the domain and version fields are not empty
+ if payload.Context.Domain == "" || payload.Context.Version == "" {
+ http.Error(w, "Invalid payload: domain and version are required fields", http.StatusBadRequest)
return
}
+ schemaFileName := "ondc_trv10_v2.0.0_cancel"
validator, exists := validators[schemaFileName]
if !exists {
@@ -87,10 +122,12 @@ func validateHandler(w http.ResponseWriter, r *http.Request) {
return
}
- ctx := r.Context()
- err = validator.Validate(ctx, payloadData)
+ ctx := context.Background()
+ valid, err := validator.Validate(ctx, *u, payloadData)
if err != nil {
http.Error(w, fmt.Sprintf("Document validation failed: %v", err), http.StatusBadRequest)
+ } else if !valid {
+ http.Error(w, "Document validation failed", http.StatusBadRequest)
} else {
w.WriteHeader(http.StatusOK)
w.Write([]byte("Document validation succeeded!"))