updated code.

1. Resolved merge conflicts.
2. Resolved go linting issues.
This commit is contained in:
MohitKatare-protean
2025-03-28 22:45:58 +05:30
parent 87bc86e1d6
commit 7055c1c0d8
24 changed files with 117 additions and 376 deletions

View File

@@ -25,12 +25,12 @@ type Config struct {
Log log.Config `yaml:"log"`
PluginManager *plugin.ManagerConfig `yaml:"pluginManager"`
Modules []module.Config `yaml:"modules"`
HTTP timeouts `yaml:"http"` // Nest http config
HTTP timeouts `yaml:"http"`
}
type timeouts struct {
Port string `yaml:"port"`
Timeout timeoutConfig `yaml:"timeout"`
Port string `yaml:"port"`
Timeouts timeoutConfig `yaml:"timeout"`
}
type timeoutConfig struct {
@@ -138,9 +138,9 @@ func run(ctx context.Context, configPath string) error {
httpServer := &http.Server{
Addr: net.JoinHostPort("", cfg.HTTP.Port),
Handler: srv,
ReadTimeout: cfg.HTTP.Timeout.Read * time.Second, // Use timeouts from config
WriteTimeout: cfg.HTTP.Timeout.Write * time.Second,
IdleTimeout: cfg.HTTP.Timeout.Idle * time.Second,
ReadTimeout: cfg.HTTP.Timeouts.Read * time.Second,
WriteTimeout: cfg.HTTP.Timeouts.Write * time.Second,
IdleTimeout: cfg.HTTP.Timeouts.Idle * time.Second,
}
// Start HTTP server.

View File

@@ -366,7 +366,7 @@ func TestNewServerSuccess(t *testing.T) {
Modules: tt.modules,
HTTP: timeouts{
Port: "8080",
Timeout: timeoutConfig{
Timeouts: timeoutConfig{
Read: 5,
Write: 5,
Idle: 10,
@@ -411,7 +411,7 @@ func TestNewServerFailure(t *testing.T) {
Modules: tt.modules,
HTTP: timeouts{
Port: "8080",
Timeout: timeoutConfig{
Timeouts: timeoutConfig{
Read: 5,
Write: 5,
Idle: 10,

View File

@@ -1,69 +0,0 @@
appName: "bapAdapter"
log:
level: debug
destinations:
- type: stdout
context_keys:
- transaction_id
- message_id
http:
port: 8080
timeout:
read: 30
write: 30
idle: 30
plugin:
root: /app/plugins
pluginZipPath: /mnt/gcs/plugins/plugins_bundle.zip
plugins:
- publisher Src version raw comp zip
- nopschemavalidator
- router
- nopsigner
- nopsignvalidator
- reqpreprocessor
- gcpAuthMdw
modules:
- name: reciever
type: transaction
path: /reciever
targetType: msgQ
plugin:
schemaValidator:
id: nopschemavalidator
signValidator:
id: nopsignvalidator
publisher:
id: publisher
config:
project: ondc-seller-dev
topic: bapNetworkReciever
router:
id: router
config:
routingConfigPath: /mnt/gcs/configs/bapRecieverRouting-config.yaml
preProcessors:
- id: reqpreprocessor
steps:
steps:
- addRoute
signValidate
-addRout
customValidate
- name: transactionCaller
path: /caller
targetType: "http"
plugin:
signer:
id: nopsigner
router:
id: router
config:
routingConfigPath: /mnt/gcs/configs/bapCallerRouting-config.yaml
preProcessors:
- id: reqpreprocessor
# postProcessors:
# - id: gcpAuthMdw
# config:
# audience: https://bpp-adapter-903496459467.asia-southeast1.run.app
# serviceAccount: 903496459467-compute@developer.gserviceaccount.com

View File

@@ -1,3 +0,0 @@
routes:
- action: search
target: https://bpp-adapter-903496459467.asia-southeast1.run.app/reciever

View File

@@ -1,63 +0,0 @@
appName: "bppClientService"
log:
level: debug
destinations:
- type: stdout
context_keys:
- transaction_id
- message_id
http:
port: 8080
timeout:
read: 30
write: 30
idle: 30
plugin:
root: extracted/plugins
pluginZipPath: plugins_bundle.zip
plugins:
- publisher
- nopschemavalidator
- router
- nopsigner
- nopsignvalidator
- reqpreprocessor
- gcpAuthMdw
module:
modules:
- name: transactionReciever
path: /reciever
targetType: msgQ
plugin:
schemaValidator:
id: nopschemavalidator
signValidator:
id: nopsignValidator
publisher:
id: publisher
config:
project: ondc-seller-dev
topic: clientSideTopic
Router:
id: router
config:
routingConfigPath: configs/bppRecieverRouting-config.yaml
preProcessors:
- id: reqpreprocessor
- name: transactionCaller
path: /caller
targetType: "http"
plugin:
signer:
id: nopsigner
Router:
id: router
config:
routingConfigPath: configs/bppCallerRouting-config.yaml
preProcessors:
- id: reqpreprocessor
postProcessors:
- id: gcpAuthMdw
config:
audience: "target"

View File

@@ -1,63 +0,0 @@
appName: "bppClientService"
log:
level: debug
destinations:
- type: stdout
context_keys:
- transaction_id
- message_id
http:
port: 8080
timeout:
read: 30
write: 30
idle: 30
plugin:
root: /app/plugins
pluginZipPath: /mnt/gcs/plugins/plugins_bundle.zip
plugins:
- publisher
- nopschemavalidator
- router
- nopsigner
- nopsignvalidator
- reqpreprocessor
- gcpAuthMdw
module:
modules:
- name: transactionReciever
path: /reciever
targetType: msgQ
plugin:
schemaValidator:
id: nopschemavalidator
signValidator:
id: nopsignvalidator
publisher:
id: publisher
config:
project: ondc-seller-dev
topic: bppNetworkReciever
router:
id: router
config:
routingConfigPath: /mnt/gcs/configs/bppRecieverRouting-config.yaml
preProcessors:
- id: reqpreprocessor
- name: transactionCaller
path: /caller
targetType: "http"
plugin:
signer:
id: nopsigner
router:
id: router
config:
routingConfigPath: /mnt/gcs/configs/bppCallerRouting-config.yaml
preProcessors:
- id: reqpreprocessor
# postProcessors:
# - id: gcpAuthMdw
# config:
# audience: https://bap-adapter-903496459467.asia-southeast1.run.app
# serviceAccount: 903496459467-compute@developer.gserviceaccount.com

View File

@@ -1,3 +0,0 @@
routes:
- action: on_search
target: targeturl

View File

@@ -1,3 +0,0 @@
routes:
- action: search
target: https://sellerapp-903496459467.asia-southeast1.run.app

View File

@@ -1,2 +0,0 @@
bap_url: "https://bap-csr-903496459467.asia-southeast1.run.app" # Replace with actual Beckn API endpoint
port: "8080" # The port on which the server will run

View File

@@ -1,69 +0,0 @@
- id: pooja-stores
descriptor:
name: Pooja Stores
locations:
- id: koramangala-4th-block-location
gps: "12.9349377,77.6055586"
categories:
- id: fresh_fruits
descriptor:
name: Fresh Fruits
- id: beverages
descriptor:
name: Beverages
items:
- id: item_2
descriptor:
name: Green Apples Organic
images:
- url: "https://mock_bpp.com/images/item_2.jpg"
category_id: fresh_fruits
location_id: koramangala-4th-block-location
price:
currency: INR
value: "170"
matched: true
- id: item_1
descriptor:
name: Red Apples
images:
- url: "https://mock_bpp.com/images/item_1.jpg"
category_id: fresh_fruits
location_id: koramangala-4th-block-location
price:
currency: INR
value: "90"
related: true
- id: item_7
descriptor:
name: Green Apple Juice
images:
- url: "https://mock_bpp.com/images/item_7.jpg"
category_id: beverages
location_id: koramangala-4th-block-location
price:
currency: INR
value: "70"
matched: true
- id: food-mall
descriptor:
name: Food Mall
locations:
- id: food-mall-location
gps: "12.9349377,77.6055586"
categories:
- id: fresh-food
descriptor:
name: Fresh food
items:
- id: item_1_1
descriptor:
name: Green Apple Salad
images:
- url: "https://mock_bpp.com/images/item_1_1.jpg"
category_id: fresh-food
location_id: food-mall-location
price:
currency: INR
value: "200"
matched: true

View File

@@ -24,7 +24,7 @@ type Config struct {
// RegisteryClient encapsulates the logic for calling the subscribe and lookup endpoints.
type RegisteryClient struct {
Config *Config
Client *retryablehttp.Client // Retryable HTTP Client
Client *retryablehttp.Client
}
// NewRegisteryClient creates a new instance of Client.

View File

@@ -2,7 +2,6 @@ package handler
import (
"context"
"fmt"
"net/http"
"github.com/beckn/beckn-onix/pkg/model"
@@ -10,7 +9,7 @@ import (
"github.com/beckn/beckn-onix/pkg/plugin/definition"
)
// PluginManager defines the methods required for managing plugins in stdHandler.
// PluginManager defines an interface for managing plugins dynamically.
type PluginManager interface {
Middleware(ctx context.Context, cfg *plugin.Config) (func(http.Handler) http.Handler, error)
SignValidator(ctx context.Context, cfg *plugin.Config) (definition.Verifier, error)
@@ -53,45 +52,4 @@ type Config struct {
RegistryURL string `yaml:"registryUrl"`
Role model.Role
SubscriberID string `yaml:"subscriberId"`
Trace map[string]bool
}
// Step represents a named processing step.
type Step string
const (
// StepInitialize represents the initialization phase of the request processing pipeline.
StepInitialize Step = "initialize"
// StepValidate represents the validation phase, where input data is checked for correctness.
StepValidate Step = "validate"
// StepProcess represents the core processing phase of the request.
StepProcess Step = "process"
// StepFinalize represents the finalization phase, where the response is prepared and sent.
StepFinalize Step = "finalize"
)
// validSteps ensures only allowed step values are used.
var validSteps = map[Step]bool{
StepInitialize: true,
StepValidate: true,
StepProcess: true,
StepFinalize: true,
}
// UnmarshalYAML customizes YAML unmarshalling for Step to enforce valid values.
func (s *Step) UnmarshalYAML(unmarshal func(interface{}) error) error {
var stepName string
if err := unmarshal(&stepName); err != nil {
return err
}
step := Step(stepName)
if !validSteps[step] {
return fmt.Errorf("invalid step: %s", stepName)
}
*s = step
return nil
}

View File

@@ -38,11 +38,11 @@ func NewStdHandler(ctx context.Context, mgr PluginManager, cfg *Config) (http.Ha
SubscriberID: cfg.SubscriberID,
role: cfg.Role,
}
// Initialize plugins
// Initialize plugins.
if err := h.initPlugins(ctx, mgr, &cfg.Plugins, cfg.RegistryURL); err != nil {
return nil, fmt.Errorf("failed to initialize plugins: %w", err)
}
// Initialize steps
// Initialize steps.
if err := h.initSteps(ctx, mgr, cfg); err != nil {
return nil, fmt.Errorf("failed to initialize steps: %w", err)
}
@@ -59,7 +59,7 @@ func (h *stdHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
log.Request(r.Context(), r, ctx.Body)
// Execute processing steps
// Execute processing steps.
for _, step := range h.steps {
if err := step.Run(ctx); err != nil {
log.Errorf(ctx, err, "%T.run(%v):%v", step, ctx, err)
@@ -67,14 +67,14 @@ func (h *stdHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
}
// Restore request body before forwarding or publishing
// Restore request body before forwarding or publishing.
r.Body = io.NopCloser(bytes.NewReader(ctx.Body))
if ctx.Route == nil {
response.SendAck(w)
return
}
// Handle routing based on the defined route type
// Handle routing based on the defined route type.
route(ctx, r, w, h.publisher)
}

View File

@@ -1,7 +1,6 @@
package handler
import (
"context"
"fmt"
"strings"
"time"
@@ -74,17 +73,17 @@ func (s *validateSignStep) Run(ctx *model.StepContext) error {
if len(headerValue) != 0 {
if err := s.validate(ctx, headerValue); err != nil {
ctx.RespHeader.Set(model.UnaAuthorizedHeaderGateway, unauthHeader)
return model.NewSignValidationErrf("failed to validate %s: %w", model.AuthHeaderGateway, err)
return model.NewSignValidationErr(fmt.Errorf("failed to validate %s: %w", model.AuthHeaderGateway, err))
}
}
headerValue = ctx.Request.Header.Get(model.AuthHeaderSubscriber)
if len(headerValue) == 0 {
ctx.RespHeader.Set(model.UnaAuthorizedHeaderSubscriber, unauthHeader)
return model.NewSignValidationErrf("%s missing", model.UnaAuthorizedHeaderSubscriber)
return model.NewSignValidationErr(fmt.Errorf("%s missing", model.UnaAuthorizedHeaderSubscriber))
}
if err := s.validate(ctx, headerValue); err != nil {
ctx.RespHeader.Set(model.UnaAuthorizedHeaderSubscriber, unauthHeader)
return model.NewSignValidationErrf("failed to validate %s: %w", model.AuthHeaderSubscriber, err)
return model.NewSignValidationErr(fmt.Errorf("failed to validate %s: %w", model.AuthHeaderSubscriber, err))
}
return nil
}
@@ -113,15 +112,6 @@ type validateSchemaStep struct {
validator definition.SchemaValidator
}
// newValidateSchemaStep creates and returns the validateSchema step after validation
func newValidateSchemaStep(schemaValidator definition.SchemaValidator) (definition.Step, error) {
if schemaValidator == nil {
return nil, fmt.Errorf("invalid config: SchemaValidator plugin not configured")
}
log.Debug(context.Background(), "adding schema validator")
return &validateSchemaStep{validator: schemaValidator}, nil
}
// Run executes the schema validation step.
func (s *validateSchemaStep) Run(ctx *model.StepContext) error {
if err := s.validator.Validate(ctx, ctx.Request.URL, ctx.Body); err != nil {
@@ -135,14 +125,6 @@ type addRouteStep struct {
router definition.Router
}
// newRouteStep initializes and returns a new routing step.
func newRouteStep(router definition.Router) (definition.Step, error) {
if router == nil {
return nil, fmt.Errorf("invalid config: Router plugin not configured")
}
return &addRouteStep{router: router}, nil
}
// Run executes the routing step.
func (s *addRouteStep) Run(ctx *model.StepContext) error {
route, err := s.router.Route(ctx, ctx.Request.URL, ctx.Body)
@@ -150,8 +132,5 @@ func (s *addRouteStep) Run(ctx *model.StepContext) error {
return fmt.Errorf("failed to determine route: %w", err)
}
log.Debugf(ctx, "Routing to %#v", route)
ctx.Route = route
log.Debugf(ctx, "ctx.Route to %#v", ctx.Route)
return nil
}

8
go.mod
View File

@@ -27,9 +27,15 @@ require (
golang.org/x/text v0.23.0 // indirect
)
require golang.org/x/sys v0.31.0 // indirect
require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
golang.org/x/sys v0.31.0 // indirect
)
require (
github.com/hashicorp/go-retryablehttp v0.7.7
github.com/rs/zerolog v1.34.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v2 v2.4.0
)

13
go.sum
View File

@@ -1,3 +1,4 @@
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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=
@@ -5,6 +6,7 @@ github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxK
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
@@ -22,14 +24,20 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/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/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
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/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
@@ -40,6 +48,9 @@ github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03 h1:m1h+vudopHsI67F
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.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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=
@@ -47,6 +58,8 @@ 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/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
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=

View File

@@ -25,11 +25,13 @@ type destination struct {
Config map[string]string `yaml:"config"`
}
// Destination types for logging output.
const (
Stdout destinationType = "stdout"
File destinationType = "file"
)
// Log levels define the severity of log messages.
const (
DebugLevel level = "debug"
InfoLevel level = "info"
@@ -48,6 +50,7 @@ var logLevels = map[level]zerolog.Level{
PanicLevel: zerolog.PanicLevel,
}
// Config represents the configuration for logging.
type Config struct {
Level level `yaml:"level"`
Destinations []destination `yaml:"destinations"`
@@ -60,6 +63,7 @@ var (
once sync.Once
)
// Logger instance and configuration.
var (
ErrInvalidLogLevel = errors.New("invalid log level")
ErrLogDestinationNil = errors.New("log Destinations cant be empty")
@@ -163,6 +167,8 @@ func getLogger(config Config) (zerolog.Logger, error) {
return newLogger, nil
}
// InitLogger initializes the logger with the given configuration.
// It ensures that the logger is initialized only once using sync.Once.
func InitLogger(c Config) error {
var initErr error
once.Do(func() {
@@ -175,60 +181,74 @@ func InitLogger(c Config) error {
return initErr
}
// Debug logs a debug-level message with the provided context.
func Debug(ctx context.Context, msg string) {
logEvent(ctx, zerolog.DebugLevel, msg, nil)
}
// Debugf logs a formatted debug-level message with the provided context.
func Debugf(ctx context.Context, format string, v ...any) {
msg := fmt.Sprintf(format, v...)
logEvent(ctx, zerolog.DebugLevel, msg, nil)
}
// Info logs an info-level message with the provided context.
func Info(ctx context.Context, msg string) {
logEvent(ctx, zerolog.InfoLevel, msg, nil)
}
// Infof logs a formatted info-level message with the provided context.
func Infof(ctx context.Context, format string, v ...any) {
msg := fmt.Sprintf(format, v...)
logEvent(ctx, zerolog.InfoLevel, msg, nil)
}
// Warn logs a warning-level message with the provided context.
func Warn(ctx context.Context, msg string) {
logEvent(ctx, zerolog.WarnLevel, msg, nil)
}
// Warnf logs a formatted warning-level message with the provided context.
func Warnf(ctx context.Context, format string, v ...any) {
msg := fmt.Sprintf(format, v...)
logEvent(ctx, zerolog.WarnLevel, msg, nil)
}
// Error logs an error-level message along with an error object.
func Error(ctx context.Context, err error, msg string) {
logEvent(ctx, zerolog.ErrorLevel, msg, err)
}
// Errorf logs a formatted error-level message along with an error object.
func Errorf(ctx context.Context, err error, format string, v ...any) {
msg := fmt.Sprintf(format, v...)
logEvent(ctx, zerolog.ErrorLevel, msg, err)
}
// Fatal logs a fatal-level message along with an error object and exits the application.
func Fatal(ctx context.Context, err error, msg string) {
logEvent(ctx, zerolog.FatalLevel, msg, err)
}
// Fatalf logs a formatted fatal-level message along with an error object and exits the application.
func Fatalf(ctx context.Context, err error, format string, v ...any) {
msg := fmt.Sprintf(format, v...)
logEvent(ctx, zerolog.FatalLevel, msg, err)
}
// Panic logs a panic-level message along with an error object and panics.
func Panic(ctx context.Context, err error, msg string) {
logEvent(ctx, zerolog.PanicLevel, msg, err)
}
// Panicf logs a formatted panic-level message along with an error object and panics.
func Panicf(ctx context.Context, err error, format string, v ...any) {
msg := fmt.Sprintf(format, v...)
logEvent(ctx, zerolog.PanicLevel, msg, err)
}
// logEvent logs an event at the specified log level with an optional error message.
// It adds contextual information before logging the message.
func logEvent(ctx context.Context, level zerolog.Level, msg string, err error) {
event := logger.WithLevel(level)
@@ -239,6 +259,7 @@ func logEvent(ctx context.Context, level zerolog.Level, msg string, err error) {
event.Msg(msg)
}
// Request logs details of an incoming HTTP request, including method, URL, body, and remote address.
func Request(ctx context.Context, r *http.Request, body []byte) {
event := logger.Info()
addCtx(ctx, event)
@@ -249,6 +270,7 @@ func Request(ctx context.Context, r *http.Request, body []byte) {
Msg("HTTP Request")
}
// addCtx adds context values to the log event based on configured context keys.
func addCtx(ctx context.Context, event *zerolog.Event) {
for _, key := range cfg.ContextKeys {
val, ok := ctx.Value(key).(string)
@@ -260,6 +282,7 @@ func addCtx(ctx context.Context, event *zerolog.Event) {
}
}
// Response logs details of an outgoing HTTP response, including method, URL, status code, and response time.
func Response(ctx context.Context, r *http.Request, statusCode int, responseTime time.Duration) {
event := logger.Info()
addCtx(ctx, event)

View File

@@ -40,14 +40,20 @@ const (
type contextKey string
// MsgIDKey is the context key used to store and retrieve the message ID in a request context.
const MsgIDKey = contextKey("message_id")
// Role defines the type of participant in the network.
type Role string
const (
RoleBAP Role = "bap"
RoleBPP Role = "bpp"
RoleGateway Role = "gateway"
// RoleBAP represents a Buyer App Participant (BAP) in the network.
RoleBAP Role = "bap"
// RoleBPP represents a Buyer Platform Participant (BPP) in the network.
RoleBPP Role = "bpp"
// RoleGateway represents a Gateway that facilitates communication in the network.
RoleGateway Role = "gateway"
// RoleRegistery represents the Registry that maintains network participant details.
RoleRegistery Role = "registery"
)
@@ -91,22 +97,32 @@ type StepContext struct {
RespHeader http.Header
}
// WithContext updates the existing StepContext with a new context.
func (ctx *StepContext) WithContext(newCtx context.Context) {
ctx.Context = newCtx
}
// Status represents the acknowledgment status in a response.
type Status string
const (
StatusACK Status = "ACK"
// StatusACK indicates a successful acknowledgment.
StatusACK Status = "ACK"
// StatusNACK indicates a negative acknowledgment or failure.
StatusNACK Status = "NACK"
)
// Ack represents an acknowledgment response.
type Ack struct {
// Status holds the acknowledgment status (ACK/NACK).
Status Status `json:"status"`
}
// Message represents the structure of a response message.
type Message struct {
Ack Ack `json:"ack"`
// Ack contains the acknowledgment status.
Ack Ack `json:"ack"`
// Error holds error details, if any, in the response.
Error *Error `json:"error,omitempty"`
}

View File

@@ -5,7 +5,7 @@ import (
"errors"
definition "github.com/beckn/beckn-onix/pkg/plugin/definition"
schemaValidator "github.com/beckn/beckn-onix/pkg/plugin/implementation/schemaValidator"
schemaValidator "github.com/beckn/beckn-onix/pkg/plugin/implementation/schemavalidator"
)
// schemaValidatorProvider provides instances of schemaValidator.

View File

@@ -1,4 +1,4 @@
package schemaValidator
package schemavalidator
import (
"context"

View File

@@ -1,4 +1,4 @@
package schemaValidator
package schemavalidator
import (
"context"

View File

@@ -149,8 +149,15 @@ func (m *Manager) Router(ctx context.Context, cfg *Config) (definition.Router, e
if err != nil {
return nil, fmt.Errorf("failed to load provider for %s: %w", cfg.ID, err)
}
return rp.New(ctx, cfg.Config)
router, closer, err := rp.New(ctx, cfg.Config)
if closer != nil {
m.addCloser(func() {
if err := closer(); err != nil {
panic(err)
}
})
}
return router, nil
}
func (m *Manager) Middleware(ctx context.Context, cfg *Config) (func(http.Handler) http.Handler, error) {

View File

@@ -12,10 +12,16 @@ import (
"github.com/beckn/beckn-onix/pkg/model"
)
// Error represents a standardized error response used across the system.
type Error struct {
Code string `json:"code,omitempty"`
// Code is a short, machine-readable error code.
Code string `json:"code,omitempty"`
// Message provides a human-readable description of the error.
Message string `json:"message,omitempty"`
Paths string `json:"paths,omitempty"`
// Paths indicates the specific field(s) or endpoint(s) related to the error.
Paths string `json:"paths,omitempty"`
}
// SchemaValidationErr represents a collection of schema validation failures.
@@ -32,13 +38,18 @@ func (e *SchemaValidationErr) Error() string {
return strings.Join(errorMessages, "; ")
}
// Message represents a standard message structure with acknowledgment and error information.
type Message struct {
// Ack contains the acknowledgment status of the response.
Ack struct {
Status string `json:"status,omitempty"`
} `json:"ack,omitempty"`
// Error holds error details if any occurred during processing.
Error *Error `json:"error,omitempty"`
}
// SendAck sends an acknowledgment response (ACK) to the client.
func SendAck(w http.ResponseWriter) {
resp := &model.Response{
Message: model.Message{
@@ -59,7 +70,8 @@ func SendAck(w http.ResponseWriter) {
}
}
func nack(w http.ResponseWriter, err *model.Error, status int, ctx context.Context) {
// nack sends a negative acknowledgment (NACK) response with an error message.
func nack(ctx context.Context, w http.ResponseWriter, err *model.Error, status int) {
resp := &model.Response{
Message: model.Message{
Ack: model.Ack{
@@ -80,6 +92,7 @@ func nack(w http.ResponseWriter, err *model.Error, status int, ctx context.Conte
}
}
// internalServerError generates an internal server error response.
func internalServerError(ctx context.Context) *model.Error {
return &model.Error{
Code: http.StatusText(http.StatusInternalServerError),
@@ -87,6 +100,7 @@ func internalServerError(ctx context.Context) *model.Error {
}
}
// SendNack processes different types of errors and sends an appropriate NACK response.
func SendNack(ctx context.Context, w http.ResponseWriter, err error) {
var schemaErr *model.SchemaValidationErr
var signErr *model.SignValidationErr
@@ -95,19 +109,19 @@ func SendNack(ctx context.Context, w http.ResponseWriter, err error) {
switch {
case errors.As(err, &schemaErr):
nack(w, schemaErr.BecknError(), http.StatusBadRequest, ctx)
nack(ctx, w, schemaErr.BecknError(), http.StatusBadRequest)
return
case errors.As(err, &signErr):
nack(w, signErr.BecknError(), http.StatusUnauthorized, ctx)
nack(ctx, w, signErr.BecknError(), http.StatusUnauthorized)
return
case errors.As(err, &badReqErr):
nack(w, badReqErr.BecknError(), http.StatusBadRequest, ctx)
nack(ctx, w, badReqErr.BecknError(), http.StatusBadRequest)
return
case errors.As(err, &notFoundErr):
nack(w, notFoundErr.BecknError(), http.StatusNotFound, ctx)
nack(ctx, w, notFoundErr.BecknError(), http.StatusNotFound)
return
default:
nack(w, internalServerError(ctx), http.StatusInternalServerError, ctx)
nack(ctx, w, internalServerError(ctx), http.StatusInternalServerError)
return
}
}

View File

@@ -234,7 +234,7 @@ func TestNack_1(t *testing.T) {
return
}
nack(w, tt.err, tt.status, ctx)
nack(ctx, w, tt.err, tt.status)
if !tt.useBadWrite {
recorder, ok := w.(*httptest.ResponseRecorder)
if !ok {