small fixes

This commit is contained in:
tanyamadaan
2025-03-22 00:05:08 +05:30
parent 423adb4e00
commit fe1ffa356c
16 changed files with 35 additions and 1016 deletions

15
go.mod
View File

@@ -1,26 +1,15 @@
module github.com/beckn/beckn-onix module github.com/beckn/beckn-onix
go 1.23.4 go 1.24.1
toolchain go1.23.7
require ( require (
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 github.com/santhosh-tekuri/jsonschema/v6 v6.0.1
golang.org/x/crypto v0.36.0 golang.org/x/crypto v0.36.0
) )
require (
github.com/kr/pretty v0.3.1 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
)
require ( require (
github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03 github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03
golang.org/x/text v0.23.0 // indirect golang.org/x/text v0.23.0 // indirect
) )
require ( require golang.org/x/sys v0.31.0 // indirect
golang.org/x/sys v0.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1
)

17
go.sum
View File

@@ -1,17 +1,5 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= 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/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw= 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/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03 h1:m1h+vudopHsI67FPT9MOncyndWhTcdUoBtI1R1uajGY= github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03 h1:m1h+vudopHsI67FPT9MOncyndWhTcdUoBtI1R1uajGY=
@@ -22,8 +10,3 @@ golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1,24 +0,0 @@
package definition
import (
"context"
"net/url"
)
// Route defines the structure for the Route returned
type Route struct {
RoutingType string // "url" or "msgq"
TopicID string // For message queues
TargetURL string // For API calls
}
// RouterProvider initializes the a new Router instance with the given config
type RouterProvider interface {
New(ctx context.Context, config map[string]string) (Router, func() error, error)
}
// Router defines the interface for routing requests
type Router interface {
// Route determines the routing destination based on the request context.
Route(ctx context.Context, url *url.URL, body []byte) (*Route, error)
}

View File

@@ -1,31 +0,0 @@
package main
import (
"context"
"errors"
definition "github.com/beckn/beckn-onix/pkg/plugin/definition"
router "github.com/beckn/beckn-onix/pkg/plugin/implementation/router"
)
// RouterProvider provides instances of Router.
type RouterProvider struct{}
// New initializes a new Router instance.
func (rp RouterProvider) New(ctx context.Context, config map[string]string) (definition.Router, func() error, error) {
if ctx == nil {
return nil, nil, errors.New("context cannot be nil")
}
// Parse the routing_config key from the config map
routingConfig, ok := config["routingConfig"]
if !ok {
return nil, nil, errors.New("routingConfig is required in the configuration")
}
return router.New(ctx, &router.Config{
RoutingConfig: routingConfig,
})
}
// Provider is the exported symbol that the plugin manager will look for.
var Provider definition.RouterProvider = RouterProvider{}

View File

