From a9ffe29a6e0d330bd5db733aee84e1ab0d007cb6 Mon Sep 17 00:00:00 2001 From: tanyamadaan Date: Thu, 27 Mar 2025 11:28:45 +0530 Subject: [PATCH] updated router --- go.mod | 14 +- go.sum | 8 +- pkg/plugin/definition/router.go | 12 +- .../implementation/router/cmd/plugin.go | 2 +- .../implementation/router/cmd/plugin_test.go | 94 ++++---- pkg/plugin/implementation/router/router.go | 210 +++++++++-------- .../implementation/router/router_test.go | 216 ++++++++---------- .../router/testData/bap_caller.yaml | 6 +- .../router/testData/bap_receiver.yaml | 6 +- .../router/testData/bpp_caller.yaml | 4 +- .../router/testData/bpp_receiver.yaml | 8 +- pkg/plugin/manager.go | 24 +- 12 files changed, 282 insertions(+), 322 deletions(-) diff --git a/go.mod b/go.mod index c2692fb..8c19ef1 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,8 @@ module github.com/beckn/beckn-onix -go 1.23.4 +go 1.24 -toolchain go1.23.7 - -require ( - github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 - golang.org/x/crypto v0.36.0 -) +require golang.org/x/crypto v0.36.0 require ( github.com/kr/pretty v0.3.1 // indirect @@ -15,10 +10,7 @@ require ( gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) -require ( - github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03 - golang.org/x/text v0.23.0 // indirect -) +require github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03 require ( golang.org/x/sys v0.31.0 // indirect diff --git a/go.sum b/go.sum index 896f459..b4c0bb0 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,4 @@ 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/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= @@ -12,18 +10,14 @@ github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsK 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/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/go.mod h1:8sheVFH84v3PCyFY/O02mIgSQY9I6wMYPWsq7mDnEZY= golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= 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/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= \ No newline at end of file +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/plugin/definition/router.go b/pkg/plugin/definition/router.go index b9f7e41..399143d 100644 --- a/pkg/plugin/definition/router.go +++ b/pkg/plugin/definition/router.go @@ -5,19 +5,19 @@ import ( "net/url" ) -// Route defines the structure for the Route returned +// 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 + TargetType string // "url" or "msgq" + PublisherID string // For message queues + URL string // For API calls } -// RouterProvider initializes the a new Router instance with the given config +// 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 +// 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) diff --git a/pkg/plugin/implementation/router/cmd/plugin.go b/pkg/plugin/implementation/router/cmd/plugin.go index f38ffa9..23816ee 100644 --- a/pkg/plugin/implementation/router/cmd/plugin.go +++ b/pkg/plugin/implementation/router/cmd/plugin.go @@ -17,7 +17,7 @@ func (rp RouterProvider) New(ctx context.Context, config map[string]string) (def return nil, nil, errors.New("context cannot be nil") } - // Parse the routing_config key from the config map + // Parse the routingConfig key from the config map routingConfig, ok := config["routingConfig"] if !ok { return nil, nil, errors.New("routingConfig is required in the configuration") diff --git a/pkg/plugin/implementation/router/cmd/plugin_test.go b/pkg/plugin/implementation/router/cmd/plugin_test.go index afa0c97..08c9bd7 100644 --- a/pkg/plugin/implementation/router/cmd/plugin_test.go +++ b/pkg/plugin/implementation/router/cmd/plugin_test.go @@ -4,6 +4,7 @@ import ( "context" "os" "path/filepath" + "runtime" "strings" "testing" ) @@ -12,42 +13,23 @@ import ( func setupTestConfig(t *testing.T) string { t.Helper() - // Create a temporary directory for the routing rules - configDir, err := os.MkdirTemp("", "routingRules") + // Get project root (assuming testData is in project root) + _, filename, _, _ := runtime.Caller(0) // Path to plugin_test.go + projectRoot := filepath.Dir(filepath.Dir(filename)) // Move up from cmd/ + yamlPath := filepath.Join(projectRoot, "testData", "bap_receiver.yaml") + + // Copy to temp file (to test file loading logic) + tempDir := t.TempDir() + tempPath := filepath.Join(tempDir, "routingRules.yaml") + content, err := os.ReadFile(yamlPath) if err != nil { - t.Fatalf("Failed to create temp directory: %v", err) + t.Fatalf("Failed to read test file: %v", err) + } + if err := os.WriteFile(tempPath, content, 0644); err != nil { + t.Fatalf("Failed to create temp config: %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 + return tempPath } // TestRouterProviderSuccess tests the RouterProvider implementation for success cases. @@ -57,9 +39,10 @@ func TestRouterProviderSuccess(t *testing.T) { // Define test cases tests := []struct { - name string - ctx context.Context - config map[string]string + name string + ctx context.Context + config map[string]string + wantErr bool }{ { name: "Valid configuration", @@ -67,6 +50,7 @@ func TestRouterProviderSuccess(t *testing.T) { config: map[string]string{ "routingConfig": rulesFilePath, }, + wantErr: false, }, } @@ -76,14 +60,14 @@ func TestRouterProviderSuccess(t *testing.T) { router, _, err := provider.New(tt.ctx, tt.config) // Ensure no error occurred - if err != nil { - t.Errorf("unexpected error: %v", err) + if (err != nil) != tt.wantErr { + t.Errorf("New(%v, %v) error = %v, wantErr %v", tt.ctx, tt.config, err, tt.wantErr) return } // Ensure the router and close function are not nil if router == nil { - t.Error("expected a non-nil Router instance, got nil") + t.Errorf("New(%v, %v) = nil router, want non-nil", tt.ctx, tt.config) } }) } @@ -96,10 +80,10 @@ func TestRouterProviderFailure(t *testing.T) { // Define test cases tests := []struct { - name string - ctx context.Context - config map[string]string - expectedError string + name string + ctx context.Context + config map[string]string + wantErr string }{ { name: "Empty routing config path", @@ -107,19 +91,19 @@ func TestRouterProviderFailure(t *testing.T) { config: map[string]string{ "routingConfig": "", }, - expectedError: "failed to load routing rules: routingConfig path is empty", + wantErr: "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: "Missing routing config key", + ctx: context.Background(), + config: map[string]string{}, + wantErr: "routingConfig is required in the configuration", }, { - name: "Nil context", - ctx: nil, - config: map[string]string{"routingConfig": rulesFilePath}, - expectedError: "context cannot be nil", + name: "Nil context", + ctx: nil, + config: map[string]string{"routingConfig": rulesFilePath}, + wantErr: "context cannot be nil", }, } @@ -129,8 +113,10 @@ func TestRouterProviderFailure(t *testing.T) { _, _, 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) + if err == nil { + t.Errorf("New(%v, %v) = nil error, want error containing %q", tt.ctx, tt.config, tt.wantErr) + } else if !strings.Contains(err.Error(), tt.wantErr) { + t.Errorf("New(%v, %v) = %v, want error containing %q", tt.ctx, tt.config, err, tt.wantErr) } }) } diff --git a/pkg/plugin/implementation/router/router.go b/pkg/plugin/implementation/router/router.go index 2649dbd..ae98baf 100644 --- a/pkg/plugin/implementation/router/router.go +++ b/pkg/plugin/implementation/router/router.go @@ -24,27 +24,34 @@ type routingConfig struct { RoutingRules []routingRule `yaml:"routingRules"` } -// Router implements Router interface +// Router implements Router interface. type Router struct { - config *Config - rules []routingRule + rules map[string]map[string]map[string]*definition.Route // domain -> version -> endpoint -> route } // 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"` + Domain string `yaml:"domain"` + Version string `yaml:"version"` + TargetType string `yaml:"targetType"` // "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 + URL string `yaml:"url,omitempty"` // URL for "url" or gateway endpoint for "bpp"/"bap" + PublisherID string `yaml:"publisherId,omitempty"` // For "msgq" type } +// TargetType defines possible target destinations. +const ( + targetTypeURL = "url" // Route to a specific URL + targetTypeMSGQ = "msgq" // Route to a message queue + targetTypeBPP = "bpp" // Route to a BPP endpoint + targetTypeBAP = "bap" // Route to a BAP endpoint +) + // 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. @@ -54,24 +61,24 @@ func New(ctx context.Context, config *Config) (*Router, func() error, error) { return nil, nil, fmt.Errorf("config cannot be nil") } router := &Router{ - config: config, + rules: make(map[string]map[string]map[string]*definition.Route), } // Load rules at bootup - if err := router.loadRules(); err != nil { + if err := router.loadRules(config.RoutingConfig); 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 == "" { +func (r *Router) loadRules(configPath string) error { + if configPath == "" { return fmt.Errorf("routingConfig path is empty") } - data, err := os.ReadFile(r.config.RoutingConfig) + data, err := os.ReadFile(configPath) if err != nil { - return fmt.Errorf("error reading config file at %s: %w", r.config.RoutingConfig, err) + return fmt.Errorf("error reading config file at %s: %w", configPath, err) } var config routingConfig if err := yaml.Unmarshal(data, &config); err != nil { @@ -82,36 +89,74 @@ func (r *Router) loadRules() error { if err := validateRules(config.RoutingRules); err != nil { return fmt.Errorf("invalid routing rules: %w", err) } - r.rules = config.RoutingRules + // Build the optimized rule map + for _, rule := range config.RoutingRules { + // Initialize domain map if not exists + if _, ok := r.rules[rule.Domain]; !ok { + r.rules[rule.Domain] = make(map[string]map[string]*definition.Route) + } + + // Initialize version map if not exists + if _, ok := r.rules[rule.Domain][rule.Version]; !ok { + r.rules[rule.Domain][rule.Version] = make(map[string]*definition.Route) + } + + // Add all endpoints for this rule + for _, endpoint := range rule.Endpoints { + var route *definition.Route + switch rule.TargetType { + case targetTypeMSGQ: + route = &definition.Route{ + TargetType: rule.TargetType, + PublisherID: rule.Target.PublisherID, + } + case targetTypeURL: + route = &definition.Route{ + TargetType: rule.TargetType, + URL: rule.Target.URL, + } + case targetTypeBPP, targetTypeBAP: + route = &definition.Route{ + TargetType: rule.TargetType, + URL: rule.Target.URL, // Fallback URL if URI not provided in request + } + } + + fmt.Print(r.rules) + + r.rules[rule.Domain][rule.Version][endpoint] = route + } + } + 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") + // Ensure domain, version, and TargetType are present + if rule.Domain == "" || rule.Version == "" || rule.TargetType == "" { + return fmt.Errorf("invalid rule: domain, version, and targetType are required") } - // Validate based on routingType - switch rule.RoutingType { - case "url": + // Validate based on TargetType + switch rule.TargetType { + case targetTypeURL: if rule.Target.URL == "" { - return fmt.Errorf("invalid rule: url is required for routingType 'url'") + return fmt.Errorf("invalid rule: url is required for targetType '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 targetTypeMSGQ: + if rule.Target.PublisherID == "" { + return fmt.Errorf("invalid rule: publisherID is required for targetType 'msgq'") } - case "bpp", "bap": + case targetTypeBPP, targetTypeBAP: // 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 fmt.Errorf("invalid rule: unknown targetType '%s'", rule.TargetType) } } return nil @@ -125,8 +170,8 @@ func (r *Router) Route(ctx context.Context, url *url.URL, body []byte) (*definit Context struct { Domain string `json:"domain"` Version string `json:"version"` - BppURI string `json:"bpp_uri,omitempty"` - BapURI string `json:"bap_uri,omitempty"` + BPPURI string `json:"bpp_uri,omitempty"` + BAPURI string `json:"bap_uri,omitempty"` } `json:"context"` } if err := json.Unmarshal(body, &requestBody); err != nil { @@ -136,69 +181,52 @@ func (r *Router) Route(ctx context.Context, url *url.URL, body []byte) (*definit // 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) + // Lookup route in the optimized map + domainRules, ok := r.rules[requestBody.Context.Domain] + if !ok { + return nil, fmt.Errorf("no routing rules found for domain %s", requestBody.Context.Domain) } - // 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) - } - } + versionRules, ok := domainRules[requestBody.Context.Version] + if !ok { + return nil, fmt.Errorf("no routing rules found for domain %s version %s", requestBody.Context.Domain, requestBody.Context.Version) + } + + route, ok := versionRules[endpoint] + if !ok { + return nil, fmt.Errorf("endpoint '%s' is not supported for domain %s and version %s in routing config", + endpoint, requestBody.Context.Domain, requestBody.Context.Version) + } + + // Handle BPP/BAP routing with request URIs + switch route.TargetType { + case targetTypeBPP: + uri := strings.TrimSpace(requestBody.Context.BPPURI) + target := strings.TrimSpace(route.URL) + if len(uri) != 0 { + target = uri + } + if len(target) == 0 { + return nil, fmt.Errorf("could not determine destination for endpoint '%s': neither request contained a BPP URI nor was a default URL configured in routing rules", endpoint) + } + route = &definition.Route{ + TargetType: route.TargetType, + URL: target, + } + case targetTypeBAP: + uri := strings.TrimSpace(requestBody.Context.BAPURI) + target := strings.TrimSpace(route.URL) + if len(uri) != 0 { + target = uri + } + if len(target) == 0 { + return nil, fmt.Errorf("could not determine destination for endpoint '%s': neither request contained a BAP URI nor was a default URL configured in routing rules", endpoint) + } + route = &definition.Route{ + TargetType: route.TargetType, + URL: target, } } - // 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 + return route, nil } diff --git a/pkg/plugin/implementation/router/router_test.go b/pkg/plugin/implementation/router/router_test.go index 3d3fe70..f67b577 100644 --- a/pkg/plugin/implementation/router/router_test.go +++ b/pkg/plugin/implementation/router/router_test.go @@ -15,35 +15,19 @@ var testData embed.FS func setupTestConfig(t *testing.T, yamlFileName string) string { t.Helper() + configDir := t.TempDir() - // Create a temporary directory for the routing rules - configDir, err := os.MkdirTemp("", "routing_rules") + content, err := testData.ReadFile("testData/" + yamlFileName) if err != nil { - t.Fatalf("Failed to create temp directory: %v", err) + t.Fatalf("ReadFile() err = %v, want nil", 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) + rulesPath := filepath.Join(configDir, "routing_rules.yaml") + if err := os.WriteFile(rulesPath, content, 0644); err != nil { + t.Fatalf("WriteFile() err = %v, want nil", 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) + return rulesPath } // setupRouter is a helper function to create router instance. @@ -78,35 +62,35 @@ func TestNew(t *testing.T) { // Define test cases tests := []struct { - name string - config *Config - expectedError string + name string + config *Config + wantErr string }{ { name: "Valid configuration", config: &Config{ RoutingConfig: rulesFilePath, }, - expectedError: "", + wantErr: "", }, { - name: "Empty config", - config: nil, - expectedError: "config cannot be nil", + name: "Empty config", + config: nil, + wantErr: "config cannot be nil", }, { name: "Empty routing config path", config: &Config{ RoutingConfig: "", }, - expectedError: "routingConfig path is empty", + wantErr: "routingConfig path is empty", }, { name: "Routing config file does not exist", config: &Config{ RoutingConfig: "/nonexistent/path/to/rules.yaml", }, - expectedError: "error reading config file", + wantErr: "error reading config file", }, } @@ -115,22 +99,22 @@ func TestNew(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) + if tt.wantErr != "" { + if err == nil || !strings.Contains(err.Error(), tt.wantErr) { + t.Errorf("New(%v) = %v, want error containing %q", tt.config, err, tt.wantErr) } return } // Ensure no error occurred if err != nil { - t.Errorf("unexpected error: %v", err) + t.Errorf("New(%v) = %v, want nil error", tt.config, err) return } // Ensure the router and close function are not nil if router == nil { - t.Error("expected a non-nil Router instance, got nil") + t.Errorf("New(%v, %v) = nil router, want non-nil", ctx, tt.config) } }) } @@ -148,9 +132,9 @@ func TestValidateRulesSuccess(t *testing.T) { name: "Valid rules with url routing", rules: []routingRule{ { - Domain: "retail", - Version: "1.0.0", - RoutingType: "url", + Domain: "retail", + Version: "1.0.0", + TargetType: "url", Target: target{ URL: "https://example.com/api", }, @@ -162,11 +146,11 @@ func TestValidateRulesSuccess(t *testing.T) { name: "Valid rules with msgq routing", rules: []routingRule{ { - Domain: "retail", - Version: "1.0.0", - RoutingType: "msgq", + Domain: "retail", + Version: "1.0.0", + TargetType: "msgq", Target: target{ - TopicID: "example_topic", + PublisherID: "example_topic", }, Endpoints: []string{"on_search", "on_select"}, }, @@ -176,9 +160,9 @@ func TestValidateRulesSuccess(t *testing.T) { name: "Valid rules with bpp routing to gateway", rules: []routingRule{ { - Domain: "retail", - Version: "1.0.0", - RoutingType: "bpp", + Domain: "retail", + Version: "1.0.0", + TargetType: "bpp", Target: target{ URL: "https://mock_gateway.com/api", }, @@ -190,10 +174,10 @@ func TestValidateRulesSuccess(t *testing.T) { name: "Valid rules with bpp routing", rules: []routingRule{ { - Domain: "retail", - Version: "1.0.0", - RoutingType: "bpp", - Endpoints: []string{"select"}, + Domain: "retail", + Version: "1.0.0", + TargetType: "bpp", + Endpoints: []string{"select"}, }, }, }, @@ -201,10 +185,10 @@ func TestValidateRulesSuccess(t *testing.T) { name: "Valid rules with bap routing", rules: []routingRule{ { - Domain: "retail", - Version: "1.0.0", - RoutingType: "bap", - Endpoints: []string{"select"}, + Domain: "retail", + Version: "1.0.0", + TargetType: "bap", + Endpoints: []string{"select"}, }, }, }, @@ -214,7 +198,7 @@ func TestValidateRulesSuccess(t *testing.T) { t.Run(tt.name, func(t *testing.T) { err := validateRules(tt.rules) if err != nil { - t.Errorf("unexpected error: %v", err) + t.Errorf("validateRules(%v) = %v, want nil error", tt.rules, err) } }) } @@ -223,40 +207,40 @@ func TestValidateRulesSuccess(t *testing.T) { // TestValidateRulesFailure tests the validate function for failure cases. func TestValidateRulesFailure(t *testing.T) { tests := []struct { - name string - rules []routingRule - expectedErr string + name string + rules []routingRule + wantErr string }{ { name: "Missing domain", rules: []routingRule{ { - Version: "1.0.0", - RoutingType: "url", + Version: "1.0.0", + TargetType: "url", Target: target{ URL: "https://example.com/api", }, Endpoints: []string{"search", "select"}, }, }, - expectedErr: "invalid rule: domain, version, and routingType are required", + wantErr: "invalid rule: domain, version, and targetType are required", }, { name: "Missing version", rules: []routingRule{ { - Domain: "retail", - RoutingType: "url", + Domain: "retail", + TargetType: "url", Target: target{ URL: "https://example.com/api", }, Endpoints: []string{"search", "select"}, }, }, - expectedErr: "invalid rule: domain, version, and routingType are required", + wantErr: "invalid rule: domain, version, and targetType are required", }, { - name: "Missing routingType", + name: "Missing targetType", rules: []routingRule{ { Domain: "retail", @@ -267,62 +251,60 @@ func TestValidateRulesFailure(t *testing.T) { Endpoints: []string{"search", "select"}, }, }, - expectedErr: "invalid rule: domain, version, and routingType are required", + wantErr: "invalid rule: domain, version, and targetType are required", }, { - name: "Invalid routingType", + name: "Invalid targetType", rules: []routingRule{ { - Domain: "retail", - Version: "1.0.0", - RoutingType: "invalid", + Domain: "retail", + Version: "1.0.0", + TargetType: "invalid", Target: target{ URL: "https://example.com/api", }, Endpoints: []string{"search", "select"}, }, }, - expectedErr: "invalid rule: unknown routingType 'invalid'", + wantErr: "invalid rule: unknown targetType 'invalid'", }, { - name: "Missing url for routingType: url", + name: "Missing url for targetType: url", rules: []routingRule{ { - Domain: "retail", - Version: "1.0.0", - RoutingType: "url", - Target: target{ + Domain: "retail", + Version: "1.0.0", + TargetType: "url", + Target: target{ // URL is missing }, Endpoints: []string{"search", "select"}, }, }, - expectedErr: "invalid rule: url is required for routingType 'url'", + wantErr: "invalid rule: url is required for targetType 'url'", }, { - name: "Missing topic_id for routingType: msgq", + name: "Missing topic_id for targetType: msgq", rules: []routingRule{ { - Domain: "retail", - Version: "1.0.0", - RoutingType: "msgq", - Target: target{ - // TopicID is missing + Domain: "retail", + Version: "1.0.0", + TargetType: "msgq", + Target: target{ + // PublisherID is missing }, Endpoints: []string{"search", "select"}, }, }, - expectedErr: "invalid rule: topicId is required for routingType 'msgq'", + wantErr: "invalid rule: publisherID is required for targetType '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) + if err == nil || !strings.Contains(err.Error(), tt.wantErr) { + t.Errorf("validateRules(%v) = %v, want error containing %q", tt.rules, err, tt.wantErr) } }) } @@ -387,7 +369,7 @@ func TestRouteSuccess(t *testing.T) { // Ensure no error occurred if err != nil { - t.Errorf("unexpected error: %v", err) + t.Errorf("router.Route(%v, %v, %v) = %v, want nil error", ctx, parsedURL, []byte(tt.body), err) } }) } @@ -399,39 +381,39 @@ func TestRouteFailure(t *testing.T) { // Define failure test cases tests := []struct { - name string - configFile string - url string - body string - expectedError string + name string + configFile string + url string + body string + wantErr 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: "Unsupported endpoint", + configFile: "bpp_receiver.yaml", + url: "https://example.com/v1/ondc/unsupported", + body: `{"context": {"domain": "ONDC:TRV11", "version": "2.0.0"}}`, + wantErr: "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: "No matching rule", + configFile: "bpp_receiver.yaml", + url: "https://example.com/v1/ondc/select", + body: `{"context": {"domain": "ONDC:SRV11", "version": "2.0.0"}}`, + wantErr: "no routing rules found for domain ONDC:SRV11", }, { - 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 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"}}`, + wantErr: "could not determine destination for endpoint 'on_search': neither request contained a BAP URI nor was a default URL configured in routing rules", }, { - 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", + 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"}}`, + wantErr: "could not determine destination for endpoint 'select': neither request contained a BPP URI nor was a default URL configured in routing rules", }, } @@ -444,8 +426,8 @@ func TestRouteFailure(t *testing.T) { _, 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) + if err == nil || !strings.Contains(err.Error(), tt.wantErr) { + t.Errorf("Route(%q, %q) = %v, want error containing %q", tt.url, tt.body, err, tt.wantErr) } }) } diff --git a/pkg/plugin/implementation/router/testData/bap_caller.yaml b/pkg/plugin/implementation/router/testData/bap_caller.yaml index b1d5a44..0c595a6 100644 --- a/pkg/plugin/implementation/router/testData/bap_caller.yaml +++ b/pkg/plugin/implementation/router/testData/bap_caller.yaml @@ -1,14 +1,14 @@ routingRules: - domain: "ONDC:TRV10" version: "2.0.0" - routingType: "bpp" + targetType: "bpp" target: url: "https://gateway.example.com" endpoints: - search - domain: "ONDC:TRV10" version: "2.0.0" - routingType: "bpp" + targetType: "bpp" endpoints: - select - init @@ -17,7 +17,7 @@ routingRules: - cancel - domain: "ONDC:TRV12" version: "2.0.0" - routingType: "bpp" + targetType: "bpp" endpoints: - select - init diff --git a/pkg/plugin/implementation/router/testData/bap_receiver.yaml b/pkg/plugin/implementation/router/testData/bap_receiver.yaml index ca4a478..353ecc7 100644 --- a/pkg/plugin/implementation/router/testData/bap_receiver.yaml +++ b/pkg/plugin/implementation/router/testData/bap_receiver.yaml @@ -1,7 +1,7 @@ routingRules: - domain: "ONDC:TRV10" version: "2.0.0" - routingType: "url" + targetType: "url" target: url: "https://services-backend/trv/v1" endpoints: @@ -13,8 +13,8 @@ routingRules: - on_cancel - domain: "ONDC:TRV10" version: "2.0.0" - routingType: "msgq" + targetType: "msgq" target: - topic_id: "trv_topic_id1" + publisherId: "trv_topic_id1" endpoints: - on_search \ No newline at end of file diff --git a/pkg/plugin/implementation/router/testData/bpp_caller.yaml b/pkg/plugin/implementation/router/testData/bpp_caller.yaml index 0d9a670..8d0c212 100644 --- a/pkg/plugin/implementation/router/testData/bpp_caller.yaml +++ b/pkg/plugin/implementation/router/testData/bpp_caller.yaml @@ -1,7 +1,7 @@ routingRules: - domain: "ONDC:TRV10" version: "2.0.0" - routingType: "bap" + targetType: "bap" endpoints: - on_search - on_select @@ -12,7 +12,7 @@ routingRules: - on_cancel - domain: "ONDC:TRV11" version: "2.0.0" - routingType: "bap" + targetType: "bap" endpoints: - on_search - on_select diff --git a/pkg/plugin/implementation/router/testData/bpp_receiver.yaml b/pkg/plugin/implementation/router/testData/bpp_receiver.yaml index 6febce6..9633923 100644 --- a/pkg/plugin/implementation/router/testData/bpp_receiver.yaml +++ b/pkg/plugin/implementation/router/testData/bpp_receiver.yaml @@ -1,7 +1,7 @@ routingRules: - domain: "ONDC:TRV10" version: "2.0.0" - routingType: "url" + targetType: "url" target: url: "https://services-backend/trv/v1" endpoints: @@ -13,15 +13,15 @@ routingRules: - domain: "ONDC:TRV10" version: "2.0.0" - routingType: "msgq" + targetType: "msgq" target: - topic_id: "trv_topic_id1" + publisherId: "trv_topic_id1" endpoints: - search - domain: "ONDC:TRV11" version: "2.0.0" - routingType: "url" + targetType: "url" target: url: "https://services-backend/trv/v1" endpoints: diff --git a/pkg/plugin/manager.go b/pkg/plugin/manager.go index 6a4cecd..86a0b02 100644 --- a/pkg/plugin/manager.go +++ b/pkg/plugin/manager.go @@ -18,7 +18,6 @@ type Config struct { Decrypter PluginConfig `yaml:"decrypter"` Encrypter PluginConfig `yaml:"encrypter"` Publisher PluginConfig `yaml:"publisher"` - Router PluginConfig `yaml:"router"` } // PluginConfig represents configuration details for a plugin. @@ -34,7 +33,6 @@ type Manager struct { dp definition.DecrypterProvider ep definition.EncrypterProvider pb definition.PublisherProvider - rp definition.RouterProvider cfg *Config } @@ -74,13 +72,7 @@ func NewManager(ctx context.Context, cfg *Config) (*Manager, error) { return nil, fmt.Errorf("failed to load encryption plugin: %w", err) } - // Load router plugin. - rp, err := provider[definition.RouterProvider](cfg.Root, cfg.Router.ID) - if err != nil { - return nil, fmt.Errorf("failed to load encryption plugin: %w", err) - } - - return &Manager{sp: sp, vp: vp, pb: pb, ep: ep, dp: dp, rp: rp, cfg: cfg}, nil + return &Manager{sp: sp, vp: vp, pb: pb, ep: ep, dp: dp, cfg: cfg}, nil } // provider loads a plugin dynamically and retrieves its provider instance. @@ -177,17 +169,3 @@ func (m *Manager) Publisher(ctx context.Context) (definition.Publisher, error) { } return publisher, nil } - -// Router retrieves the router plugin instances. -func (m *Manager) Router(ctx context.Context) (definition.Router, func() error, error) { - if m.rp == nil { - return nil, nil, fmt.Errorf("router plugin provider not loaded") - - } - schemaValidator, close, err := m.rp.New(ctx, m.cfg.Router.Config) - if err != nil { - - return nil, nil, fmt.Errorf("failed to initialize schema validator: %v", err) - } - return schemaValidator, close, nil -}