Merge pull request #545 from Beckn-One/540-dedi-wrapper
Issue 540 - fix: remove hardcoded value and add test cases
This commit is contained in:
@@ -1,162 +1,142 @@
|
|||||||
# DeDi Registry Plugin
|
# DeDi Registry Plugin
|
||||||
|
|
||||||
A **registry type plugin** for Beckn-ONIX that integrates with DeDi (Decentralized Digital Infrastructure) registry services.
|
A **registry type plugin** for Beckn-ONIX that integrates with DeDi (Decentralized Digital Infrastructure) registry services via the new DeDi Wrapper API.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The DeDi Registry plugin is a **registry implementation** that enables Beckn-ONIX to lookup participant records from remote DeDi registries via REST API calls.
|
The DeDi Registry plugin implements the `RegistryLookup` interface to retrieve public keys and participant information from DeDi registry services. It's used by the KeyManager for **signature validation of incoming requests** from other network participants.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
plugins:
|
registry:
|
||||||
registry:
|
|
||||||
id: dediregistry
|
id: dediregistry
|
||||||
config:
|
config:
|
||||||
baseURL: "https://dedi-api.example.com"
|
url: "https://dedi-wrapper.example.com/dedi"
|
||||||
apiKey: "your-bearer-token"
|
registryName: "subscribers.beckn.one"
|
||||||
namespaceID: "76EU8BF1gzRGGatgw7wZZb7nEVx77XSwkKDv4UDLdxh8ztty4zmbYU"
|
timeout: 30
|
||||||
registryName: "dedi_registry"
|
|
||||||
timeout: "30" # seconds
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Configuration Parameters
|
### Configuration Parameters
|
||||||
|
|
||||||
| Parameter | Required | Description | Default |
|
| Parameter | Required | Description | Default |
|
||||||
|-----------|----------|-------------|---------|
|
|-----------|----------|-------------|---------|
|
||||||
| `baseURL` | Yes | DeDi registry API base URL | - |
|
| `url` | Yes | DeDi wrapper API base URL (include /dedi path) | - |
|
||||||
| `apiKey` | Yes | Bearer token for API authentication | - |
|
| `registryName` | Yes | Registry name for lookup path | - |
|
||||||
| `namespaceID` | Yes | DeDi namespace identifier | - |
|
| `timeout` | No | Request timeout in seconds | Client default |
|
||||||
| `registryName` | Yes | Registry name to query | - |
|
|
||||||
| `timeout` | No | Request timeout in seconds | 30 |
|
|
||||||
|
|
||||||
## Usage
|
## API Integration
|
||||||
|
|
||||||
### In Module Configuration
|
### DeDi Wrapper API Format
|
||||||
|
```
|
||||||
|
GET {url}/lookup/{subscriber_id}/{registryName}/{key_id}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example**: `https://dedi-wrapper.com/dedi/lookup/bpp.example.com/subscribers.beckn.one/key-1`
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
**No authentication required** - DeDi wrapper API is public.
|
||||||
|
|
||||||
|
### Expected Response Format
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message": "Record retrieved from registry cache",
|
||||||
|
"data": {
|
||||||
|
"record_id": "76EU8vY9TkuJ9T62Sc3FyQLf5Kt9YAVgbZhryX6mFi56ipefkP9d9a",
|
||||||
|
"details": {
|
||||||
|
"url": "http://dev.np2.com/beckn/bap",
|
||||||
|
"type": "BAP",
|
||||||
|
"domain": "energy",
|
||||||
|
"subscriber_id": "dev.np2.com",
|
||||||
|
"signing_public_key": "384qqkIIpxo71WaJPsWqQNWUDGAFnfnJPxuDmtuBiLo=",
|
||||||
|
"encr_public_key": "test-encr-key"
|
||||||
|
},
|
||||||
|
"created_at": "2025-10-27T11:45:27.963Z",
|
||||||
|
"updated_at": "2025-10-27T11:46:23.563Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage Context
|
||||||
|
|
||||||
|
### Signature Validation Flow
|
||||||
|
```
|
||||||
|
1. External ONIX → Request with Authorization header
|
||||||
|
2. ONIX Receiver → parseHeader() extracts subscriberID/keyID
|
||||||
|
3. validateSign step → KeyManager.LookupNPKeys()
|
||||||
|
4. KeyManager → DeDiRegistry.Lookup() with extracted values
|
||||||
|
5. DeDi Registry → GET {url}/lookup/{subscriberID}/{registryName}/{keyID}
|
||||||
|
6. DeDi Wrapper → Returns participant public keys
|
||||||
|
7. SignValidator → Validates signature using retrieved public key
|
||||||
|
```
|
||||||
|
|
||||||
|
### Module Configuration Example
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
modules:
|
modules:
|
||||||
- name: bapTxnReceiver
|
- name: bppTxnReceiver
|
||||||
handler:
|
handler:
|
||||||
plugins:
|
plugins:
|
||||||
registry:
|
registry:
|
||||||
id: dediregistry
|
id: dediregistry
|
||||||
config:
|
config:
|
||||||
baseURL: "https://dedi-registry.example.com"
|
url: "https://dedi-wrapper.example.com/dedi"
|
||||||
apiKey: "your-api-key"
|
registryName: "subscribers.beckn.one"
|
||||||
namespaceID: "beckn-network"
|
timeout: 30
|
||||||
registryName: "participants"
|
steps:
|
||||||
|
- validateSign # Required for registry lookup
|
||||||
|
- addRoute
|
||||||
```
|
```
|
||||||
|
|
||||||
### In Code
|
## Field Mapping
|
||||||
|
|
||||||
```go
|
| DeDi Wrapper Field | Beckn Field | Description |
|
||||||
// Load DeDi registry plugin (same as any registry plugin)
|
|-------------------|-------------|-------------|
|
||||||
dediRegistry, err := manager.Registry(ctx, &plugin.Config{
|
| `data.details.subscriber_id` | `subscriber_id` | Participant identifier |
|
||||||
ID: "dediregistry", // Plugin ID specifies DeDi implementation
|
| `{key_id from URL}` | `key_id` | Unique key identifier |
|
||||||
Config: map[string]string{
|
|
||||||
"baseURL": "https://dedi-registry.example.com",
|
|
||||||
"apiKey": "your-api-key",
|
|
||||||
"namespaceID": "beckn-network",
|
|
||||||
"registryName": "participants",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
// Lookup participant with dynamic subscriber ID (from request context)
|
|
||||||
subscription := &model.Subscription{
|
|
||||||
Subscriber: model.Subscriber{
|
|
||||||
SubscriberID: "bap-network", // Extracted from Authorization header or request body
|
|
||||||
},
|
|
||||||
}
|
|
||||||
results, err := dediRegistry.Lookup(ctx, subscription)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract public key from result (standard Beckn format)
|
|
||||||
if len(results) > 0 {
|
|
||||||
publicKey := results[0].SigningPublicKey
|
|
||||||
subscriberID := results[0].SubscriberID
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Integration
|
|
||||||
|
|
||||||
### DeDi API URL Pattern
|
|
||||||
```
|
|
||||||
{baseURL}/dedi/lookup/{namespaceID}/{registryName}/{subscriberID}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Example**: `https://dedi-api.com/dedi/lookup/76EU8BF1gzRGGatgw7wZZb7nEVx77XSwkKDv4UDLdxh8ztty4zmbYU/dedi_registry/bap-network`
|
|
||||||
|
|
||||||
### Authentication
|
|
||||||
```
|
|
||||||
Authorization: Bearer {apiKey}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Expected DeDi Response Format
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"message": "Resource retrieved successfully",
|
|
||||||
"data": {
|
|
||||||
"namespace": "dediregistry",
|
|
||||||
"namespace_id": "76EU8BF1gzRGGatgw7wZZb7nEVx77XSwkKDv4UDLdxh8ztty4zmbYU",
|
|
||||||
"registry_name": "dedi_registry",
|
|
||||||
"record_name": "bap-network",
|
|
||||||
"details": {
|
|
||||||
"key_id": "b692d295-5425-40f5-af77-d62646841dca",
|
|
||||||
"signing_public_key": "YK3Xqc83Bpobc1UT0ObAe6mBJMiAOkceIsNtmph9WTc=",
|
|
||||||
"encr_public_key": "YK3Xqc83Bpobc1UT0ObAe6mBJMiAOkceIsNtmph9WTc=",
|
|
||||||
"status": "SUBSCRIBED",
|
|
||||||
"created": "2024-01-15T10:00:00Z",
|
|
||||||
"updated": "2024-01-15T10:00:00Z",
|
|
||||||
"valid_from": "2024-01-01T00:00:00Z",
|
|
||||||
"valid_until": "2025-12-31T23:59:59Z"
|
|
||||||
},
|
|
||||||
"state": "live",
|
|
||||||
"created_at": "2025-10-09T06:09:48.295Z"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Field Mapping to Beckn Subscription
|
|
||||||
|
|
||||||
| DeDi Field | Beckn Field | Description |
|
|
||||||
|------------|-------------|-------------|
|
|
||||||
| `data.record_name` | `subscriber_id` | Participant identifier |
|
|
||||||
| `data.details.key_id` | `key_id` | Unique key identifier |
|
|
||||||
| `data.details.signing_public_key` | `signing_public_key` | Public key for signature verification |
|
| `data.details.signing_public_key` | `signing_public_key` | Public key for signature verification |
|
||||||
| `data.details.encr_public_key` | `encr_public_key` | Public key for encryption |
|
| `data.details.encr_public_key` | `encr_public_key` | Public key for encryption |
|
||||||
| `data.details.status` | `status` | Subscription status |
|
| `data.is_revoked` | `status` | Not mapped (Status field will be empty) |
|
||||||
| `data.details.created` | `created` | Creation timestamp |
|
| `data.created_at` | `created` | Creation timestamp |
|
||||||
| `data.details.updated` | `updated` | Last update timestamp |
|
| `data.updated_at` | `updated` | Last update timestamp |
|
||||||
| `data.details.valid_from` | `valid_from` | Key validity start |
|
|
||||||
| `data.details.valid_until` | `valid_until` | Key validity end |
|
|
||||||
|
|
||||||
### Lookup Flow
|
## Features
|
||||||
|
|
||||||
1. **Request Processing**: Plugin extracts `subscriber_id` from incoming request
|
- **No Authentication Required**: DeDi wrapper API doesn't require API keys
|
||||||
2. **API Call**: Makes GET request to DeDi API with static config + dynamic subscriber ID
|
- **GET Request Format**: Simple URL-based parameter passing
|
||||||
3. **Response Parsing**: Extracts data from `data.details` object
|
- **Comprehensive Error Handling**: Validates required fields and HTTP responses
|
||||||
4. **Format Conversion**: Maps DeDi fields to Beckn Subscription format
|
- **Simplified Response**: Focuses on public key retrieval for signature validation
|
||||||
5. **Return**: Returns array of Subscription objects
|
- **Retry Support**: Built-in retry mechanism for network resilience
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
Run plugin tests:
|
Run the test suite:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go test ./pkg/plugin/implementation/dediregistry -v
|
go test ./pkg/plugin/implementation/dediregistry -v
|
||||||
```
|
```
|
||||||
|
|
||||||
Test coverage includes:
|
The tests cover:
|
||||||
|
- URL construction validation
|
||||||
|
- Response parsing for new API format
|
||||||
|
- Error handling scenarios
|
||||||
- Configuration validation
|
- Configuration validation
|
||||||
- Successful API responses
|
- Plugin provider functionality
|
||||||
- HTTP error handling
|
|
||||||
- Network failures
|
## Migration Notes
|
||||||
- Invalid JSON responses
|
|
||||||
- Missing required fields
|
This plugin replaces direct DeDi API integration with the new DeDi Wrapper API format:
|
||||||
|
|
||||||
|
- **Removed**: API key authentication, namespaceID parameters
|
||||||
|
- **Added**: Configurable registryName parameter
|
||||||
|
- **Changed**: POST requests → GET requests
|
||||||
|
- **Updated**: Response structure parsing (`data.details` object)
|
||||||
|
- **Added**: New URL path parameter format
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
@@ -165,19 +145,8 @@ Test coverage includes:
|
|||||||
|
|
||||||
## Error Handling
|
## Error Handling
|
||||||
|
|
||||||
- **Configuration Errors**: Missing required config parameters
|
- **Configuration Errors**: Missing url or registryName
|
||||||
- **Network Errors**: Connection failures, timeouts
|
- **Network Errors**: Connection failures, timeouts
|
||||||
- **HTTP Errors**: Non-200 status codes from DeDi API
|
- **HTTP Errors**: Non-200 status codes from DeDi wrapper
|
||||||
- **Data Errors**: Missing required fields in response
|
- **Data Errors**: Missing required fields in response
|
||||||
- **Validation Errors**: Empty subscriber ID in request
|
- **Validation Errors**: Empty subscriber ID or key ID in request
|
||||||
|
|
||||||
|
|
||||||
### Integration Notes
|
|
||||||
|
|
||||||
- **Plugin Type**: Registry implementation
|
|
||||||
- **Interface**: Implements `RegistryLookup` interface with `Lookup(ctx, *model.Subscription) ([]model.Subscription, error)`
|
|
||||||
- **Manager Access**: Available via `manager.Registry()` method (same as standard registry)
|
|
||||||
- **Dynamic Lookup**: Uses `req.SubscriberID` from request context, not static configuration
|
|
||||||
- **Data Conversion**: Automatically converts DeDi API format to Beckn Subscription format
|
|
||||||
- **Build Integration**: Included in `build-plugins.sh`, compiles to `dediregistry.so`
|
|
||||||
- **Usage Pattern**: Configure with `id: dediregistry` in registry plugin configuration
|
|
||||||
@@ -21,7 +21,8 @@ func (d dediRegistryProvider) New(ctx context.Context, config map[string]string)
|
|||||||
|
|
||||||
// Create dediregistry.Config directly from map - validation is handled by dediregistry.New
|
// Create dediregistry.Config directly from map - validation is handled by dediregistry.New
|
||||||
dediConfig := &dediregistry.Config{
|
dediConfig := &dediregistry.Config{
|
||||||
BaseURL: config["baseURL"],
|
URL: config["url"],
|
||||||
|
RegistryName: config["registryName"],
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse timeout if provided
|
// Parse timeout if provided
|
||||||
|
|||||||
@@ -10,11 +10,8 @@ func TestDediRegistryProvider_New(t *testing.T) {
|
|||||||
provider := dediRegistryProvider{}
|
provider := dediRegistryProvider{}
|
||||||
|
|
||||||
config := map[string]string{
|
config := map[string]string{
|
||||||
"baseURL": "https://test.com",
|
"url": "https://test.com/dedi",
|
||||||
"apiKey": "test-key",
|
"registryName": "subscribers.beckn.one",
|
||||||
"namespaceID": "test-namespace",
|
|
||||||
"registryName": "test-registry",
|
|
||||||
"recordName": "test-record",
|
|
||||||
"timeout": "30",
|
"timeout": "30",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,12 +44,12 @@ func TestDediRegistryProvider_New_InvalidConfig(t *testing.T) {
|
|||||||
config map[string]string
|
config map[string]string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "missing baseURL",
|
name: "missing url",
|
||||||
config: map[string]string{"apiKey": "test-key"},
|
config: map[string]string{"registryName": "subscribers.beckn.one", "timeout": "30"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "missing apiKey",
|
name: "missing registryName",
|
||||||
config: map[string]string{"baseURL": "https://test.com"},
|
config: map[string]string{"url": "https://test.com/dedi", "timeout": "30"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty config",
|
name: "empty config",
|
||||||
@@ -75,11 +72,8 @@ func TestDediRegistryProvider_New_InvalidTimeout(t *testing.T) {
|
|||||||
provider := dediRegistryProvider{}
|
provider := dediRegistryProvider{}
|
||||||
|
|
||||||
config := map[string]string{
|
config := map[string]string{
|
||||||
"baseURL": "https://test.com",
|
"url": "https://test.com/dedi",
|
||||||
"apiKey": "test-key",
|
"registryName": "subscribers.beckn.one",
|
||||||
"namespaceID": "test-namespace",
|
|
||||||
"registryName": "test-registry",
|
|
||||||
"recordName": "test-record",
|
|
||||||
"timeout": "invalid",
|
"timeout": "invalid",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,3 +89,20 @@ func TestDediRegistryProvider_New_InvalidTimeout(t *testing.T) {
|
|||||||
closer()
|
closer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDediRegistryProvider_New_NilContext(t *testing.T) {
|
||||||
|
provider := dediRegistryProvider{}
|
||||||
|
|
||||||
|
config := map[string]string{
|
||||||
|
"url": "https://test.com/dedi",
|
||||||
|
"registryName": "subscribers.beckn.one",
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err := provider.New(nil, config)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("New() with nil context should return error")
|
||||||
|
}
|
||||||
|
if err.Error() != "context cannot be nil" {
|
||||||
|
t.Errorf("Expected specific error message, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ import (
|
|||||||
|
|
||||||
// Config holds configuration parameters for the DeDi registry client.
|
// Config holds configuration parameters for the DeDi registry client.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
BaseURL string `yaml:"baseURL" json:"baseURL"`
|
URL string `yaml:"url" json:"url"`
|
||||||
|
RegistryName string `yaml:"registryName" json:"registryName"`
|
||||||
Timeout int `yaml:"timeout" json:"timeout"`
|
Timeout int `yaml:"timeout" json:"timeout"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,8 +31,11 @@ func validate(cfg *Config) error {
|
|||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return fmt.Errorf("DeDi registry config cannot be nil")
|
return fmt.Errorf("DeDi registry config cannot be nil")
|
||||||
}
|
}
|
||||||
if cfg.BaseURL == "" {
|
if cfg.URL == "" {
|
||||||
return fmt.Errorf("baseURL cannot be empty")
|
return fmt.Errorf("url cannot be empty")
|
||||||
|
}
|
||||||
|
if cfg.RegistryName == "" {
|
||||||
|
return fmt.Errorf("registryName cannot be empty")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -82,8 +86,8 @@ func (c *DeDiRegistryClient) Lookup(ctx context.Context, req *model.Subscription
|
|||||||
return nil, fmt.Errorf("key_id is required for DeDi lookup")
|
return nil, fmt.Errorf("key_id is required for DeDi lookup")
|
||||||
}
|
}
|
||||||
|
|
||||||
lookupURL := fmt.Sprintf("%s/dedi/lookup/%s/subscribers.beckn.one/%s",
|
lookupURL := fmt.Sprintf("%s/lookup/%s/%s/%s",
|
||||||
c.config.BaseURL, subscriberID, keyID)
|
c.config.URL, subscriberID, c.config.RegistryName, keyID)
|
||||||
|
|
||||||
httpReq, err := retryablehttp.NewRequest("GET", lookupURL, nil)
|
httpReq, err := retryablehttp.NewRequest("GET", lookupURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -151,13 +155,6 @@ func (c *DeDiRegistryClient) Lookup(ctx context.Context, req *model.Subscription
|
|||||||
// Extract fields from data level
|
// Extract fields from data level
|
||||||
createdAt, _ := data["created_at"].(string)
|
createdAt, _ := data["created_at"].(string)
|
||||||
updatedAt, _ := data["updated_at"].(string)
|
updatedAt, _ := data["updated_at"].(string)
|
||||||
isRevoked, _ := data["is_revoked"].(bool)
|
|
||||||
|
|
||||||
// Determine status from is_revoked flag
|
|
||||||
status := "SUBSCRIBED"
|
|
||||||
if isRevoked {
|
|
||||||
status = "UNSUBSCRIBED"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to Subscription format
|
// Convert to Subscription format
|
||||||
subscription := model.Subscription{
|
subscription := model.Subscription{
|
||||||
@@ -170,7 +167,6 @@ func (c *DeDiRegistryClient) Lookup(ctx context.Context, req *model.Subscription
|
|||||||
KeyID: keyID, // Use original keyID from request
|
KeyID: keyID, // Use original keyID from request
|
||||||
SigningPublicKey: signingPublicKey,
|
SigningPublicKey: signingPublicKey,
|
||||||
EncrPublicKey: encrPublicKey, // May be empty if not provided
|
EncrPublicKey: encrPublicKey, // May be empty if not provided
|
||||||
Status: status,
|
|
||||||
Created: parseTime(createdAt),
|
Created: parseTime(createdAt),
|
||||||
Updated: parseTime(updatedAt),
|
Updated: parseTime(updatedAt),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,55 +22,29 @@ func TestValidate(t *testing.T) {
|
|||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty baseURL",
|
name: "empty url",
|
||||||
config: &Config{
|
config: &Config{
|
||||||
BaseURL: "",
|
URL: "",
|
||||||
ApiKey: "test-key",
|
|
||||||
NamespaceID: "test-namespace",
|
|
||||||
RegistryName: "test-registry",
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "empty apiKey",
|
|
||||||
config: &Config{
|
|
||||||
BaseURL: "https://test.com",
|
|
||||||
ApiKey: "",
|
|
||||||
NamespaceID: "test-namespace",
|
|
||||||
RegistryName: "test-registry",
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "empty namespaceID",
|
|
||||||
config: &Config{
|
|
||||||
BaseURL: "https://test.com",
|
|
||||||
ApiKey: "test-key",
|
|
||||||
NamespaceID: "",
|
|
||||||
RegistryName: "test-registry",
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "empty registryName",
|
|
||||||
config: &Config{
|
|
||||||
BaseURL: "https://test.com",
|
|
||||||
ApiKey: "test-key",
|
|
||||||
NamespaceID: "test-namespace",
|
|
||||||
RegistryName: "",
|
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid config",
|
name: "valid config",
|
||||||
config: &Config{
|
config: &Config{
|
||||||
BaseURL: "https://test.com",
|
URL: "https://test.com/dedi",
|
||||||
ApiKey: "test-key",
|
RegistryName: "subscribers.beckn.one",
|
||||||
NamespaceID: "test-namespace",
|
Timeout: 30,
|
||||||
RegistryName: "test-registry",
|
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "missing registry name",
|
||||||
|
config: &Config{
|
||||||
|
URL: "https://test.com/dedi",
|
||||||
|
Timeout: 30,
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@@ -87,10 +61,8 @@ func TestNew(t *testing.T) {
|
|||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
validConfig := &Config{
|
validConfig := &Config{
|
||||||
BaseURL: "https://test.com",
|
URL: "https://test.com/dedi",
|
||||||
ApiKey: "test-key",
|
RegistryName: "subscribers.beckn.one",
|
||||||
NamespaceID: "test-namespace",
|
|
||||||
RegistryName: "test-registry",
|
|
||||||
Timeout: 30,
|
Timeout: 30,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,32 +97,26 @@ func TestLookup(t *testing.T) {
|
|||||||
if r.Method != "GET" {
|
if r.Method != "GET" {
|
||||||
t.Errorf("Expected GET request, got %s", r.Method)
|
t.Errorf("Expected GET request, got %s", r.Method)
|
||||||
}
|
}
|
||||||
if r.URL.Path != "/dedi/lookup/test-namespace/test-registry/bap-network" {
|
if r.URL.Path != "/dedi/lookup/dev.np2.com/subscribers.beckn.one/test-key-id" {
|
||||||
t.Errorf("Unexpected path: %s", r.URL.Path)
|
t.Errorf("Unexpected path: %s", r.URL.Path)
|
||||||
}
|
}
|
||||||
// Verify Authorization header
|
// No authorization header expected
|
||||||
if auth := r.Header.Get("Authorization"); auth != "Bearer test-key" {
|
|
||||||
t.Errorf("Expected Bearer test-key, got %s", auth)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return mock response using actual DeDI format
|
// Return mock response using new DeDi wrapper format
|
||||||
response := map[string]interface{}{
|
response := map[string]interface{}{
|
||||||
"message": "Resource retrieved successfully",
|
"message": "Record retrieved from registry cache",
|
||||||
"data": map[string]interface{}{
|
"data": map[string]interface{}{
|
||||||
"record_name": "bap-network",
|
"record_id": "76EU8vY9TkuJ9T62Sc3FyQLf5Kt9YAVgbZhryX6mFi56ipefkP9d9a",
|
||||||
"details": map[string]interface{}{
|
"details": map[string]interface{}{
|
||||||
"key_id": "b692d295-5425-40f5-af77-d62646841dca",
|
"url": "http://dev.np2.com/beckn/bap",
|
||||||
"signing_public_key": "test-public-key",
|
"type": "BAP",
|
||||||
|
"domain": "energy",
|
||||||
|
"subscriber_id": "dev.np2.com",
|
||||||
|
"signing_public_key": "384qqkIIpxo71WaJPsWqQNWUDGAFnfnJPxuDmtuBiLo=",
|
||||||
"encr_public_key": "test-encr-key",
|
"encr_public_key": "test-encr-key",
|
||||||
"status": "SUBSCRIBED",
|
|
||||||
"created": "2023-01-01T00:00:00Z",
|
|
||||||
"updated": "2023-01-01T00:00:00Z",
|
|
||||||
"valid_from": "2023-01-01T00:00:00Z",
|
|
||||||
"valid_until": "2024-01-01T00:00:00Z",
|
|
||||||
},
|
},
|
||||||
"state": "live",
|
"created_at": "2025-10-27T11:45:27.963Z",
|
||||||
"created_at": "2023-01-01T00:00:00Z",
|
"updated_at": "2025-10-27T11:46:23.563Z",
|
||||||
"updated_at": "2023-01-01T00:00:00Z",
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
@@ -159,10 +125,8 @@ func TestLookup(t *testing.T) {
|
|||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
config := &Config{
|
config := &Config{
|
||||||
BaseURL: server.URL,
|
URL: server.URL + "/dedi",
|
||||||
ApiKey: "test-key",
|
RegistryName: "subscribers.beckn.one",
|
||||||
NamespaceID: "test-namespace",
|
|
||||||
RegistryName: "test-registry",
|
|
||||||
Timeout: 30,
|
Timeout: 30,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,8 +138,9 @@ func TestLookup(t *testing.T) {
|
|||||||
|
|
||||||
req := &model.Subscription{
|
req := &model.Subscription{
|
||||||
Subscriber: model.Subscriber{
|
Subscriber: model.Subscriber{
|
||||||
SubscriberID: "bap-network",
|
SubscriberID: "dev.np2.com",
|
||||||
},
|
},
|
||||||
|
KeyID: "test-key-id",
|
||||||
}
|
}
|
||||||
results, err := client.Lookup(ctx, req)
|
results, err := client.Lookup(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -189,24 +154,23 @@ func TestLookup(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
subscription := results[0]
|
subscription := results[0]
|
||||||
if subscription.Subscriber.SubscriberID != "bap-network" {
|
if subscription.Subscriber.SubscriberID != "dev.np2.com" {
|
||||||
t.Errorf("Expected subscriber_id bap-network, got %s", subscription.Subscriber.SubscriberID)
|
t.Errorf("Expected subscriber_id dev.np2.com, got %s", subscription.Subscriber.SubscriberID)
|
||||||
}
|
}
|
||||||
if subscription.SigningPublicKey != "test-public-key" {
|
if subscription.SigningPublicKey != "384qqkIIpxo71WaJPsWqQNWUDGAFnfnJPxuDmtuBiLo=" {
|
||||||
t.Errorf("Expected signing_public_key test-public-key, got %s", subscription.SigningPublicKey)
|
t.Errorf("Expected signing_public_key 384qqkIIpxo71WaJPsWqQNWUDGAFnfnJPxuDmtuBiLo=, got %s", subscription.SigningPublicKey)
|
||||||
}
|
}
|
||||||
if subscription.Status != "SUBSCRIBED" {
|
|
||||||
t.Errorf("Expected status SUBSCRIBED, got %s", subscription.Status)
|
if subscription.KeyID != "test-key-id" {
|
||||||
|
t.Errorf("Expected keyID test-key-id, got %s", subscription.KeyID)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Test empty subscriber ID
|
// Test empty subscriber ID
|
||||||
t.Run("empty subscriber ID", func(t *testing.T) {
|
t.Run("empty subscriber ID", func(t *testing.T) {
|
||||||
config := &Config{
|
config := &Config{
|
||||||
BaseURL: "https://test.com",
|
URL: "https://test.com/dedi",
|
||||||
ApiKey: "test-key",
|
RegistryName: "subscribers.beckn.one",
|
||||||
NamespaceID: "test-namespace",
|
|
||||||
RegistryName: "test-registry",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client, closer, err := New(ctx, config)
|
client, closer, err := New(ctx, config)
|
||||||
@@ -219,6 +183,7 @@ func TestLookup(t *testing.T) {
|
|||||||
Subscriber: model.Subscriber{
|
Subscriber: model.Subscriber{
|
||||||
SubscriberID: "",
|
SubscriberID: "",
|
||||||
},
|
},
|
||||||
|
KeyID: "test-key-id",
|
||||||
}
|
}
|
||||||
_, err = client.Lookup(ctx, req)
|
_, err = client.Lookup(ctx, req)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -229,6 +194,34 @@ func TestLookup(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Test empty key ID
|
||||||
|
t.Run("empty key ID", func(t *testing.T) {
|
||||||
|
config := &Config{
|
||||||
|
URL: "https://test.com/dedi",
|
||||||
|
RegistryName: "subscribers.beckn.one",
|
||||||
|
}
|
||||||
|
|
||||||
|
client, closer, err := New(ctx, config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("New() error = %v", err)
|
||||||
|
}
|
||||||
|
defer closer()
|
||||||
|
|
||||||
|
req := &model.Subscription{
|
||||||
|
Subscriber: model.Subscriber{
|
||||||
|
SubscriberID: "dev.np2.com",
|
||||||
|
},
|
||||||
|
KeyID: "",
|
||||||
|
}
|
||||||
|
_, err = client.Lookup(ctx, req)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected error for empty key ID, got nil")
|
||||||
|
}
|
||||||
|
if err.Error() != "key_id is required for DeDi lookup" {
|
||||||
|
t.Errorf("Expected specific error message, got %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Test HTTP error response
|
// Test HTTP error response
|
||||||
t.Run("http error response", func(t *testing.T) {
|
t.Run("http error response", func(t *testing.T) {
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -238,10 +231,8 @@ func TestLookup(t *testing.T) {
|
|||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
config := &Config{
|
config := &Config{
|
||||||
BaseURL: server.URL,
|
URL: server.URL + "/dedi",
|
||||||
ApiKey: "test-key",
|
RegistryName: "subscribers.beckn.one",
|
||||||
NamespaceID: "test-namespace",
|
|
||||||
RegistryName: "test-registry",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client, closer, err := New(ctx, config)
|
client, closer, err := New(ctx, config)
|
||||||
@@ -252,8 +243,9 @@ func TestLookup(t *testing.T) {
|
|||||||
|
|
||||||
req := &model.Subscription{
|
req := &model.Subscription{
|
||||||
Subscriber: model.Subscriber{
|
Subscriber: model.Subscriber{
|
||||||
SubscriberID: "bap-network",
|
SubscriberID: "dev.np2.com",
|
||||||
},
|
},
|
||||||
|
KeyID: "test-key-id",
|
||||||
}
|
}
|
||||||
_, err = client.Lookup(ctx, req)
|
_, err = client.Lookup(ctx, req)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -261,14 +253,13 @@ func TestLookup(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Test missing required fields
|
// Test missing signing_public_key
|
||||||
t.Run("missing signing_public_key", func(t *testing.T) {
|
t.Run("missing signing_public_key", func(t *testing.T) {
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
response := map[string]interface{}{
|
response := map[string]interface{}{
|
||||||
"data": map[string]interface{}{
|
"data": map[string]interface{}{
|
||||||
"details": map[string]interface{}{
|
"details": map[string]interface{}{
|
||||||
"key_id": "test-key-id",
|
"subscriber_id": "dev.np2.com",
|
||||||
"status": "SUBSCRIBED",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -278,10 +269,8 @@ func TestLookup(t *testing.T) {
|
|||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
config := &Config{
|
config := &Config{
|
||||||
BaseURL: server.URL,
|
URL: server.URL + "/dedi",
|
||||||
ApiKey: "test-key",
|
RegistryName: "subscribers.beckn.one",
|
||||||
NamespaceID: "test-namespace",
|
|
||||||
RegistryName: "test-registry",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client, closer, err := New(ctx, config)
|
client, closer, err := New(ctx, config)
|
||||||
@@ -292,8 +281,9 @@ func TestLookup(t *testing.T) {
|
|||||||
|
|
||||||
req := &model.Subscription{
|
req := &model.Subscription{
|
||||||
Subscriber: model.Subscriber{
|
Subscriber: model.Subscriber{
|
||||||
SubscriberID: "bap-network",
|
SubscriberID: "dev.np2.com",
|
||||||
},
|
},
|
||||||
|
KeyID: "test-key-id",
|
||||||
}
|
}
|
||||||
_, err = client.Lookup(ctx, req)
|
_, err = client.Lookup(ctx, req)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -310,10 +300,8 @@ func TestLookup(t *testing.T) {
|
|||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
config := &Config{
|
config := &Config{
|
||||||
BaseURL: server.URL,
|
URL: server.URL + "/dedi",
|
||||||
ApiKey: "test-key",
|
RegistryName: "subscribers.beckn.one",
|
||||||
NamespaceID: "test-namespace",
|
|
||||||
RegistryName: "test-registry",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client, closer, err := New(ctx, config)
|
client, closer, err := New(ctx, config)
|
||||||
@@ -324,8 +312,9 @@ func TestLookup(t *testing.T) {
|
|||||||
|
|
||||||
req := &model.Subscription{
|
req := &model.Subscription{
|
||||||
Subscriber: model.Subscriber{
|
Subscriber: model.Subscriber{
|
||||||
SubscriberID: "bap-network",
|
SubscriberID: "dev.np2.com",
|
||||||
},
|
},
|
||||||
|
KeyID: "test-key-id",
|
||||||
}
|
}
|
||||||
_, err = client.Lookup(ctx, req)
|
_, err = client.Lookup(ctx, req)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -333,33 +322,6 @@ func TestLookup(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Test network error
|
|
||||||
t.Run("network error", func(t *testing.T) {
|
|
||||||
config := &Config{
|
|
||||||
BaseURL: "http://invalid-url-that-does-not-exist.local",
|
|
||||||
ApiKey: "test-key",
|
|
||||||
NamespaceID: "test-namespace",
|
|
||||||
RegistryName: "test-registry",
|
|
||||||
Timeout: 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
client, closer, err := New(ctx, config)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("New() error = %v", err)
|
|
||||||
}
|
|
||||||
defer closer()
|
|
||||||
|
|
||||||
req := &model.Subscription{
|
|
||||||
Subscriber: model.Subscriber{
|
|
||||||
SubscriberID: "bap-network",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
_, err = client.Lookup(ctx, req)
|
|
||||||
if err == nil {
|
|
||||||
t.Error("Expected network error, got nil")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Test missing data field
|
// Test missing data field
|
||||||
t.Run("missing data field", func(t *testing.T) {
|
t.Run("missing data field", func(t *testing.T) {
|
||||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -372,10 +334,8 @@ func TestLookup(t *testing.T) {
|
|||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
config := &Config{
|
config := &Config{
|
||||||
BaseURL: server.URL,
|
URL: server.URL + "/dedi",
|
||||||
ApiKey: "test-key",
|
RegistryName: "subscribers.beckn.one",
|
||||||
NamespaceID: "test-namespace",
|
|
||||||
RegistryName: "test-registry",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client, closer, err := New(ctx, config)
|
client, closer, err := New(ctx, config)
|
||||||
@@ -386,12 +346,39 @@ func TestLookup(t *testing.T) {
|
|||||||
|
|
||||||
req := &model.Subscription{
|
req := &model.Subscription{
|
||||||
Subscriber: model.Subscriber{
|
Subscriber: model.Subscriber{
|
||||||
SubscriberID: "bap-network",
|
SubscriberID: "dev.np2.com",
|
||||||
},
|
},
|
||||||
|
KeyID: "test-key-id",
|
||||||
}
|
}
|
||||||
_, err = client.Lookup(ctx, req)
|
_, err = client.Lookup(ctx, req)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Expected error for missing data field, got nil")
|
t.Error("Expected error for missing data field, got nil")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Test network error
|
||||||
|
t.Run("network error", func(t *testing.T) {
|
||||||
|
config := &Config{
|
||||||
|
URL: "http://invalid-url-that-does-not-exist.local/dedi",
|
||||||
|
RegistryName: "subscribers.beckn.one",
|
||||||
|
Timeout: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
client, closer, err := New(ctx, config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("New() error = %v", err)
|
||||||
|
}
|
||||||
|
defer closer()
|
||||||
|
|
||||||
|
req := &model.Subscription{
|
||||||
|
Subscriber: model.Subscriber{
|
||||||
|
SubscriberID: "dev.np2.com",
|
||||||
|
},
|
||||||
|
KeyID: "test-key-id",
|
||||||
|
}
|
||||||
|
_, err = client.Lookup(ctx, req)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected network error, got nil")
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user