@@ -1,137 +0,0 @@
package main
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
)
// setupTestConfig creates a temporary directory and writes a sample routing rules file.
func setupTestConfig(t *testing.T) string {
t.Helper()
// Create a temporary directory for the routing rules
configDir, err := os.MkdirTemp("", "routingRules")
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
// Define sample routing rules
rulesContent := `
routingRules:
- domain: "ONDC:TRV11"
version: "2.0.0"
routingType: "url"
target:
url: "https://services-backend/trv/v1"
endpoints:
- select
- init
- confirm
- status
- domain: "ONDC:TRV11"
version: "2.0.0"
routingType: "msgq"
target:
topic_id: "trv_topic_id1"
endpoints:
- search
`
// Write the routing rules to a file
rulesFilePath := filepath.Join(configDir, "routingRules.yaml")
if err := os.WriteFile(rulesFilePath, []byte(rulesContent), 0644); err != nil {
t.Fatalf("Failed to write routing rules file: %v", err)
}
return rulesFilePath
}
// TestRouterProviderSuccess tests the RouterProvider implementation for success cases.
func TestRouterProviderSuccess(t *testing.T) {
rulesFilePath := setupTestConfig(t)
defer os.RemoveAll(filepath.Dir(rulesFilePath))
// Define test cases
tests := []struct {
name string
ctx context.Context
config map[string]string
}{
{
name: "Valid configuration",
ctx: context.Background(),
config: map[string]string{
"routingConfig": rulesFilePath,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
provider := RouterProvider{}
router, _, err := provider.New(tt.ctx, tt.config)
// Ensure no error occurred
if err != nil {
t.Errorf("unexpected error: %v", err)
return
}
// Ensure the router and close function are not nil
if router == nil {
t.Error("expected a non-nil Router instance, got nil")
}
})
}
}
// TestRouterProviderFailure tests the RouterProvider implementation for failure cases.
func TestRouterProviderFailure(t *testing.T) {
rulesFilePath := setupTestConfig(t)
defer os.RemoveAll(filepath.Dir(rulesFilePath))
// Define test cases
tests := []struct {
name string
ctx context.Context
config map[string]string
expectedError string
}{
{
name: "Empty routing config path",
ctx: context.Background(),
config: map[string]string{
"routingConfig": "",
},
expectedError: "failed to load routing rules: routingConfig path is empty",
},
{
name: "Missing routing config key",
ctx: context.Background(),
config: map[string]string{},
expectedError: "routingConfig is required in the configuration",
},
{
name: "Nil context",
ctx: nil,
config: map[string]string{"routingConfig": rulesFilePath},
expectedError: "context cannot be nil",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
provider := RouterProvider{}
_, _, err := provider.New(tt.ctx, tt.config)
// Check for expected error
if err == nil || !strings.Contains(err.Error(), tt.expectedError) {
t.Errorf("expected error %q, got %v", tt.expectedError, err)
}
})
}
}

View File

