merged with latest main
This commit is contained in:
130
CONFIG.md
130
CONFIG.md
@@ -481,7 +481,34 @@ registry:
|
||||
|
||||
---
|
||||
|
||||
#### 2. Key Manager Plugin
|
||||
#### 2. Dediregistry Plugin
|
||||
|
||||
**Purpose**: Lookup participant information from a Decentralized Discovery (DeDi) registry.
|
||||
|
||||
**Configuration**:
|
||||
```yaml
|
||||
registry:
|
||||
id: dediregistry
|
||||
config:
|
||||
url: "https://dedi-wrapper.example.com/dedi"
|
||||
registryName: "subscribers.beckn.one"
|
||||
timeout: 30
|
||||
retry_max: 3
|
||||
retry_wait_min: 1s
|
||||
retry_wait_max: 5s
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
- `url`: DeDi wrapper API base URL (Required)
|
||||
- `registryName`: Name of the registry (Required)
|
||||
- `timeout`: Request timeout in seconds (Optional, default: client default)
|
||||
- `retry_max`: Maximum number of retry attempts (Optional, default: 4)
|
||||
- `retry_wait_min`: Minimum wait time between retries in duration format (Optional, default: 1s)
|
||||
- `retry_wait_max`: Maximum wait time between retries in duration format (Optional, default: 30s)
|
||||
|
||||
---
|
||||
|
||||
#### 3. Key Manager Plugin
|
||||
|
||||
**Purpose**: Manage cryptographic keys for signing and verification.
|
||||
|
||||
@@ -711,9 +738,43 @@ routingRules:
|
||||
|
||||
#### `domain`
|
||||
**Type**: `string`
|
||||
**Required**: Yes
|
||||
**Required**: Conditional (Required for v1.x.x, Optional for v2.x.x)
|
||||
**Description**: Beckn domain identifier (e.g., `retail:1.1.0`, `ONDC:TRV10`, `nic2004:60221`)
|
||||
|
||||
**Version-Specific Behavior**:
|
||||
- **Beckn Protocol v1.x.x**: Domain is **required**. Each rule must specify a domain, and routing uses domain as a key.
|
||||
- **Beckn Protocol v2.x.x**: Domain is **optional** and ignored during routing. If provided, a warning is logged. All v2 rules are domain-agnostic.
|
||||
- **Conflict Detection**: For v2, multiple rules with the same version and endpoint (regardless of domain) will cause a configuration error.
|
||||
|
||||
**Examples**:
|
||||
```yaml
|
||||
# Valid v1 rule - domain required
|
||||
- domain: "ONDC:TRV10"
|
||||
version: "1.1.0"
|
||||
targetType: "url"
|
||||
target:
|
||||
url: "http://backend:3000"
|
||||
endpoints:
|
||||
- search
|
||||
|
||||
# Valid v2 rule - domain optional (omitted)
|
||||
- version: "2.0.0"
|
||||
targetType: "url"
|
||||
target:
|
||||
url: "http://backend:3000"
|
||||
endpoints:
|
||||
- search
|
||||
|
||||
# Valid v2 rule - domain provided (warning logged, but ignored)
|
||||
- domain: "ONDC:TRV10"
|
||||
version: "2.0.0"
|
||||
targetType: "url"
|
||||
target:
|
||||
url: "http://backend:3000"
|
||||
endpoints:
|
||||
- search
|
||||
```
|
||||
|
||||
#### `version`
|
||||
**Type**: `string`
|
||||
**Required**: Yes
|
||||
@@ -881,6 +942,71 @@ routingRules:
|
||||
|
||||
**Behavior**: All endpoints route to exactly `http://backend:3000/webhook` without appending the endpoint name.
|
||||
|
||||
#### Example 5: Beckn Protocol v2 Domain-Agnostic Routing
|
||||
|
||||
```yaml
|
||||
routingRules:
|
||||
# v2 rule without domain (recommended)
|
||||
- version: "2.0.0"
|
||||
targetType: "url"
|
||||
target:
|
||||
url: "https://gateway.example.com/v2"
|
||||
endpoints:
|
||||
- search
|
||||
- select
|
||||
- init
|
||||
- confirm
|
||||
|
||||
# v1 rules still require domain
|
||||
- domain: "ONDC:TRV10"
|
||||
version: "1.1.0"
|
||||
targetType: "url"
|
||||
target:
|
||||
url: "https://gateway.example.com/v1/trv"
|
||||
endpoints:
|
||||
- search
|
||||
```
|
||||
|
||||
**Behavior**:
|
||||
- v2 requests (version `2.0.0`) route to gateway regardless of domain in request
|
||||
- v1 requests (version `1.1.0`) route based on domain matching
|
||||
- Domain field is ignored for v2 routing decisions
|
||||
|
||||
#### Example 6: v2 Conflict Detection
|
||||
|
||||
```yaml
|
||||
# INVALID CONFIGURATION - Will fail at startup
|
||||
routingRules:
|
||||
- domain: "ONDC:TRV10"
|
||||
version: "2.0.0"
|
||||
targetType: "url"
|
||||
target:
|
||||
url: "https://backend-a.com"
|
||||
endpoints:
|
||||
- search
|
||||
|
||||
- domain: "ONDC:TRV11" # Different domain, but same version and endpoint
|
||||
version: "2.0.0"
|
||||
targetType: "url"
|
||||
target:
|
||||
url: "https://backend-b.com"
|
||||
endpoints:
|
||||
- search # ERROR: Duplicate v2 rule for 'search' endpoint
|
||||
```
|
||||
|
||||
**Error**: Configuration will fail with: `duplicate endpoint 'search' found for version 2.0.0. For v2.x.x, domain is ignored, so you can only define each endpoint once per version. Please remove the duplicate rule`
|
||||
|
||||
**Fix**: For v2, use a single rule per endpoint since domain is ignored:
|
||||
```yaml
|
||||
routingRules:
|
||||
- version: "2.0.0"
|
||||
targetType: "url"
|
||||
target:
|
||||
url: "https://unified-backend.com"
|
||||
endpoints:
|
||||
- search
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Deployment Scenarios
|
||||
|
||||
10
README.md
10
README.md
@@ -151,7 +151,7 @@ Resources:
|
||||
|
||||
#### 3. **Plugin Types**
|
||||
- **Cache**: Redis-based response caching
|
||||
- **Router**: YAML-based routing rules engine for request forwarding
|
||||
- **Router**: YAML-based routing rules engine for request forwarding (supports domain-agnostic routing for Beckn v2.x.x)
|
||||
- **Signer**: Ed25519 digital signature creation for outgoing requests
|
||||
- **SignValidator**: Ed25519 signature validation for incoming requests
|
||||
- **SchemaValidator**: JSON schema validation
|
||||
@@ -239,12 +239,18 @@ This automated script will:
|
||||
- Start ONIX adapter in Docker
|
||||
- Create environment configuration
|
||||
|
||||
**Note:** Extract schemas before running: `unzip schemas.zip` (required for schema validation) and before running the automated setup, build the adapter image if required ,update docker-compose-adapter.yaml to use the correct onix image
|
||||
**Note:**
|
||||
- **Schema Validation**: Extract schemas before running: `unzip schemas.zip` (required for `schemavalidator` plugin)
|
||||
- **Alternative**: You can use `schemav2validator` plugin instead, which fetches schemas from a URL and doesn't require local schema extraction. See [CONFIG.md](CONFIG.md) for more configuration details.
|
||||
- **Optional**: Before running the automated setup, build the adapter image and update `docker-compose-adapter.yaml` to use the correct image
|
||||
|
||||
```bash
|
||||
# from the repository root
|
||||
docker build -f Dockerfile.adapter-with-plugins -t beckn-onix:latest .
|
||||
```
|
||||
|
||||
**For detailed setup instructions, see [SETUP.md](SETUP.md)**
|
||||
|
||||
**Services Started:**
|
||||
- Redis: localhost:6379
|
||||
- ONIX Adapter: http://localhost:8081
|
||||
|
||||
80
SETUP.md
80
SETUP.md
@@ -88,14 +88,53 @@ This will automatically:
|
||||
|
||||
**Key Management:** Uses `simplekeymanager` with embedded keys - no Vault setup required!
|
||||
|
||||
**Note:** Extract schemas before running: `unzip schemas.zip` (required for schema validation) and before running the automated setup, build the adapter image ,update docker-compose-adapter.yaml to use the correct image (optional)
|
||||
**Note:**
|
||||
- **Schema Validation**: Extract schemas before running: `unzip schemas.zip` (required for `schemavalidator` plugin)
|
||||
- **Alternative**: You can use `schemav2validator` plugin instead, which fetches schemas from a URL and doesn't require local schema extraction. See [CONFIG.md](CONFIG.md) for more configuration details.
|
||||
- **Optional**: Before running the automated setup, build the adapter image and update `docker-compose-adapter.yaml` to use the correct image
|
||||
|
||||
```bash
|
||||
# from the repository root
|
||||
docker build -f Dockerfile.adapter-with-plugins -t beckn-onix:latest .
|
||||
```
|
||||
|
||||
### Option 2: Complete Beckn Network
|
||||
### Option 2: Beckn One Network Setup
|
||||
|
||||
For a local setup using Beckn One with DeDI-Registry and Catalog Discovery:
|
||||
|
||||
```bash
|
||||
cd beckn-onix/install
|
||||
chmod +x beckn-onix.sh
|
||||
./beckn-onix.sh
|
||||
|
||||
# Choose option 3: "Set up a network on local machine with Beckn One (DeDI-Registry, Catalog Discovery)"
|
||||
```
|
||||
|
||||
This will automatically:
|
||||
- Install required packages (Docker, docker-compose, jq)
|
||||
- Build ONIX adapter plugins
|
||||
- Start Redis
|
||||
- Start ONIX adapters for BAP and BPP with Beckn One configuration
|
||||
- Start Sandbox containers for BAP and BPP
|
||||
|
||||
**Services Started:**
|
||||
- Redis: localhost:6379
|
||||
- ONIX Adapter (BAP): http://localhost:8081
|
||||
- ONIX Adapter (BPP): http://localhost:8082
|
||||
- Sandbox BAP: http://localhost:3001
|
||||
- Sandbox BPP: http://localhost:3002
|
||||
|
||||
**Key Features:**
|
||||
- Uses Beckn One (DeDI-Registry) for registry services
|
||||
- Pre-configured keys in YAML configuration files
|
||||
- No local registry or gateway deployment required
|
||||
|
||||
**Note:**
|
||||
- **Schema Validation**: Extract schemas before running: `unzip schemas.zip` (required for `schemavalidator` plugin)
|
||||
- **Alternative**: You can use `schemav2validator` plugin instead, which fetches schemas from a URL and doesn't require local schema extraction. See [CONFIG.md](CONFIG.md) for more configuration details.
|
||||
|
||||
### Option 3: Complete Beckn Network
|
||||
|
||||
|
||||
For a full local Beckn network with all components:
|
||||
|
||||
@@ -104,7 +143,8 @@ cd beckn-onix/install
|
||||
chmod +x beckn-onix.sh
|
||||
./beckn-onix.sh
|
||||
|
||||
# Choose option 3: "Set up a network on your local machine"
|
||||
# Choose option 4: "Set up a network on local machine with local registry and gateway (without Beckn One)"
|
||||
|
||||
```
|
||||
|
||||
This will automatically:
|
||||
@@ -125,7 +165,10 @@ This will automatically:
|
||||
- ONIX Adapter: http://localhost:8081
|
||||
- Redis: localhost:6379
|
||||
|
||||
**Note:** Extract schemas before running: `unzip schemas.zip` (required for schema validation) and before running the automated full-network setup, build the adapter image , update docker-compose-adapter.yaml to use the correct image(optional)
|
||||
**Note:**
|
||||
- **Schema Validation**: Extract schemas before running: `unzip schemas.zip` (required for `schemavalidator` plugin)
|
||||
- **Alternative**: You can use `schemav2validator` plugin instead, which fetches schemas from a URL and doesn't require local schema extraction. See [CONFIG.md](CONFIG.md) for more configuration details.
|
||||
- **Optional**: Before running the automated full-network setup, build the adapter image and update `docker-compose-adapter.yaml` to use the correct image
|
||||
|
||||
```bash
|
||||
# from the repository root
|
||||
@@ -193,6 +236,7 @@ This creates `.so` files in the `plugins/` directory:
|
||||
- `signer.so` - Message signing
|
||||
- `signvalidator.so` - Signature validation
|
||||
- `schemavalidator.so` - JSON schema validation
|
||||
- `schemav2validator.so` - OpenAPI 3.x schema validation
|
||||
- `keymanager.so` - Vault integration
|
||||
- `simplekeymanager.so` - Simple key management (no Vault)
|
||||
- `publisher.so` - RabbitMQ publishing
|
||||
@@ -865,29 +909,39 @@ router:
|
||||
|
||||
### Routing Rules Configuration
|
||||
|
||||
**For complete routing configuration documentation including v2 domain-agnostic routing, see [CONFIG.md - Routing Configuration](CONFIG.md#routing-configuration).**
|
||||
|
||||
Basic example:
|
||||
|
||||
```yaml
|
||||
routingRules:
|
||||
# v1.x.x - domain is required
|
||||
- domain: "ONDC:RET10"
|
||||
version: "1.0.0"
|
||||
targetType: "url" # or "publisher"
|
||||
target:
|
||||
url: "https://seller.example.com/beckn"
|
||||
# OR for async
|
||||
# queueName: "retail_queue"
|
||||
# routingKey: "retail.*"
|
||||
endpoints:
|
||||
- search
|
||||
- select
|
||||
- init
|
||||
- confirm
|
||||
headers: # Optional additional headers
|
||||
X-Custom-Header: "value"
|
||||
timeout: 60 # seconds
|
||||
retryPolicy:
|
||||
maxRetries: 3
|
||||
backoff: exponential
|
||||
|
||||
# v2.x.x - domain is optional (domain-agnostic routing)
|
||||
- version: "2.0.0"
|
||||
targetType: "url"
|
||||
target:
|
||||
url: "https://seller.example.com/v2/beckn"
|
||||
endpoints:
|
||||
- search
|
||||
- select
|
||||
```
|
||||
|
||||
**Key Points**:
|
||||
- **v1.x.x**: Domain field is required and used for routing
|
||||
- **v2.x.x**: Domain field is optional and ignored (domain-agnostic)
|
||||
- See CONFIG.md for target types: `url`, `bpp`, `bap`, `publisher`
|
||||
|
||||
### Processing Steps
|
||||
|
||||
Available steps for configuration:
|
||||
|
||||
119
config/local-beckn-one-bap.yaml
Normal file
119
config/local-beckn-one-bap.yaml
Normal file
@@ -0,0 +1,119 @@
|
||||
appName: "onix-local"
|
||||
log:
|
||||
level: debug
|
||||
destinations:
|
||||
- type: stdout
|
||||
contextKeys:
|
||||
- transaction_id
|
||||
- message_id
|
||||
- subscriber_id
|
||||
- module_id
|
||||
http:
|
||||
port: 8081
|
||||
timeout:
|
||||
read: 30
|
||||
write: 30
|
||||
idle: 30
|
||||
pluginManager:
|
||||
root: ./plugins
|
||||
modules:
|
||||
- name: bapTxnReceiver
|
||||
path: /bap/receiver/
|
||||
handler:
|
||||
type: std
|
||||
role: bap
|
||||
httpClientConfig:
|
||||
maxIdleConns: 1000
|
||||
maxIdleConnsPerHost: 200
|
||||
idleConnTimeout: 300s
|
||||
responseHeaderTimeout: 5s
|
||||
plugins:
|
||||
registry:
|
||||
id: dediregistry
|
||||
config:
|
||||
url: http://34.14.173.68:8080/dedi
|
||||
registryName: subscribers.beckn.one
|
||||
timeout: 10
|
||||
retry_max: 3
|
||||
retry_wait_min: 100ms
|
||||
retry_wait_max: 500ms
|
||||
keyManager:
|
||||
id: simplekeymanager
|
||||
config:
|
||||
networkParticipant: ev-charging.sandbox1.com
|
||||
keyId: 76EU7PktCXdPoNEZBjmg4Eb25A2egsd5MYJ67Qxza7bJQFvBHCYxgk
|
||||
signingPrivateKey: 9NBh67Pk/6v3irrkYZHlQ5E1qw+GivHdDFtKeCylzIM=
|
||||
signingPublicKey: Z3Hnc8FZDo/7dwWApeRVs6OV560gr7uxPsFUDGUMsBg=
|
||||
encrPrivateKey: 9NBh67Pk/6v3irrkYZHlQ5E1qw+GivHdDFtKeCylzIM=
|
||||
encrPublicKey: Z3Hnc8FZDo/7dwWApeRVs6OV560gr7uxPsFUDGUMsBg=
|
||||
cache:
|
||||
id: cache
|
||||
config:
|
||||
addr: redis:6379
|
||||
schemaValidator:
|
||||
id: schemavalidator
|
||||
config:
|
||||
schemaDir: ./schemas
|
||||
signValidator:
|
||||
id: signvalidator
|
||||
router:
|
||||
id: router
|
||||
config:
|
||||
routingConfig: ./config/local-beckn-one-routing-BAPReceiver.yaml
|
||||
middleware:
|
||||
- id: reqpreprocessor
|
||||
config:
|
||||
uuidKeys: transaction_id,message_id
|
||||
role: bap
|
||||
steps:
|
||||
- validateSign
|
||||
- addRoute
|
||||
|
||||
- name: bapTxnCaller
|
||||
path: /bap/caller/
|
||||
handler:
|
||||
type: std
|
||||
role: bap
|
||||
httpClientConfig:
|
||||
maxIdleConns: 1000
|
||||
maxIdleConnsPerHost: 200
|
||||
idleConnTimeout: 300s
|
||||
responseHeaderTimeout: 5s
|
||||
plugins:
|
||||
registry:
|
||||
id: dediregistry
|
||||
config:
|
||||
url: http://34.14.173.68:8080/dedi
|
||||
registryName: subscribers.beckn.one
|
||||
timeout: 10
|
||||
retry_max: 3
|
||||
retry_wait_min: 100ms
|
||||
retry_wait_max: 500ms
|
||||
keyManager:
|
||||
id: simplekeymanager
|
||||
config:
|
||||
networkParticipant: ev-charging.sandbox1.com
|
||||
keyId: 76EU7PktCXdPoNEZBjmg4Eb25A2egsd5MYJ67Qxza7bJQFvBHCYxgk
|
||||
signingPrivateKey: 9NBh67Pk/6v3irrkYZHlQ5E1qw+GivHdDFtKeCylzIM=
|
||||
signingPublicKey: Z3Hnc8FZDo/7dwWApeRVs6OV560gr7uxPsFUDGUMsBg=
|
||||
encrPrivateKey: 9NBh67Pk/6v3irrkYZHlQ5E1qw+GivHdDFtKeCylzIM=
|
||||
encrPublicKey: Z3Hnc8FZDo/7dwWApeRVs6OV560gr7uxPsFUDGUMsBg=
|
||||
cache:
|
||||
id: cache
|
||||
config:
|
||||
addr: redis:6379
|
||||
router:
|
||||
id: router
|
||||
config:
|
||||
routingConfig: ./config/local-beckn-one-routing-BAPCaller.yaml
|
||||
signer:
|
||||
id: signer
|
||||
middleware:
|
||||
- id: reqpreprocessor
|
||||
config:
|
||||
uuidKeys: transaction_id,message_id
|
||||
role: bap
|
||||
steps:
|
||||
- addRoute
|
||||
- sign
|
||||
|
||||
113
config/local-beckn-one-bpp.yaml
Normal file
113
config/local-beckn-one-bpp.yaml
Normal file
@@ -0,0 +1,113 @@
|
||||
appName: "onix-local"
|
||||
log:
|
||||
level: debug
|
||||
destinations:
|
||||
- type: stdout
|
||||
contextKeys:
|
||||
- transaction_id
|
||||
- message_id
|
||||
- subscriber_id
|
||||
- module_id
|
||||
http:
|
||||
port: 8082
|
||||
timeout:
|
||||
read: 30
|
||||
write: 30
|
||||
idle: 30
|
||||
pluginManager:
|
||||
root: ./plugins
|
||||
modules:
|
||||
- name: bppTxnReceiver
|
||||
path: /bpp/receiver/
|
||||
handler:
|
||||
type: std
|
||||
role: bpp
|
||||
httpClientConfig:
|
||||
maxIdleConns: 1000
|
||||
maxIdleConnsPerHost: 200
|
||||
idleConnTimeout: 300s
|
||||
responseHeaderTimeout: 5s
|
||||
plugins:
|
||||
registry:
|
||||
id: dediregistry
|
||||
config:
|
||||
url: http://34.14.173.68:8080/dedi
|
||||
registryName: subscribers.beckn.one
|
||||
timeout: 10
|
||||
retry_max: 3
|
||||
retry_wait_min: 100ms
|
||||
retry_wait_max: 500ms
|
||||
keyManager:
|
||||
id: simplekeymanager
|
||||
config:
|
||||
networkParticipant: ev-charging.sandbox2.com
|
||||
keyId: 76EU7ncBX74BMNTQJMcMYoTMSzU7k71owUF53fN4jdxmosxZrdjdDk
|
||||
signingPrivateKey: hnMdzvcZBnLJ6W1f4Y2ZCLlJo4phKMvs48ZbXjbS7/k=
|
||||
signingPublicKey: H1xM/ejGIJpH+DmAF1A9KjBBLJ74pZ8B0gnZ4z4DIkU=
|
||||
encrPrivateKey: hnMdzvcZBnLJ6W1f4Y2ZCLlJo4phKMvs48ZbXjbS7/k=
|
||||
encrPublicKey: H1xM/ejGIJpH+DmAF1A9KjBBLJ74pZ8B0gnZ4z4DIkU=
|
||||
cache:
|
||||
id: cache
|
||||
config:
|
||||
addr: redis:6379
|
||||
schemaValidator:
|
||||
id: schemavalidator
|
||||
config:
|
||||
schemaDir: ./schemas
|
||||
signValidator:
|
||||
id: signvalidator
|
||||
router:
|
||||
id: router
|
||||
config:
|
||||
routingConfig: ./config/local-beckn-one-routing-BPPReceiver.yaml
|
||||
steps:
|
||||
- validateSign
|
||||
- addRoute
|
||||
|
||||
- name: bppTxnCaller
|
||||
path: /bpp/caller/
|
||||
handler:
|
||||
type: std
|
||||
role: bpp
|
||||
httpClientConfig:
|
||||
maxIdleConns: 1000
|
||||
maxIdleConnsPerHost: 200
|
||||
idleConnTimeout: 300s
|
||||
responseHeaderTimeout: 5s
|
||||
plugins:
|
||||
registry:
|
||||
id: dediregistry
|
||||
config:
|
||||
url: http://34.14.173.68:8080/dedi
|
||||
registryName: subscribers.beckn.one
|
||||
timeout: 10
|
||||
retry_max: 3
|
||||
retry_wait_min: 100ms
|
||||
retry_wait_max: 500ms
|
||||
keyManager:
|
||||
id: simplekeymanager
|
||||
config:
|
||||
networkParticipant: ev-charging.sandbox2.com
|
||||
keyId: 76EU7ncBX74BMNTQJMcMYoTMSzU7k71owUF53fN4jdxmosxZrdjdDk
|
||||
signingPrivateKey: hnMdzvcZBnLJ6W1f4Y2ZCLlJo4phKMvs48ZbXjbS7/k=
|
||||
signingPublicKey: H1xM/ejGIJpH+DmAF1A9KjBBLJ74pZ8B0gnZ4z4DIkU=
|
||||
encrPrivateKey: hnMdzvcZBnLJ6W1f4Y2ZCLlJo4phKMvs48ZbXjbS7/k=
|
||||
encrPublicKey: H1xM/ejGIJpH+DmAF1A9KjBBLJ74pZ8B0gnZ4z4DIkU=
|
||||
cache:
|
||||
id: cache
|
||||
config:
|
||||
addr: redis:6379
|
||||
router:
|
||||
id: router
|
||||
config:
|
||||
routingConfig: ./config/local-beckn-one-routing-BPPCaller.yaml
|
||||
signer:
|
||||
id: signer
|
||||
middleware:
|
||||
- id: reqpreprocessor
|
||||
config:
|
||||
uuidKeys: transaction_id,message_id
|
||||
role: bpp
|
||||
steps:
|
||||
- addRoute
|
||||
- sign
|
||||
22
config/local-beckn-one-routing-BAPCaller.yaml
Normal file
22
config/local-beckn-one-routing-BAPCaller.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
routingRules:
|
||||
- domain: "beckn.one:deg:ev-charging:2.0.0" # Retail domain
|
||||
version: "2.0.0"
|
||||
targetType: "bpp"
|
||||
endpoints:
|
||||
- select
|
||||
- init
|
||||
- confirm
|
||||
- status
|
||||
- track
|
||||
- cancel
|
||||
- update
|
||||
- rating
|
||||
- support
|
||||
|
||||
- domain: "beckn.one:deg:ev-charging:2.0.0" # Retail domain
|
||||
version: "2.0.0"
|
||||
targetType: "url"
|
||||
target:
|
||||
url: "https://34.93.141.21.sslip.io/beckn"
|
||||
endpoints:
|
||||
- discover
|
||||
14
config/local-beckn-one-routing-BAPReceiver.yaml
Normal file
14
config/local-beckn-one-routing-BAPReceiver.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
routingRules:
|
||||
- domain: "beckn.one:deg:ev-charging:2.0.0"
|
||||
version: "2.0.0"
|
||||
targetType: "url"
|
||||
target:
|
||||
url: "http://sandbox-bap:3001/api/bap-webhook"
|
||||
endpoints:
|
||||
- on_discover
|
||||
- on_select
|
||||
- on_init
|
||||
- on_confirm
|
||||
- on_status
|
||||
- on_update
|
||||
- on_cancel
|
||||
14
config/local-beckn-one-routing-BPPCaller.yaml
Normal file
14
config/local-beckn-one-routing-BPPCaller.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
routingRules:
|
||||
- domain: "beckn.one:deg:ev-charging:2.0.0" # Retail domain
|
||||
version: "2.0.0"
|
||||
targetType: "bap"
|
||||
endpoints:
|
||||
- on_status
|
||||
- on_cancel
|
||||
- on_update
|
||||
- on_select
|
||||
- on_init
|
||||
- on_confirm
|
||||
- on_track
|
||||
- on_rating
|
||||
- on_support
|
||||
16
config/local-beckn-one-routing-BPPReceiver.yaml
Normal file
16
config/local-beckn-one-routing-BPPReceiver.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
routingRules:
|
||||
- domain: "beckn.one:deg:ev-charging:2.0.0" # Retail domain
|
||||
version: "2.0.0"
|
||||
targetType: "url"
|
||||
target:
|
||||
url: "http://sandbox-bpp:3002/api/webhook"
|
||||
endpoints:
|
||||
- select
|
||||
- init
|
||||
- confirm
|
||||
- status
|
||||
- track
|
||||
- cancel
|
||||
- update
|
||||
- rating
|
||||
- support
|
||||
@@ -928,6 +928,58 @@ install_bap_adapter() {
|
||||
install_bpp_adapter() {
|
||||
install_adapter "BPP"
|
||||
}
|
||||
|
||||
# Function to install Beckn One Adapter (Option 3)
|
||||
install_beckn_one_adapter() {
|
||||
echo "${GREEN}................Setting up network with Beckn One................${NC}"
|
||||
|
||||
# Create schemas directory if not exists
|
||||
if [ ! -d "schemas" ]; then
|
||||
mkdir -p schemas
|
||||
echo -e "${GREEN}✓ Created schemas directory${NC}"
|
||||
fi
|
||||
|
||||
echo "${GREEN}................Building plugins for ONIX Adapter................${NC}"
|
||||
|
||||
# Build plugins
|
||||
cd ..
|
||||
if [ -f "./install/build-plugins.sh" ]; then
|
||||
chmod +x ./install/build-plugins.sh
|
||||
./install/build-plugins.sh
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "${GREEN}✓ Plugins built successfully${NC}"
|
||||
else
|
||||
echo "${RED}Error: Plugin build failed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "${RED}Error: install/build-plugins.sh not found${NC}"
|
||||
exit 1
|
||||
fi
|
||||
cd install
|
||||
|
||||
echo "${GREEN}................Starting Redis and Beckn One Adapters................${NC}"
|
||||
|
||||
# Start Redis first
|
||||
start_support_services
|
||||
|
||||
# Start Beckn One adapters (onix-bap, onix-bpp, sandbox-bap, sandbox-bpp)
|
||||
start_container $adapter_beckn_one_docker_compose_file "onix-bap"
|
||||
start_container $adapter_beckn_one_docker_compose_file "onix-bpp"
|
||||
start_container $adapter_beckn_one_docker_compose_file "sandbox-bap"
|
||||
start_container $adapter_beckn_one_docker_compose_file "sandbox-bpp"
|
||||
|
||||
sleep 10
|
||||
|
||||
echo "${GREEN}✓ Beckn One network setup complete${NC}"
|
||||
echo ""
|
||||
echo "${BLUE}Services running:${NC}"
|
||||
echo " - ONIX BAP Adapter: http://localhost:8081"
|
||||
echo " - ONIX BPP Adapter: http://localhost:8082"
|
||||
echo " - Sandbox BAP: http://localhost:3001"
|
||||
echo " - Sandbox BPP: http://localhost:3002"
|
||||
echo " - Redis: localhost:6379"
|
||||
}
|
||||
# MAIN SCRIPT STARTS HERE
|
||||
|
||||
echo "Welcome to Beckn-ONIX!"
|
||||
@@ -941,29 +993,35 @@ echo "Checking prerequisites of Beckn-ONIX deployment"
|
||||
check_docker_permissions
|
||||
|
||||
echo "Beckn-ONIX is a platform that helps you quickly launch and configure beckn-enabled networks."
|
||||
echo -e "\nWhat would you like to do?\n1. Join an existing network\n2. Create new production network\n3. Set up a network on your local machine\n4. Merge multiple networks\n5. Configure Existing Network\n6. Update/Upgrade Application\n(Press Ctrl+C to exit)"
|
||||
echo -e "\nWhat would you like to do?\n1. Join an existing network\n2. Create new production network\n3. Set up a network on local machine with Beckn One (DeDI-Registry, Catalog Discovery)\n4. Set up a network on local machine with local registry and gateway (without Beckn One)\n5. Merge multiple networks\n6. Configure Existing Network\n7. Update/Upgrade Application\n(Press Ctrl+C to exit)"
|
||||
read -p "Enter your choice: " choice
|
||||
|
||||
validate_input "$choice" 6
|
||||
validate_input "$choice" 7
|
||||
if [[ $? -ne 0 ]]; then
|
||||
restart_script # Restart the script if input is invalid
|
||||
fi
|
||||
|
||||
if [[ $choice -eq 3 ]]; then
|
||||
echo "Installing all components on the local machine"
|
||||
echo "${GREEN}Setting up network with Beckn One${NC}"
|
||||
echo "${BLUE}This setup uses Beckn One (DeDI-Registry) for registry and catalog discovery${NC}"
|
||||
echo ""
|
||||
install_package
|
||||
install_beckn_one_adapter
|
||||
elif [[ $choice -eq 4 ]]; then
|
||||
echo "Installing network with local registry and gateway (without Beckn One)"
|
||||
install_package
|
||||
install_registry
|
||||
install_gateway
|
||||
install_bap_protocol_server
|
||||
install_bpp_protocol_server_with_sandbox
|
||||
install_adapter "BOTH"
|
||||
elif [[ $choice -eq 4 ]]; then
|
||||
elif [[ $choice -eq 5 ]]; then
|
||||
echo "Determining the platforms available based on the initial choice"
|
||||
mergingNetworks
|
||||
elif [[ $choice -eq 5 ]]; then
|
||||
elif [[ $choice -eq 6 ]]; then
|
||||
echo "${BoldGreen}Currently this feature is not available in this distribution of Beckn ONIX${NC}"
|
||||
restart_script
|
||||
elif [[ $choice -eq 6 ]]; then
|
||||
elif [[ $choice -eq 7 ]]; then
|
||||
update_network
|
||||
else
|
||||
# Determine the platforms available based on the initial choice
|
||||
|
||||
101
install/docker-compose-adapter-beckn-one.yml
Normal file
101
install/docker-compose-adapter-beckn-one.yml
Normal file
@@ -0,0 +1,101 @@
|
||||
services:
|
||||
# ============================================
|
||||
# Core Infrastructure Services
|
||||
# ============================================
|
||||
|
||||
# Redis - Caching Service
|
||||
redis:
|
||||
image: redis:alpine
|
||||
container_name: redis
|
||||
ports:
|
||||
- "6379:6379"
|
||||
networks:
|
||||
- beckn_network
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
|
||||
onix-bap:
|
||||
image: fidedocker/onix-adapter
|
||||
container_name: onix-bap
|
||||
platform: linux/amd64
|
||||
networks:
|
||||
- beckn_network
|
||||
ports:
|
||||
- "8081:8081"
|
||||
environment:
|
||||
CONFIG_FILE: "/app/config/local-simple.yaml"
|
||||
VAULT_ADDR: http://vault:8200
|
||||
VAULT_TOKEN: root
|
||||
REDIS_ADDR: redis:6379
|
||||
RABBITMQ_ADDR: rabbitmq:5672
|
||||
RABBITMQ_USER: admin
|
||||
RABBITMQ_PASS: admin123
|
||||
volumes:
|
||||
- ../config:/app/config
|
||||
- ../schemas:/app/schemas
|
||||
command: ["./server", "--config=/app/config/local-beckn-one-bap.yaml"]
|
||||
|
||||
onix-bpp:
|
||||
image: fidedocker/onix-adapter
|
||||
container_name: onix-bpp
|
||||
platform: linux/amd64
|
||||
networks:
|
||||
- beckn_network
|
||||
ports:
|
||||
- "8082:8082"
|
||||
environment:
|
||||
CONFIG_FILE: "/app/config/local-simple.yaml"
|
||||
VAULT_ADDR: http://vault:8200
|
||||
VAULT_TOKEN: root
|
||||
REDIS_ADDR: redis:6379
|
||||
RABBITMQ_ADDR: rabbitmq:5672
|
||||
RABBITMQ_USER: admin
|
||||
RABBITMQ_PASS: admin123
|
||||
volumes:
|
||||
- ../config:/app/config
|
||||
- ../schemas:/app/schemas
|
||||
command: ["./server", "--config=/app/config/local-beckn-one-bpp.yaml"]
|
||||
|
||||
sandbox-bap:
|
||||
container_name: sandbox-bap
|
||||
image: fidedocker/sandbox-2.0:latest
|
||||
platform: linux/amd64
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- PORT=3001
|
||||
ports:
|
||||
- "3001:3001"
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-qO-", "http://localhost:3001/api/health"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
networks:
|
||||
- beckn_network
|
||||
|
||||
sandbox-bpp:
|
||||
container_name: sandbox-bpp
|
||||
image: fidedocker/sandbox-2.0:latest
|
||||
platform: linux/amd64
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- PORT=3002
|
||||
ports:
|
||||
- "3002:3002"
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-qO-", "http://localhost:3002/api/health"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
networks:
|
||||
- beckn_network
|
||||
|
||||
networks:
|
||||
beckn_network:
|
||||
name: beckn_network
|
||||
driver: bridge
|
||||
@@ -74,4 +74,5 @@ bap_docker_compose_file=docker-compose-bap.yml
|
||||
registry_docker_compose_file=docker-compose-registry.yml
|
||||
gateway_docker_compose_file=docker-compose-gateway.yml
|
||||
gcl_docker_compose_file=docker-compose-gcl.yml
|
||||
adapter_docker_compose_file=docker-compose-adapter.yml
|
||||
adapter_docker_compose_file=docker-compose-adapter.yml
|
||||
adapter_beckn_one_docker_compose_file=docker-compose-adapter-beckn-one.yml
|
||||
@@ -1,4 +1,5 @@
|
||||
docker compose -f docker-compose-adapter.yml down -v
|
||||
docker compose -f docker-compose-adapter-beckn-one.yml down -v
|
||||
docker compose -f docker-compose-bap.yml down -v
|
||||
docker compose -f docker-compose-bpp.yml down -v
|
||||
docker compose -f docker-compose-bpp-with-sandbox.yml down -v
|
||||
|
||||
@@ -19,6 +19,9 @@ registry:
|
||||
url: "https://dedi-wrapper.example.com/dedi"
|
||||
registryName: "subscribers.beckn.one"
|
||||
timeout: 30
|
||||
retry_max: 3
|
||||
retry_wait_min: 1s
|
||||
retry_wait_max: 5s
|
||||
```
|
||||
|
||||
### Configuration Parameters
|
||||
@@ -28,6 +31,9 @@ registry:
|
||||
| `url` | Yes | DeDi wrapper API base URL (include /dedi path) | - |
|
||||
| `registryName` | Yes | Registry name for lookup path | - |
|
||||
| `timeout` | No | Request timeout in seconds | Client default |
|
||||
| `retry_max` | No | Maximum number of retry attempts | 4 (library default) |
|
||||
| `retry_wait_min` | No | Minimum wait time between retries (e.g., "1s", "500ms") | 1s (library default) |
|
||||
| `retry_wait_max` | No | Maximum wait time between retries (e.g., "5s") | 30s (library default) |
|
||||
|
||||
## API Integration
|
||||
|
||||
@@ -88,6 +94,9 @@ modules:
|
||||
url: "https://dedi-wrapper.example.com/dedi"
|
||||
registryName: "subscribers.beckn.one"
|
||||
timeout: 30
|
||||
retry_max: 3
|
||||
retry_wait_min: 1s
|
||||
retry_wait_max: 5s
|
||||
steps:
|
||||
- validateSign # Required for registry lookup
|
||||
- addRoute
|
||||
|
||||
@@ -15,9 +15,12 @@ import (
|
||||
|
||||
// Config holds configuration parameters for the DeDi registry client.
|
||||
type Config struct {
|
||||
URL string `yaml:"url" json:"url"`
|
||||
RegistryName string `yaml:"registryName" json:"registryName"`
|
||||
Timeout int `yaml:"timeout" json:"timeout"`
|
||||
URL string `yaml:"url" json:"url"`
|
||||
RegistryName string `yaml:"registryName" json:"registryName"`
|
||||
Timeout int `yaml:"timeout" json:"timeout"`
|
||||
RetryMax int `yaml:"retry_max" json:"retry_max"`
|
||||
RetryWaitMin time.Duration `yaml:"retry_wait_min" json:"retry_wait_min"`
|
||||
RetryWaitMax time.Duration `yaml:"retry_wait_max" json:"retry_wait_max"`
|
||||
}
|
||||
|
||||
// DeDiRegistryClient encapsulates the logic for calling the DeDi registry endpoints.
|
||||
@@ -55,6 +58,17 @@ func New(ctx context.Context, cfg *Config) (*DeDiRegistryClient, func() error, e
|
||||
retryClient.HTTPClient.Timeout = time.Duration(cfg.Timeout) * time.Second
|
||||
}
|
||||
|
||||
// Configure retry settings if provided
|
||||
if cfg.RetryMax > 0 {
|
||||
retryClient.RetryMax = cfg.RetryMax
|
||||
}
|
||||
if cfg.RetryWaitMin > 0 {
|
||||
retryClient.RetryWaitMin = cfg.RetryWaitMin
|
||||
}
|
||||
if cfg.RetryWaitMax > 0 {
|
||||
retryClient.RetryWaitMax = cfg.RetryWaitMax
|
||||
}
|
||||
|
||||
client := &DeDiRegistryClient{
|
||||
config: cfg,
|
||||
client: retryClient,
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/beckn-one/beckn-onix/pkg/model"
|
||||
)
|
||||
@@ -84,6 +85,30 @@ func TestNew(t *testing.T) {
|
||||
if err := closer(); err != nil {
|
||||
t.Errorf("closer() error = %v", err)
|
||||
}
|
||||
|
||||
t.Run("should apply custom retry settings", func(t *testing.T) {
|
||||
cfg := &Config{
|
||||
URL: "http://test.com",
|
||||
RegistryName: "subscribers.beckn.one",
|
||||
RetryMax: 10,
|
||||
RetryWaitMin: 100 * time.Millisecond,
|
||||
RetryWaitMax: 1 * time.Second,
|
||||
}
|
||||
client, _, err := New(ctx, cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, but got: %v", err)
|
||||
}
|
||||
|
||||
if client.client.RetryMax != cfg.RetryMax {
|
||||
t.Errorf("expected RetryMax to be %d, but got %d", cfg.RetryMax, client.client.RetryMax)
|
||||
}
|
||||
if client.client.RetryWaitMin != cfg.RetryWaitMin {
|
||||
t.Errorf("expected RetryWaitMin to be %v, but got %v", cfg.RetryWaitMin, client.client.RetryWaitMin)
|
||||
}
|
||||
if client.client.RetryWaitMax != cfg.RetryWaitMax {
|
||||
t.Errorf("expected RetryWaitMax to be %v, but got %v", cfg.RetryWaitMax, client.client.RetryWaitMax)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestLookup(t *testing.T) {
|
||||
|
||||
@@ -92,14 +92,23 @@ func (r *Router) loadRules(configPath string) error {
|
||||
}
|
||||
// Build the optimized rule map
|
||||
for _, rule := range config.RoutingRules {
|
||||
// For v2.x.x, warn if domain is provided and normalize to wildcard "*"
|
||||
domain := rule.Domain
|
||||
if isV2Version(rule.Version) {
|
||||
if domain != "" {
|
||||
fmt.Printf("WARNING: Domain field '%s' is not needed for version %s and will be ignored. Consider removing it from your config.\n", domain, rule.Version)
|
||||
}
|
||||
domain = "*"
|
||||
}
|
||||
|
||||
// Initialize domain map if not exists
|
||||
if _, ok := r.rules[rule.Domain]; !ok {
|
||||
r.rules[rule.Domain] = make(map[string]map[string]*model.Route)
|
||||
if _, ok := r.rules[domain]; !ok {
|
||||
r.rules[domain] = make(map[string]map[string]*model.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]*model.Route)
|
||||
if _, ok := r.rules[domain][rule.Version]; !ok {
|
||||
r.rules[domain][rule.Version] = make(map[string]*model.Route)
|
||||
}
|
||||
|
||||
// Add all endpoints for this rule
|
||||
@@ -137,7 +146,13 @@ func (r *Router) loadRules(configPath string) error {
|
||||
URL: parsedURL,
|
||||
}
|
||||
}
|
||||
r.rules[rule.Domain][rule.Version][endpoint] = route
|
||||
// Check for conflicting v2 rules
|
||||
if isV2Version(rule.Version) {
|
||||
if _, exists := r.rules[domain][rule.Version][endpoint]; exists {
|
||||
return fmt.Errorf("duplicate endpoint '%s' found for version %s. For v2.x.x, domain is ignored, so you can only define each endpoint once per version. Please remove the duplicate rule", endpoint, rule.Version)
|
||||
}
|
||||
}
|
||||
r.rules[domain][rule.Version][endpoint] = route
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,9 +162,14 @@ func (r *Router) loadRules(configPath string) error {
|
||||
// validateRules performs basic validation on the loaded routing rules.
|
||||
func validateRules(rules []routingRule) error {
|
||||
for _, rule := range rules {
|
||||
// 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")
|
||||
// Ensure version and TargetType are present
|
||||
if rule.Version == "" || rule.TargetType == "" {
|
||||
return fmt.Errorf("invalid rule: version and targetType are required")
|
||||
}
|
||||
|
||||
// Domain is required only for v1.x.x
|
||||
if !isV2Version(rule.Version) && rule.Domain == "" {
|
||||
return fmt.Errorf("invalid rule: domain is required for version %s", rule.Version)
|
||||
}
|
||||
|
||||
// Validate based on TargetType
|
||||
@@ -197,19 +217,34 @@ func (r *Router) Route(ctx context.Context, url *url.URL, body []byte) (*model.R
|
||||
// Extract the endpoint from the URL
|
||||
endpoint := path.Base(url.Path)
|
||||
|
||||
// For v2.x.x, ignore domain and use wildcard; for v1.x.x, use actual domain
|
||||
domain := requestBody.Context.Domain
|
||||
if isV2Version(requestBody.Context.Version) {
|
||||
domain = "*"
|
||||
}
|
||||
|
||||
// Lookup route in the optimized map
|
||||
domainRules, ok := r.rules[requestBody.Context.Domain]
|
||||
domainRules, ok := r.rules[domain]
|
||||
if !ok {
|
||||
if domain == "*" {
|
||||
return nil, fmt.Errorf("no routing rules found for version %s", requestBody.Context.Version)
|
||||
}
|
||||
return nil, fmt.Errorf("no routing rules found for domain %s", requestBody.Context.Domain)
|
||||
}
|
||||
|
||||
versionRules, ok := domainRules[requestBody.Context.Version]
|
||||
if !ok {
|
||||
if domain == "*" {
|
||||
return nil, fmt.Errorf("no routing rules found for version %s", requestBody.Context.Version)
|
||||
}
|
||||
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 {
|
||||
if domain == "*" {
|
||||
return nil, fmt.Errorf("endpoint '%s' is not supported for version %s in routing config", endpoint, requestBody.Context.Version)
|
||||
}
|
||||
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)
|
||||
}
|
||||
@@ -251,4 +286,9 @@ func joinPath(u *url.URL, endpoint string) string {
|
||||
u.Path = "/"
|
||||
}
|
||||
return path.Join(u.Path, endpoint)
|
||||
}
|
||||
|
||||
// isV2Version checks if the version is 2.x.x
|
||||
func isV2Version(version string) bool {
|
||||
return strings.HasPrefix(version, "2.")
|
||||
}
|
||||
@@ -124,7 +124,7 @@ func TestLoadRules(t *testing.T) {
|
||||
// Expected router.rules map structure based on the yaml.
|
||||
expectedRules := map[string]map[string]map[string]*model.Route{
|
||||
"ONDC:TRV10": {
|
||||
"2.0.0": {
|
||||
"1.1.0": {
|
||||
"search": {TargetType: targetTypeURL, URL: parseURL(t, "https://mock_gateway.com/v2/ondc/search")},
|
||||
"init": {TargetType: targetTypeBAP, URL: parseURL(t, "https://mock_bpp.com/v2/ondc/init")},
|
||||
"select": {TargetType: targetTypeBAP, URL: parseURL(t, "https://mock_bpp.com/v2/ondc/select")},
|
||||
@@ -291,7 +291,7 @@ func TestValidateRulesFailure(t *testing.T) {
|
||||
Endpoints: []string{"search", "select"},
|
||||
},
|
||||
},
|
||||
wantErr: "invalid rule: domain, version, and targetType are required",
|
||||
wantErr: "invalid rule: domain is required for version 1.0.0",
|
||||
},
|
||||
{
|
||||
name: "Missing version",
|
||||
@@ -305,7 +305,7 @@ func TestValidateRulesFailure(t *testing.T) {
|
||||
Endpoints: []string{"search", "select"},
|
||||
},
|
||||
},
|
||||
wantErr: "invalid rule: domain, version, and targetType are required",
|
||||
wantErr: "invalid rule: version and targetType are required",
|
||||
},
|
||||
{
|
||||
name: "Missing targetType",
|
||||
@@ -319,7 +319,7 @@ func TestValidateRulesFailure(t *testing.T) {
|
||||
Endpoints: []string{"search", "select"},
|
||||
},
|
||||
},
|
||||
wantErr: "invalid rule: domain, version, and targetType are required",
|
||||
wantErr: "invalid rule: version and targetType are required",
|
||||
},
|
||||
{
|
||||
name: "Invalid targetType",
|
||||
@@ -438,37 +438,37 @@ func TestRouteSuccess(t *testing.T) {
|
||||
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"}}`,
|
||||
body: `{"context": {"domain": "ONDC:TRV10", "version": "1.1.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"}}`,
|
||||
body: `{"context": {"domain": "ONDC:TRV10", "version": "1.1.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"}}`,
|
||||
body: `{"context": {"domain": "ONDC:TRV10", "version": "1.1.0"}}`,
|
||||
},
|
||||
{
|
||||
name: "Valid domain, version, and endpoint (publisher routing)",
|
||||
configFile: "bpp_receiver.yaml",
|
||||
url: "https://example.com/v1/ondc/search",
|
||||
body: `{"context": {"domain": "ONDC:TRV10", "version": "2.0.0"}}`,
|
||||
body: `{"context": {"domain": "ONDC:TRV10", "version": "1.1.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"}}`,
|
||||
body: `{"context": {"domain": "ONDC:TRV10", "version": "1.1.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"}}`,
|
||||
body: `{"context": {"domain": "ONDC:TRV10", "version": "1.1.0", "bpp_uri": "https://bpp1.example.com"}}`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -504,35 +504,35 @@ func TestRouteFailure(t *testing.T) {
|
||||
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",
|
||||
body: `{"context": {"domain": "ONDC:TRV11", "version": "1.1.0"}}`,
|
||||
wantErr: "endpoint 'unsupported' is not supported for domain ONDC:TRV11 and version 1.1.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"}}`,
|
||||
body: `{"context": {"domain": "ONDC:SRV11", "version": "1.1.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"}}`,
|
||||
body: `{"context": {"domain": "ONDC:TRV10", "version": "1.1.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"}}`,
|
||||
body: `{"context": {"domain": "ONDC:TRV10", "version": "1.1.0"}}`,
|
||||
wantErr: "could not determine destination for endpoint 'select': neither request contained a BPP URI nor was a default URL configured in routing rules",
|
||||
},
|
||||
{
|
||||
name: "Invalid bpp_uri format in request",
|
||||
configFile: "bap_caller.yaml",
|
||||
url: "https://example.com/v1/ondc/select",
|
||||
body: `{"context": {"domain": "ONDC:TRV10", "version": "2.0.0", "bpp_uri": "htp:// invalid-url"}}`, // Invalid scheme (htp instead of http)
|
||||
body: `{"context": {"domain": "ONDC:TRV10", "version": "1.1.0", "bpp_uri": "htp:// invalid-url"}}`, // Invalid scheme (htp instead of http)
|
||||
wantErr: `invalid BPP URI - htp:// invalid-url in request body for select: parse "htp:// invalid-url": invalid character " " in host name`,
|
||||
},
|
||||
}
|
||||
@@ -565,7 +565,7 @@ func TestExcludeAction(t *testing.T) {
|
||||
configFile: "exclude_action_true.yaml",
|
||||
expectedRoutes: map[string]map[string]map[string]*model.Route{
|
||||
"ONDC:TRV10": {
|
||||
"2.0.0": {
|
||||
"1.1.0": {
|
||||
"search": {TargetType: targetTypeURL, URL: parseURL(t, "https://services-backend.com/v2/ondc")},
|
||||
"init": {TargetType: targetTypeURL, URL: parseURL(t, "https://services-backend.com/v2/ondc")},
|
||||
},
|
||||
@@ -577,7 +577,7 @@ func TestExcludeAction(t *testing.T) {
|
||||
configFile: "exclude_action_false.yaml",
|
||||
expectedRoutes: map[string]map[string]map[string]*model.Route{
|
||||
"ONDC:TRV10": {
|
||||
"2.0.0": {
|
||||
"1.1.0": {
|
||||
"search": {TargetType: targetTypeURL, URL: parseURL(t, "https://services-backend.com/v2/ondc/search")},
|
||||
"init": {TargetType: targetTypeURL, URL: parseURL(t, "https://services-backend.com/v2/ondc/init")},
|
||||
},
|
||||
@@ -589,7 +589,7 @@ func TestExcludeAction(t *testing.T) {
|
||||
configFile: "exclude_action_default.yaml",
|
||||
expectedRoutes: map[string]map[string]map[string]*model.Route{
|
||||
"ONDC:TRV10": {
|
||||
"2.0.0": {
|
||||
"1.1.0": {
|
||||
"search": {TargetType: targetTypeURL, URL: parseURL(t, "https://services-backend.com/v2/ondc/search")},
|
||||
"init": {TargetType: targetTypeURL, URL: parseURL(t, "https://services-backend.com/v2/ondc/init")},
|
||||
},
|
||||
@@ -630,7 +630,7 @@ func TestExcludeActionWithNonURLTargetTypes(t *testing.T) {
|
||||
configFile: "exclude_action_bpp.yaml",
|
||||
expectedRoutes: map[string]map[string]map[string]*model.Route{
|
||||
"ONDC:TRV10": {
|
||||
"2.0.0": {
|
||||
"1.1.0": {
|
||||
"search": {TargetType: targetTypeBPP, URL: parseURL(t, "https://mock_bpp.com/v2/ondc/search")},
|
||||
"init": {TargetType: targetTypeBPP, URL: parseURL(t, "https://mock_bpp.com/v2/ondc/init")},
|
||||
},
|
||||
@@ -642,7 +642,7 @@ func TestExcludeActionWithNonURLTargetTypes(t *testing.T) {
|
||||
configFile: "exclude_action_bap.yaml",
|
||||
expectedRoutes: map[string]map[string]map[string]*model.Route{
|
||||
"ONDC:TRV10": {
|
||||
"2.0.0": {
|
||||
"1.1.0": {
|
||||
"search": {TargetType: targetTypeBAP, URL: parseURL(t, "https://mock_bap.com/v2/ondc/search")},
|
||||
"init": {TargetType: targetTypeBAP, URL: parseURL(t, "https://mock_bap.com/v2/ondc/init")},
|
||||
},
|
||||
@@ -654,7 +654,7 @@ func TestExcludeActionWithNonURLTargetTypes(t *testing.T) {
|
||||
configFile: "exclude_action_publisher.yaml",
|
||||
expectedRoutes: map[string]map[string]map[string]*model.Route{
|
||||
"ONDC:TRV10": {
|
||||
"2.0.0": {
|
||||
"1.1.0": {
|
||||
"search": {TargetType: targetTypePublisher, PublisherID: "test_topic", URL: nil},
|
||||
"init": {TargetType: targetTypePublisher, PublisherID: "test_topic", URL: nil},
|
||||
},
|
||||
@@ -682,3 +682,112 @@ func TestExcludeActionWithNonURLTargetTypes(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestV2RouteSuccess tests v2 routing with domain-agnostic behavior
|
||||
func TestV2RouteSuccess(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
configFile string
|
||||
url string
|
||||
body string
|
||||
}{
|
||||
{
|
||||
name: "v2 BAP caller - domain ignored",
|
||||
configFile: "v2_bap_caller.yaml",
|
||||
url: "https://example.com/v2/search",
|
||||
body: `{"context": {"domain": "any_domain", "version": "2.0.0"}}`,
|
||||
},
|
||||
{
|
||||
name: "v2 BPP receiver - domain ignored",
|
||||
configFile: "v2_bpp_receiver.yaml",
|
||||
url: "https://example.com/v2/select",
|
||||
body: `{"context": {"domain": "different_domain", "version": "2.0.0"}}`,
|
||||
},
|
||||
{
|
||||
name: "v2 request without domain field",
|
||||
configFile: "v2_bap_caller.yaml",
|
||||
url: "https://example.com/v2/search",
|
||||
body: `{"context": {"version": "2.0.0"}}`,
|
||||
},
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("router.Route() = %v, want nil (domain should be ignored for v2)", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestV2ConflictingRules tests that conflicting v2 rules are detected at load time
|
||||
func TestV2ConflictingRules(t *testing.T) {
|
||||
router := &Router{
|
||||
rules: make(map[string]map[string]map[string]*model.Route),
|
||||
}
|
||||
|
||||
configDir := t.TempDir()
|
||||
conflictingConfig := `routingRules:
|
||||
- version: 2.0.0
|
||||
targetType: bap
|
||||
endpoints:
|
||||
- on_search
|
||||
- version: 2.0.0
|
||||
targetType: bap
|
||||
endpoints:
|
||||
- on_search
|
||||
`
|
||||
rulesPath := filepath.Join(configDir, "conflicting_rules.yaml")
|
||||
if err := os.WriteFile(rulesPath, []byte(conflictingConfig), 0644); err != nil {
|
||||
t.Fatalf("WriteFile() err = %v, want nil", err)
|
||||
}
|
||||
defer os.RemoveAll(configDir)
|
||||
|
||||
err := router.loadRules(rulesPath)
|
||||
if err == nil {
|
||||
t.Error("loadRules() with conflicting v2 rules should return error, got nil")
|
||||
}
|
||||
|
||||
expectedErr := "duplicate endpoint 'on_search' found for version 2.0.0"
|
||||
if err != nil && !strings.Contains(err.Error(), expectedErr) {
|
||||
t.Errorf("loadRules() error = %v, want error containing %q", err, expectedErr)
|
||||
}
|
||||
}
|
||||
|
||||
// TestV1DomainRequired tests that domain is required for v1 configs
|
||||
func TestV1DomainRequired(t *testing.T) {
|
||||
router := &Router{
|
||||
rules: make(map[string]map[string]map[string]*model.Route),
|
||||
}
|
||||
|
||||
configDir := t.TempDir()
|
||||
v1ConfigWithoutDomain := `routingRules:
|
||||
- version: 1.0.0
|
||||
targetType: bap
|
||||
endpoints:
|
||||
- on_search
|
||||
`
|
||||
rulesPath := filepath.Join(configDir, "v1_no_domain.yaml")
|
||||
if err := os.WriteFile(rulesPath, []byte(v1ConfigWithoutDomain), 0644); err != nil {
|
||||
t.Fatalf("WriteFile() err = %v, want nil", err)
|
||||
}
|
||||
defer os.RemoveAll(configDir)
|
||||
|
||||
err := router.loadRules(rulesPath)
|
||||
if err == nil {
|
||||
t.Error("loadRules() with v1 config without domain should fail, got nil")
|
||||
}
|
||||
|
||||
expectedErr := "invalid rule: domain is required for version 1.0.0"
|
||||
if err != nil && !strings.Contains(err.Error(), expectedErr) {
|
||||
t.Errorf("loadRules() error = %v, want error containing %q", err, expectedErr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
routingRules:
|
||||
- domain: ONDC:TRV10
|
||||
version: 2.0.0
|
||||
version: 1.1.0
|
||||
targetType: bpp
|
||||
target:
|
||||
url: https://gateway.example.com
|
||||
endpoints:
|
||||
- search
|
||||
- domain: ONDC:TRV10
|
||||
version: 2.0.0
|
||||
version: 1.1.0
|
||||
targetType: bpp
|
||||
endpoints:
|
||||
- select
|
||||
@@ -16,7 +16,7 @@ routingRules:
|
||||
- status
|
||||
- cancel
|
||||
- domain: ONDC:TRV12
|
||||
version: 2.0.0
|
||||
version: 1.1.0
|
||||
targetType: bpp
|
||||
endpoints:
|
||||
- select
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
routingRules:
|
||||
- domain: ONDC:TRV10
|
||||
version: 2.0.0
|
||||
version: 1.1.0
|
||||
targetType: url
|
||||
target:
|
||||
url: https://services-backend/trv/v1
|
||||
@@ -12,7 +12,7 @@ routingRules:
|
||||
- on_update
|
||||
- on_cancel
|
||||
- domain: ONDC:TRV10
|
||||
version: 2.0.0
|
||||
version: 1.1.0
|
||||
targetType: publisher
|
||||
target:
|
||||
publisherId: trv_topic_id1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
routingRules:
|
||||
- domain: ONDC:TRV10
|
||||
version: 2.0.0
|
||||
version: 1.1.0
|
||||
targetType: bap
|
||||
endpoints:
|
||||
- on_search
|
||||
@@ -11,7 +11,7 @@ routingRules:
|
||||
- on_update
|
||||
- on_cancel
|
||||
- domain: ONDC:TRV11
|
||||
version: 2.0.0
|
||||
version: 1.1.0
|
||||
targetType: bap
|
||||
endpoints:
|
||||
- on_search
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
routingRules:
|
||||
- domain: ONDC:TRV10
|
||||
version: 2.0.0
|
||||
version: 1.1.0
|
||||
targetType: url
|
||||
target:
|
||||
url: https://services-backend/trv/v1
|
||||
@@ -11,14 +11,14 @@ routingRules:
|
||||
- status
|
||||
- cancel
|
||||
- domain: ONDC:TRV10
|
||||
version: 2.0.0
|
||||
version: 1.1.0
|
||||
targetType: publisher
|
||||
target:
|
||||
publisherId: trv_topic_id1
|
||||
endpoints:
|
||||
- search
|
||||
- domain: ONDC:TRV11
|
||||
version: 2.0.0
|
||||
version: 1.1.0
|
||||
targetType: url
|
||||
target:
|
||||
url: https://services-backend/trv/v1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
routingRules:
|
||||
- domain: ONDC:TRV10
|
||||
version: 2.0.0
|
||||
version: 1.1.0
|
||||
targetType: bap
|
||||
target:
|
||||
url: https://mock_bap.com/v2/ondc
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
routingRules:
|
||||
- domain: ONDC:TRV10
|
||||
version: 2.0.0
|
||||
version: 1.1.0
|
||||
targetType: bpp
|
||||
target:
|
||||
url: https://mock_bpp.com/v2/ondc
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
routingRules:
|
||||
- domain: ONDC:TRV10
|
||||
version: 2.0.0
|
||||
version: 1.1.0
|
||||
targetType: url
|
||||
target:
|
||||
url: https://services-backend.com/v2/ondc
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
routingRules:
|
||||
- domain: ONDC:TRV10
|
||||
version: 2.0.0
|
||||
version: 1.1.0
|
||||
targetType: url
|
||||
target:
|
||||
url: https://services-backend.com/v2/ondc
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
routingRules:
|
||||
- domain: ONDC:TRV10
|
||||
version: 2.0.0
|
||||
version: 1.1.0
|
||||
targetType: publisher
|
||||
target:
|
||||
publisherId: test_topic
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
routingRules:
|
||||
- domain: ONDC:TRV10
|
||||
version: 2.0.0
|
||||
version: 1.1.0
|
||||
targetType: url
|
||||
target:
|
||||
url: https://services-backend.com/v2/ondc
|
||||
|
||||
15
pkg/plugin/implementation/router/testData/v2_bap_caller.yaml
Normal file
15
pkg/plugin/implementation/router/testData/v2_bap_caller.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
routingRules:
|
||||
- version: 2.0.0
|
||||
targetType: bpp
|
||||
target:
|
||||
url: https://gateway.example.com
|
||||
endpoints:
|
||||
- search
|
||||
- version: 2.0.0
|
||||
targetType: bpp
|
||||
endpoints:
|
||||
- select
|
||||
- init
|
||||
- confirm
|
||||
- status
|
||||
- cancel
|
||||
@@ -0,0 +1,17 @@
|
||||
routingRules:
|
||||
- version: 2.0.0
|
||||
targetType: url
|
||||
target:
|
||||
url: https://services-backend/trv/v2
|
||||
endpoints:
|
||||
- select
|
||||
- init
|
||||
- confirm
|
||||
- status
|
||||
- cancel
|
||||
- version: 2.0.0
|
||||
targetType: publisher
|
||||
target:
|
||||
publisherId: trv_topic_v2
|
||||
endpoints:
|
||||
- search
|
||||
@@ -1,14 +1,14 @@
|
||||
# testData/all_route_types.yaml
|
||||
routingRules:
|
||||
- domain: ONDC:TRV10
|
||||
version: 2.0.0
|
||||
version: 1.1.0
|
||||
targetType: url
|
||||
target:
|
||||
url: https://mock_gateway.com/v2/ondc
|
||||
endpoints:
|
||||
- search
|
||||
- domain: ONDC:TRV10
|
||||
version: 2.0.0
|
||||
version: 1.1.0
|
||||
targetType: bap
|
||||
target:
|
||||
url: https://mock_bpp.com/v2/ondc
|
||||
@@ -16,14 +16,14 @@ routingRules:
|
||||
- init
|
||||
- select
|
||||
- domain: ONDC:TRV10
|
||||
version: 2.0.0
|
||||
version: 1.1.0
|
||||
targetType: publisher
|
||||
target:
|
||||
publisherId: beckn_onix_topic
|
||||
endpoints:
|
||||
- confirm
|
||||
- domain: ONDC:TRV10
|
||||
version: 2.0.0
|
||||
version: 1.1.0
|
||||
targetType: bap
|
||||
target:
|
||||
url: https://mock_bap_gateway.com/v2/ondc
|
||||
|
||||
Reference in New Issue
Block a user