@@ -1,204 +0,0 @@
package router
import (
"context"
"encoding/json"
"fmt"
"net/url"
"os"
"path"
"strings"
definition "github.com/beckn/beckn-onix/pkg/plugin/definition"
"gopkg.in/yaml.v3"
)
// Config holds the configuration for the Router plugin.
type Config struct {
RoutingConfig string `json:"routingConfig"`
}
// RoutingConfig represents the structure of the routing configuration file.
type routingConfig struct {
RoutingRules []routingRule `yaml:"routingRules"`
}
// Router implements Router interface
type Router struct {
config *Config
rules []routingRule
}
// RoutingRule represents a single routing rule.
type routingRule struct {
Domain string `yaml:"domain"`
Version string `yaml:"version"`
RoutingType string `yaml:"routingType"` // "url", "msgq", "bpp", or "bap"
Target target `yaml:"target,omitempty"`
Endpoints []string `yaml:"endpoints"`
}
// Target contains destination-specific details.
type target struct {
URL string `yaml:"url,omitempty"` // URL for "url" or gateway endpoint for "bpp"/"bap"
TopicID string `yaml:"topic_id,omitempty"` // For "msgq" type
}
// New initializes a new Router instance with the provided configuration.
// It loads and validates the routing rules from the specified YAML file.
// Returns an error if the configuration is invalid or the rules cannot be loaded.
func New(ctx context.Context, config *Config) (*Router, func() error, error) {
// Check if config is nil
if config == nil {
return nil, nil, fmt.Errorf("config cannot be nil")
}
router := &Router{
config: config,
}
// Load rules at bootup
if err := router.loadRules(); err != nil {
return nil, nil, fmt.Errorf("failed to load routing rules: %w", err)
}
return router, nil, nil
}
// LoadRules reads and parses routing rules from the YAML configuration file.
func (r *Router) loadRules() error {
if r.config.RoutingConfig == "" {
return fmt.Errorf("routingConfig path is empty")
}
data, err := os.ReadFile(r.config.RoutingConfig)
if err != nil {
return fmt.Errorf("error reading config file at %s: %w", r.config.RoutingConfig, err)
}
var config routingConfig
if err := yaml.Unmarshal(data, &config); err != nil {
return fmt.Errorf("error parsing YAML: %w", err)
}
// Validate rules
if err := validateRules(config.RoutingRules); err != nil {
return fmt.Errorf("invalid routing rules: %w", err)
}
r.rules = config.RoutingRules
return nil
}
// validateRules performs basic validation on the loaded routing rules.
func validateRules(rules []routingRule) error {
for _, rule := range rules {
// Ensure domain, version, and routingType are present
if rule.Domain == "" || rule.Version == "" || rule.RoutingType == "" {
return fmt.Errorf("invalid rule: domain, version, and routingType are required")
}
// Validate based on routingType
switch rule.RoutingType {
case "url":
if rule.Target.URL == "" {
return fmt.Errorf("invalid rule: url is required for routingType 'url'")
}
if _, err := url.ParseRequestURI(rule.Target.URL); err != nil {
return fmt.Errorf("invalid URL in rule: %w", err)
}
case "msgq":
if rule.Target.TopicID == "" {
return fmt.Errorf("invalid rule: topicId is required for routingType 'msgq'")
}
case "bpp", "bap":
// No target validation needed for bpp/bap, as they use URIs from the request body
continue
default:
return fmt.Errorf("invalid rule: unknown routingType '%s'", rule.RoutingType)
}
}
return nil
}
// Route determines the routing destination based on the request context.
func (r *Router) Route(ctx context.Context, url *url.URL, body []byte) (*definition.Route, error) {
// Parse the body to extract domain and version
var requestBody struct {
Context struct {
Domain string `json:"domain"`
Version string `json:"version"`
BppURI string `json:"bpp_uri,omitempty"`
BapURI string `json:"bap_uri,omitempty"`
} `json:"context"`
}
if err := json.Unmarshal(body, &requestBody); err != nil {
return nil, fmt.Errorf("error parsing request body: %w", err)
}
// Extract the endpoint from the URL
endpoint := path.Base(url.Path)
// Collect all matching rules for the domain and version
matchingRules := r.getMatchingRules(requestBody.Context.Domain, requestBody.Context.Version)
// If no matching rules are found, return an error
if len(matchingRules) == 0 {
return nil, fmt.Errorf("no matching routing rule found for domain %s and version %s", requestBody.Context.Domain, requestBody.Context.Version)
}
// Match the rule
for _, rule := range matchingRules {
for _, ep := range rule.Endpoints {
if strings.EqualFold(ep, endpoint) {
switch rule.RoutingType {
case "msgq":
return &definition.Route{
RoutingType: rule.RoutingType,
TopicID: rule.Target.TopicID,
}, nil
case "url":
return &definition.Route{
RoutingType: rule.RoutingType,
TargetURL: rule.Target.URL,
}, nil
case "bpp":
return handleRouting(rule, requestBody.Context.BppURI, endpoint, "bpp")
case "bap":
return handleRouting(rule, requestBody.Context.BapURI, endpoint, "bap")
default:
return nil, fmt.Errorf("unsupported routingType: %s", rule.RoutingType)
}
}
}
}
// If domain and version match but endpoint is not found, return an error
return nil, fmt.Errorf("endpoint '%s' is not supported for domain %s and version %s", endpoint, requestBody.Context.Domain, requestBody.Context.Version)
}
// getMatchingRules returns all rules that match the given domain and version
func (r *Router) getMatchingRules(domain, version string) []routingRule {
var matchingRules []routingRule
for _, rule := range r.rules {
if rule.Domain == domain && rule.Version == version {
matchingRules = append(matchingRules, rule)
}
}
return matchingRules
}
// handleRouting handles routing for bap and bpp routing type
func handleRouting(rule routingRule, uri, endpoint string, routingType string) (*definition.Route, error) {
if uri == "" {
if rule.Target.URL != "" {
return &definition.Route{
RoutingType: routingType,
TargetURL: rule.Target.URL,
}, nil
} else {
return nil, fmt.Errorf("no target URI or URL found for %s routing type and %s endpoint", routingType, endpoint)
}
}
return &definition.Route{
RoutingType: routingType,
TargetURL: uri,
}, nil
}

View File

@@ -1,452 +0,0 @@
package router
import (
"context"
"embed"
"net/url"
"os"
"path/filepath"
"strings"
"testing"
)
//go:embed testData/*
var testData embed.FS
func setupTestConfig(t *testing.T, yamlFileName string) string {
t.Helper()
// Create a temporary directory for the routing rules
configDir, err := os.MkdirTemp("", "routing_rules")
if err != nil {
t.Fatalf("Failed to create temp directory: %v", err)
}
// Read the YAML file content
yamlContent := readYAMLFile(t, yamlFileName)
// Write the routing rules to a file
rulesFilePath := filepath.Join(configDir, "routing_rules.yaml")
if err := os.WriteFile(rulesFilePath, []byte(yamlContent), 0644); err != nil {
t.Fatalf("Failed to write routing rules file: %v", err)
}
return rulesFilePath
}
func readYAMLFile(t *testing.T, fileName string) string {
t.Helper()
// Read the YAML file
content, err := testData.ReadFile("testData/" + fileName)
if err != nil {
t.Fatalf("Failed to read YAML file: %v", err)
}
return string(content)
}
// setupRouter is a helper function to create router instance.
func setupRouter(t *testing.T, configFile string) (*Router, func() error, string) {
rulesFilePath := setupTestConfig(t, configFile)
config := &Config{
RoutingConfig: rulesFilePath,
}
router, _, err := New(context.Background(), config)
if err != nil {
t.Fatalf("New failed: %v", err)
}
return router, nil, rulesFilePath
}
// TestNew tests the New function.
func TestNew(t *testing.T) {
ctx := context.Background()
// List of YAML files in the testData directory
yamlFiles := []string{
"bap_caller.yaml",
"bap_receiver.yaml",
"bpp_caller.yaml",
"bpp_receiver.yaml",
}
for _, yamlFile := range yamlFiles {
t.Run(yamlFile, func(t *testing.T) {
rulesFilePath := setupTestConfig(t, yamlFile)
defer os.RemoveAll(filepath.Dir(rulesFilePath))
// Define test cases
tests := []struct {
name string
config *Config
expectedError string
}{
{
name: "Valid configuration",
config: &Config{
RoutingConfig: rulesFilePath,
},
expectedError: "",
},
{
name: "Empty config",
config: nil,
expectedError: "config cannot be nil",
},
{
name: "Empty routing config path",
config: &Config{
RoutingConfig: "",
},
expectedError: "routingConfig path is empty",
},
{
name: "Routing config file does not exist",
config: &Config{
RoutingConfig: "/nonexistent/path/to/rules.yaml",
},
expectedError: "error reading config file",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
router, _, err := New(ctx, tt.config)
// Check for expected error
if tt.expectedError != "" {
if err == nil || !strings.Contains(err.Error(), tt.expectedError) {
t.Errorf("expected error %q, got %v", tt.expectedError, err)
}
return
}
// Ensure no error occurred
if err != nil {
t.Errorf("unexpected error: %v", err)
return
}
// Ensure the router and close function are not nil
if router == nil {
t.Error("expected a non-nil Router instance, got nil")
}
})
}
})
}
}
// TestValidateRulesSuccess tests the validate function for success cases.
func TestValidateRulesSuccess(t *testing.T) {
tests := []struct {
name string
rules []routingRule
}{
{
name: "Valid rules with url routing",
rules: []routingRule{
{
Domain: "retail",
Version: "1.0.0",
RoutingType: "url",
Target: target{
URL: "https://example.com/api",
},
Endpoints: []string{"on_search", "on_select"},
},
},
},
{
name: "Valid rules with msgq routing",
rules: []routingRule{
{
Domain: "retail",
Version: "1.0.0",
RoutingType: "msgq",
Target: target{
TopicID: "example_topic",
},
Endpoints: []string{"on_search", "on_select"},
},
},
},
{
name: "Valid rules with bpp routing to gateway",
rules: []routingRule{
{
Domain: "retail",
Version: "1.0.0",
RoutingType: "bpp",
Target: target{
URL: "https://mock_gateway.com/api",
},
Endpoints: []string{"search"},
},
},
},
{
name: "Valid rules with bpp routing",
rules: []routingRule{
{
Domain: "retail",
Version: "1.0.0",
RoutingType: "bpp",
Endpoints: []string{"select"},
},
},
},
{
name: "Valid rules with bap routing",
rules: []routingRule{
{
Domain: "retail",
Version: "1.0.0",
RoutingType: "bap",
Endpoints: []string{"select"},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateRules(tt.rules)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
})
}
}
// TestValidateRulesFailure tests the validate function for failure cases.
func TestValidateRulesFailure(t *testing.T) {
tests := []struct {
name string
rules []routingRule
expectedErr string
}{
{
name: "Missing domain",
rules: []routingRule{
{
Version: "1.0.0",
RoutingType: "url",
Target: target{
URL: "https://example.com/api",
},
Endpoints: []string{"search", "select"},
},
},
expectedErr: "invalid rule: domain, version, and routingType are required",
},
{
name: "Missing version",
rules: []routingRule{
{
Domain: "retail",
RoutingType: "url",
Target: target{
URL: "https://example.com/api",
},
Endpoints: []string{"search", "select"},
},
},
expectedErr: "invalid rule: domain, version, and routingType are required",
},
{
name: "Missing routingType",
rules: []routingRule{
{
Domain: "retail",
Version: "1.0.0",
Target: target{
URL: "https://example.com/api",
},
Endpoints: []string{"search", "select"},
},
},
expectedErr: "invalid rule: domain, version, and routingType are required",
},
{
name: "Invalid routingType",
rules: []routingRule{
{
Domain: "retail",
Version: "1.0.0",
RoutingType: "invalid",
Target: target{
URL: "https://example.com/api",
},
Endpoints: []string{"search", "select"},
},
},
expectedErr: "invalid rule: unknown routingType 'invalid'",
},
{
name: "Missing url for routingType: url",
rules: []routingRule{
{
Domain: "retail",
Version: "1.0.0",
RoutingType: "url",
Target: target{
// URL is missing
},
Endpoints: []string{"search", "select"},
},
},
expectedErr: "invalid rule: url is required for routingType 'url'",
},
{
name: "Missing topic_id for routingType: msgq",
rules: []routingRule{
{
Domain: "retail",
Version: "1.0.0",
RoutingType: "msgq",
Target: target{
// TopicID is missing
},
Endpoints: []string{"search", "select"},
},
},
expectedErr: "invalid rule: topicId is required for routingType 'msgq'",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateRules(tt.rules)
if err == nil {
t.Errorf("expected error: %v, got nil", tt.expectedErr)
} else if err.Error() != tt.expectedErr {
t.Errorf("expected error: %v, got: %v", tt.expectedErr, err)
}
})
}
}
// TestRouteSuccess tests the Route function for success cases.
func TestRouteSuccess(t *testing.T) {
ctx := context.Background()
// Define success test cases
tests := []struct {
name string
configFile string
url string
body string
}{
{
name: "Valid domain, version, and endpoint (bpp routing with gateway URL)",
configFile: "bap_caller.yaml",
url: "https://example.com/v1/ondc/search",
body: `{"context": {"domain": "ONDC:TRV10", "version": "2.0.0"}}`,
},
{
name: "Valid domain, version, and endpoint (bpp routing with bpp_uri)",
configFile: "bap_caller.yaml",
url: "https://example.com/v1/ondc/select",
body: `{"context": {"domain": "ONDC:TRV10", "version": "2.0.0", "bpp_uri": "https://bpp1.example.com"}}`,
},
{
name: "Valid domain, version, and endpoint (url routing)",
configFile: "bpp_receiver.yaml",
url: "https://example.com/v1/ondc/select",
body: `{"context": {"domain": "ONDC:TRV10", "version": "2.0.0"}}`,
},
{
name: "Valid domain, version, and endpoint (msgq routing)",
configFile: "bpp_receiver.yaml",
url: "https://example.com/v1/ondc/search",
body: `{"context": {"domain": "ONDC:TRV10", "version": "2.0.0"}}`,
},
{
name: "Valid domain, version, and endpoint (bap routing with bap_uri)",
configFile: "bpp_caller.yaml",
url: "https://example.com/v1/ondc/on_select",
body: `{"context": {"domain": "ONDC:TRV10", "version": "2.0.0", "bap_uri": "https://bap1.example.com"}}`,
},
{
name: "Valid domain, version, and endpoint (bpp routing with bpp_uri)",
configFile: "bap_receiver.yaml",
url: "https://example.com/v1/ondc/on_select",
body: `{"context": {"domain": "ONDC:TRV10", "version": "2.0.0", "bpp_uri": "https://bpp1.example.com"}}`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
router, _, rulesFilePath := setupRouter(t, tt.configFile)
defer os.RemoveAll(filepath.Dir(rulesFilePath))
parsedURL, _ := url.Parse(tt.url)
_, err := router.Route(ctx, parsedURL, []byte(tt.body))
// Ensure no error occurred
if err != nil {
t.Errorf("unexpected error: %v", err)
}
})
}
}
// TestRouteFailure tests the Route function for failure cases.
func TestRouteFailure(t *testing.T) {
ctx := context.Background()
// Define failure test cases
tests := []struct {
name string
configFile string
url string
body string
expectedError string
}{
{
name: "Unsupported endpoint",
configFile: "bpp_receiver.yaml",
url: "https://example.com/v1/ondc/unsupported",
body: `{"context": {"domain": "ONDC:TRV11", "version": "2.0.0"}}`,
expectedError: "endpoint 'unsupported' is not supported for domain ONDC:TRV11 and version 2.0.0",
},
{
name: "No matching rule",
configFile: "bpp_receiver.yaml",
url: "https://example.com/v1/ondc/select",
body: `{"context": {"domain": "ONDC:SRV11", "version": "2.0.0"}}`,
expectedError: "no matching routing rule found for domain ONDC:SRV11 and version 2.0.0",
},
{
name: "Missing bap_uri for bap routing",
configFile: "bpp_caller.yaml",
url: "https://example.com/v1/ondc/on_search",
body: `{"context": {"domain": "ONDC:TRV10", "version": "2.0.0"}}`,
expectedError: "no target URI or URL found for bap routing type and on_search endpoint",
},
{
name: "Missing bpp_uri for bpp routing",
configFile: "bap_caller.yaml",
url: "https://example.com/v1/ondc/select",
body: `{"context": {"domain": "ONDC:TRV10", "version": "2.0.0"}}`,
expectedError: "no target URI or URL found for bpp routing type and select endpoint",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
router, _, rulesFilePath := setupRouter(t, tt.configFile)
defer os.RemoveAll(filepath.Dir(rulesFilePath))
parsedURL, _ := url.Parse(tt.url)
_, err := router.Route(ctx, parsedURL, []byte(tt.body))
// Check for expected error
if err == nil || !strings.Contains(err.Error(), tt.expectedError) {
t.Errorf("expected error %q, got %v", tt.expectedError, err)
}
})
}
}

View File

@@ -1,25 +0,0 @@
routingRules:
- domain: "ONDC:TRV10"
version: "2.0.0"
routingType: "bpp"
target:
url: "https://gateway.example.com"
endpoints:
- search
- domain: "ONDC:TRV10"
version: "2.0.0"
routingType: "bpp"
endpoints:
- select
- init
- confirm
- status
- cancel
- domain: "ONDC:TRV12"
version: "2.0.0"
routingType: "bpp"
endpoints:
- select
- init
- confirm
- status

View File

@@ -1,20 +0,0 @@
routingRules:
- domain: "ONDC:TRV10"
version: "2.0.0"
routingType: "url"
target:
url: "https://services-backend/trv/v1"
endpoints:
- on_select
- on_init
- on_confirm
- on_status
- on_update
- on_cancel
- domain: "ONDC:TRV10"
version: "2.0.0"
routingType: "msgq"
target:
topic_id: "trv_topic_id1"
endpoints:
- on_search

View File

@@ -1,23 +0,0 @@
routingRules:
- domain: "ONDC:TRV10"
version: "2.0.0"
routingType: "bap"
endpoints:
- on_search
- on_select
- on_init
- on_confirm
- on_status
- on_update
- on_cancel
- domain: "ONDC:TRV11"
version: "2.0.0"
routingType: "bap"
endpoints:
- on_search
- on_select
- on_init
- on_confirm
- on_status
- on_update
- on_cancel

View File

@@ -1,30 +0,0 @@
routingRules:
- domain: "ONDC:TRV10"
version: "2.0.0"
routingType: "url"
target:
url: "https://services-backend/trv/v1"
endpoints:
- select
- init
- confirm
- status
- cancel
- domain: "ONDC:TRV10"
version: "2.0.0"
routingType: "msgq"
target:
topic_id: "trv_topic_id1"
endpoints:
- search
- domain: "ONDC:TRV11"
version: "2.0.0"
routingType: "url"
target:
url: "https://services-backend/trv/v1"
endpoints:
- select
- init
- confirm

View File

@@ -17,10 +17,10 @@ func (vp schemaValidatorProvider) New(ctx context.Context, config map[string]str
return nil, nil, errors.New("context cannot be nil") return nil, nil, errors.New("context cannot be nil")
} }
// Extract schema_dir from the config map // Extract schemaDir from the config map
schemaDir, ok := config["schema_dir"] schemaDir, ok := config["schemaDir"]
if !ok || schemaDir == "" { if !ok || schemaDir == "" {
return nil, nil, errors.New("config must contain 'schema_dir'") return nil, nil, errors.New("config must contain 'schemaDir'")
} }
// Create a new schemaValidator instance with the provided configuration // Create a new schemaValidator instance with the provided configuration

View File

@@ -63,7 +63,7 @@ func TestValidatorProviderSuccess(t *testing.T) {
{ {
name: "Valid schema directory", name: "Valid schema directory",
ctx: context.Background(), // Valid context ctx: context.Background(), // Valid context
config: map[string]string{"schema_dir": schemaDir}, config: map[string]string{"schemaDir": schemaDir},
expectedError: "", expectedError: "",
}, },
} }
@@ -114,24 +114,24 @@ func TestValidatorProviderFailure(t *testing.T) {
name: "Config is empty", name: "Config is empty",
ctx: context.Background(), ctx: context.Background(),
config: map[string]string{}, config: map[string]string{},
expectedError: "config must contain 'schema_dir'", expectedError: "config must contain 'schemaDir'",
}, },
{ {
name: "schema_dir is empty", name: "schemaDir is empty",
ctx: context.Background(), ctx: context.Background(),
config: map[string]string{"schema_dir": ""}, config: map[string]string{"schemaDir": ""},
expectedError: "config must contain 'schema_dir'", expectedError: "config must contain 'schemaDir'",
}, },
{ {
name: "Invalid schema directory", name: "Invalid schema directory",
ctx: context.Background(), // Valid context ctx: context.Background(), // Valid context
config: map[string]string{"schema_dir": "/invalid/dir"}, config: map[string]string{"schemaDir": "/invalid/dir"},
expectedError: "failed to initialise schemaValidator: schema directory does not exist: /invalid/dir", expectedError: "failed to initialise schemaValidator: schema directory does not exist: /invalid/dir",
}, },
{ {
name: "Nil context", name: "Nil context",
ctx: nil, // Nil context ctx: nil, // Nil context
config: map[string]string{"schema_dir": schemaDir}, config: map[string]string{"schemaDir": schemaDir},
expectedError: "context cannot be nil", expectedError: "context cannot be nil",
}, },
} }

View File

@@ -49,7 +49,7 @@ func New(ctx context.Context, config *Config) (*SchemaValidator, func() error, e
if err := v.initialise(); err != nil { if err := v.initialise(); err != nil {
return nil, nil, fmt.Errorf("failed to initialise schemaValidator: %v", err) return nil, nil, fmt.Errorf("failed to initialise schemaValidator: %v", err)
} }
return v, v.Close, nil return v, nil, nil
} }
// Validate validates the given data against the schema. // Validate validates the given data against the schema.
@@ -195,8 +195,3 @@ func (v *SchemaValidator) initialise() error {
return nil return nil
} }
// Close releases resources (mock implementation returning nil).
func (v *SchemaValidator) Close() error {
return nil
}

View File

@@ -289,7 +289,7 @@ func TestValidator_Initialise(t *testing.T) {
} }
} }
func TestValidator_New_Success(t *testing.T) { func TestValidatorNew_Success(t *testing.T) {
schemaDir := setupTestSchema(t) schemaDir := setupTestSchema(t)
defer os.RemoveAll(schemaDir) defer os.RemoveAll(schemaDir)
@@ -300,7 +300,7 @@ func TestValidator_New_Success(t *testing.T) {
} }
} }
func TestValidator_New_Failure(t *testing.T) { func TestValidatorNewFailure(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
config *Config config *Config
@@ -315,22 +315,6 @@ func TestValidator_New_Failure(t *testing.T) {
}, },
wantErr: "config cannot be nil", wantErr: "config cannot be nil",
}, },
// {
// name: "Config is empty",
// config: &Config{},
// setupFunc: func(schemaDir string) error {
// return nil
// },
// wantErr: "config must contain 'schema_dir'",
// },
// {
// name: "schema_dir is empty",
// config: &Config{SchemaDir: ""},
// setupFunc: func(schemaDir string) error {
// return nil
// },
// wantErr: "config must contain 'schema_dir'",
// },
{ {
name: "Failed to initialise validators", name: "Failed to initialise validators",
config: &Config{ config: &Config{

View File

@@ -27,11 +27,6 @@ type PluginConfig struct {
Config map[string]string `yaml:"config"` Config map[string]string `yaml:"config"`
} }
// SchemaDetails contains information about the plugin schema directory.
type SchemaDetails struct {
SchemaDir string `yaml:"schemaDir"`
}
// Manager handles dynamic plugin loading and management. // Manager handles dynamic plugin loading and management.
type Manager struct { type Manager struct {
sp definition.SignerProvider sp definition.SignerProvider
@@ -79,7 +74,13 @@ func NewManager(ctx context.Context, cfg *Config) (*Manager, error) {
return nil, fmt.Errorf("failed to load encryption plugin: %w", err) return nil, fmt.Errorf("failed to load encryption plugin: %w", err)
} }
return &Manager{sp: sp, vp: vp, pb: pb, ep: ep, dp: dp, cfg: cfg}, nil // Load schema validation plugin.
svp, err := provider[definition.SchemaValidatorProvider](cfg.Root, cfg.Encrypter.ID)
if err != nil {
return nil, fmt.Errorf("failed to load schema validation plugin: %w", err)
}
return &Manager{sp: sp, vp: vp, pb: pb, ep: ep, dp: dp, svp: svp, cfg: cfg}, nil
} }
// provider loads a plugin dynamically and retrieves its provider instance. // provider loads a plugin dynamically and retrieves its provider instance.
@@ -176,3 +177,16 @@ func (m *Manager) Publisher(ctx context.Context) (definition.Publisher, error) {
} }
return publisher, nil return publisher, nil
} }
// Encrypter retrieves the encryption plugin instance.
func (m *Manager) SchemaValidation(ctx context.Context) (definition.SchemaValidator, func() error, error) {
if m.svp == nil {
return nil, nil, fmt.Errorf("schema validation plugin provider not loaded")
}
schemaValidator, close, err := m.svp.New(ctx, m.cfg.SchemaValidator.Config)
if err != nil {
return nil, nil, fmt.Errorf("failed to initialize schema validator: %w", err)
}
return schemaValidator, close, nil
}