Merge pull request #529 from Beckn-One/527-DeDi-fixes
Issue 527 - Remove static record_name from DeDI plugin config to enable dynamic participant lookup
This commit is contained in:
@@ -1,32 +1,22 @@
|
|||||||
# DeDi Registry Plugin
|
# DeDi Registry Plugin
|
||||||
|
|
||||||
A Beckn-ONIX registry type plugin for integrating with DeDi registry services. Implements the `RegistryLookup` interface to provide participant information and public keys.
|
A **registry type plugin** for Beckn-ONIX that integrates with DeDi (Decentralized Digital Infrastructure) registry services.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The DeDi Registry plugin enables Beckn-ONIX to lookup DeDi registries for participant records, converting DeDi API responses to standard Beckn Subscription format for seamless integration with existing registry infrastructure.
|
The DeDi Registry plugin is a **registry implementation** that enables Beckn-ONIX to lookup participant records from remote DeDi registries via REST API calls.
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **RegistryLookup Interface**: Implements standard Beckn registry interface
|
|
||||||
- **DeDi API Integration**: GET requests to DeDi registry endpoints with Bearer authentication
|
|
||||||
- **Data Conversion**: Converts DeDi responses to Beckn Subscription format
|
|
||||||
- **HTTP Retry Logic**: Built-in retry mechanism using retryablehttp client
|
|
||||||
- **Timeout Control**: Configurable request timeouts
|
|
||||||
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
plugins:
|
plugins:
|
||||||
dediRegistry:
|
registry:
|
||||||
id: dediregistry
|
id: dediregistry
|
||||||
config:
|
config:
|
||||||
baseURL: "https://dedi-registry.example.com"
|
baseURL: "https://dedi-api.example.com"
|
||||||
apiKey: "your-api-key"
|
apiKey: "your-bearer-token"
|
||||||
namespaceID: "beckn-network"
|
namespaceID: "76EU8BF1gzRGGatgw7wZZb7nEVx77XSwkKDv4UDLdxh8ztty4zmbYU"
|
||||||
registryName: "participants"
|
registryName: "dedi_registry"
|
||||||
recordName: "participant-id"
|
|
||||||
timeout: "30" # seconds
|
timeout: "30" # seconds
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -35,10 +25,9 @@ plugins:
|
|||||||
| Parameter | Required | Description | Default |
|
| Parameter | Required | Description | Default |
|
||||||
|-----------|----------|-------------|---------|
|
|-----------|----------|-------------|---------|
|
||||||
| `baseURL` | Yes | DeDi registry API base URL | - |
|
| `baseURL` | Yes | DeDi registry API base URL | - |
|
||||||
| `apiKey` | Yes | API key for authentication | - |
|
| `apiKey` | Yes | Bearer token for API authentication | - |
|
||||||
| `namespaceID` | Yes | DeDi namespace identifier | - |
|
| `namespaceID` | Yes | DeDi namespace identifier | - |
|
||||||
| `registryName` | Yes | Registry name to query | - |
|
| `registryName` | Yes | Registry name to query | - |
|
||||||
| `recordName` | Yes | Record name/identifier | - |
|
|
||||||
| `timeout` | No | Request timeout in seconds | 30 |
|
| `timeout` | No | Request timeout in seconds | 30 |
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@@ -50,85 +39,108 @@ modules:
|
|||||||
- name: bapTxnReceiver
|
- name: bapTxnReceiver
|
||||||
handler:
|
handler:
|
||||||
plugins:
|
plugins:
|
||||||
dediRegistry:
|
registry:
|
||||||
id: dediregistry
|
id: dediregistry
|
||||||
config:
|
config:
|
||||||
baseURL: "https://dedi-registry.example.com"
|
baseURL: "https://dedi-registry.example.com"
|
||||||
apiKey: "your-api-key"
|
apiKey: "your-api-key"
|
||||||
namespaceID: "beckn-network"
|
namespaceID: "beckn-network"
|
||||||
registryName: "participants"
|
registryName: "participants"
|
||||||
recordName: "participant-id"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### In Code
|
### In Code
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// Load DeDi registry plugin
|
// Load DeDi registry plugin (same as any registry plugin)
|
||||||
dediRegistry, err := manager.Registry(ctx, &plugin.Config{
|
dediRegistry, err := manager.Registry(ctx, &plugin.Config{
|
||||||
ID: "dediregistry",
|
ID: "dediregistry", // Plugin ID specifies DeDi implementation
|
||||||
Config: map[string]string{
|
Config: map[string]string{
|
||||||
"baseURL": "https://dedi-registry.example.com",
|
"baseURL": "https://dedi-registry.example.com",
|
||||||
"apiKey": "your-api-key",
|
"apiKey": "your-api-key",
|
||||||
"namespaceID": "beckn-network",
|
"namespaceID": "beckn-network",
|
||||||
"registryName": "participants",
|
"registryName": "participants",
|
||||||
"recordName": "participant-id",
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// Or use specific method
|
// Lookup participant with dynamic subscriber ID (from request context)
|
||||||
dediRegistry, err := manager.DeDiRegistry(ctx, config)
|
subscription := &model.Subscription{
|
||||||
|
Subscriber: model.Subscriber{
|
||||||
// Lookup participant (returns Beckn Subscription format)
|
SubscriberID: "bap-network", // Extracted from Authorization header or request body
|
||||||
subscription := &model.Subscription{}
|
},
|
||||||
|
}
|
||||||
results, err := dediRegistry.Lookup(ctx, subscription)
|
results, err := dediRegistry.Lookup(ctx, subscription)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract public key from first result
|
// Extract public key from result (standard Beckn format)
|
||||||
if len(results) > 0 {
|
if len(results) > 0 {
|
||||||
publicKey := results[0].SigningPublicKey
|
publicKey := results[0].SigningPublicKey
|
||||||
subscriberID := results[0].SubscriberID
|
subscriberID := results[0].SubscriberID
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## API Response Structure
|
## API Integration
|
||||||
|
|
||||||
The plugin expects DeDi registry responses in this format:
|
### 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
|
```json
|
||||||
{
|
{
|
||||||
"message": "success",
|
"message": "Resource retrieved successfully",
|
||||||
"data": {
|
"data": {
|
||||||
"namespace": "beckn",
|
"namespace": "dediregistry",
|
||||||
"schema": {
|
"namespace_id": "76EU8BF1gzRGGatgw7wZZb7nEVx77XSwkKDv4UDLdxh8ztty4zmbYU",
|
||||||
"entity_name": "participant.example.com",
|
"registry_name": "dedi_registry",
|
||||||
"entity_url": "https://participant.example.com",
|
"record_name": "bap-network",
|
||||||
"publicKey": "base64-encoded-public-key",
|
"details": {
|
||||||
"keyType": "ed25519",
|
"key_id": "b692d295-5425-40f5-af77-d62646841dca",
|
||||||
"keyFormat": "base64"
|
"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": "active",
|
"state": "live",
|
||||||
"created_at": "2023-01-01T00:00:00Z",
|
"created_at": "2025-10-09T06:09:48.295Z"
|
||||||
"updated_at": "2023-01-01T00:00:00Z"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Converted to Beckn Format
|
### Field Mapping to Beckn Subscription
|
||||||
|
|
||||||
The plugin converts this to standard Beckn Subscription format:
|
| 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.encr_public_key` | `encr_public_key` | Public key for encryption |
|
||||||
|
| `data.details.status` | `status` | Subscription status |
|
||||||
|
| `data.details.created` | `created` | Creation timestamp |
|
||||||
|
| `data.details.updated` | `updated` | Last update timestamp |
|
||||||
|
| `data.details.valid_from` | `valid_from` | Key validity start |
|
||||||
|
| `data.details.valid_until` | `valid_until` | Key validity end |
|
||||||
|
|
||||||
```json
|
### Lookup Flow
|
||||||
{
|
|
||||||
"subscriber_id": "participant.example.com",
|
1. **Request Processing**: Plugin extracts `subscriber_id` from incoming request
|
||||||
"url": "https://participant.example.com",
|
2. **API Call**: Makes GET request to DeDi API with static config + dynamic subscriber ID
|
||||||
"signing_public_key": "base64-encoded-public-key",
|
3. **Response Parsing**: Extracts data from `data.details` object
|
||||||
"status": "active",
|
4. **Format Conversion**: Maps DeDi fields to Beckn Subscription format
|
||||||
"created": "2023-01-01T00:00:00Z",
|
5. **Return**: Returns array of Subscription objects
|
||||||
"updated": "2023-01-01T00:00:00Z"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
@@ -138,16 +150,34 @@ Run plugin tests:
|
|||||||
go test ./pkg/plugin/implementation/dediregistry -v
|
go test ./pkg/plugin/implementation/dediregistry -v
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Test coverage includes:
|
||||||
|
- Configuration validation
|
||||||
|
- Successful API responses
|
||||||
|
- HTTP error handling
|
||||||
|
- Network failures
|
||||||
|
- Invalid JSON responses
|
||||||
|
- Missing required fields
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
- `github.com/hashicorp/go-retryablehttp`: HTTP client with retry logic
|
- `github.com/hashicorp/go-retryablehttp`: HTTP client with retry logic
|
||||||
- Standard Go libraries for HTTP and JSON handling
|
- Standard Go libraries for HTTP and JSON handling
|
||||||
|
|
||||||
## Integration Notes
|
## Error Handling
|
||||||
|
|
||||||
- **Registry Type Plugin**: Implements `RegistryLookup` interface, not a separate plugin category
|
- **Configuration Errors**: Missing required config parameters
|
||||||
- **Interchangeable**: Can be used alongside or instead of standard registry plugin
|
- **Network Errors**: Connection failures, timeouts
|
||||||
- **Manager Integration**: Available via `manager.Registry()` or `manager.DeDiRegistry()` methods
|
- **HTTP Errors**: Non-200 status codes from DeDi API
|
||||||
- **Data Conversion**: Automatically converts DeDi format to Beckn Subscription format
|
- **Data Errors**: Missing required fields in response
|
||||||
- **Interface Compliance**: Implements `RegistryLookup` interface with `Lookup()` method only
|
- **Validation Errors**: Empty subscriber ID in request
|
||||||
- **Build Integration**: Included in `build-plugins.sh` script, compiles to `dediregistry.so`
|
|
||||||
|
|
||||||
|
### 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
|
||||||
@@ -25,13 +25,14 @@ func (d dediRegistryProvider) New(ctx context.Context, config map[string]string)
|
|||||||
ApiKey: config["apiKey"],
|
ApiKey: config["apiKey"],
|
||||||
NamespaceID: config["namespaceID"],
|
NamespaceID: config["namespaceID"],
|
||||||
RegistryName: config["registryName"],
|
RegistryName: config["registryName"],
|
||||||
RecordName: config["recordName"],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse timeout if provided
|
// Parse timeout if provided
|
||||||
if timeoutStr, exists := config["timeout"]; exists && timeoutStr != "" {
|
if timeoutStr, exists := config["timeout"]; exists && timeoutStr != "" {
|
||||||
if timeout, err := strconv.Atoi(timeoutStr); err == nil {
|
if timeout, err := strconv.Atoi(timeoutStr); err == nil {
|
||||||
dediConfig.Timeout = timeout
|
dediConfig.Timeout = timeout
|
||||||
|
} else {
|
||||||
|
log.Warnf(ctx, "Invalid timeout value '%s', using default", timeoutStr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ type Config struct {
|
|||||||
ApiKey string `yaml:"apiKey" json:"apiKey"`
|
ApiKey string `yaml:"apiKey" json:"apiKey"`
|
||||||
NamespaceID string `yaml:"namespaceID" json:"namespaceID"`
|
NamespaceID string `yaml:"namespaceID" json:"namespaceID"`
|
||||||
RegistryName string `yaml:"registryName" json:"registryName"`
|
RegistryName string `yaml:"registryName" json:"registryName"`
|
||||||
RecordName string `yaml:"recordName" json:"recordName"`
|
|
||||||
Timeout int `yaml:"timeout" json:"timeout"`
|
Timeout int `yaml:"timeout" json:"timeout"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,9 +45,7 @@ func validate(cfg *Config) error {
|
|||||||
if cfg.RegistryName == "" {
|
if cfg.RegistryName == "" {
|
||||||
return fmt.Errorf("registryName cannot be empty")
|
return fmt.Errorf("registryName cannot be empty")
|
||||||
}
|
}
|
||||||
if cfg.RecordName == "" {
|
|
||||||
return fmt.Errorf("recordName cannot be empty")
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,8 +84,15 @@ func New(ctx context.Context, cfg *Config) (*DeDiRegistryClient, func() error, e
|
|||||||
|
|
||||||
// Lookup implements RegistryLookup interface - calls the DeDi lookup endpoint and returns Subscription.
|
// Lookup implements RegistryLookup interface - calls the DeDi lookup endpoint and returns Subscription.
|
||||||
func (c *DeDiRegistryClient) Lookup(ctx context.Context, req *model.Subscription) ([]model.Subscription, error) {
|
func (c *DeDiRegistryClient) Lookup(ctx context.Context, req *model.Subscription) ([]model.Subscription, error) {
|
||||||
|
// Extract subscriber ID from request
|
||||||
|
subscriberID := req.SubscriberID
|
||||||
|
log.Infof(ctx, "DeDI Registry: Looking up subscriber ID: %s", subscriberID)
|
||||||
|
if subscriberID == "" {
|
||||||
|
return nil, fmt.Errorf("subscriber_id is required for DeDi lookup")
|
||||||
|
}
|
||||||
|
|
||||||
lookupURL := fmt.Sprintf("%s/dedi/lookup/%s/%s/%s",
|
lookupURL := fmt.Sprintf("%s/dedi/lookup/%s/%s/%s",
|
||||||
c.config.BaseURL, c.config.NamespaceID, c.config.RegistryName, c.config.RecordName)
|
c.config.BaseURL, c.config.NamespaceID, c.config.RegistryName, subscriberID)
|
||||||
|
|
||||||
httpReq, err := retryablehttp.NewRequest("GET", lookupURL, nil)
|
httpReq, err := retryablehttp.NewRequest("GET", lookupURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -116,7 +120,7 @@ func (c *DeDiRegistryClient) Lookup(ctx context.Context, req *model.Subscription
|
|||||||
return nil, fmt.Errorf("failed to read response body: %w", err)
|
return nil, fmt.Errorf("failed to read response body: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse response using local variables
|
// Parse response
|
||||||
var responseData map[string]interface{}
|
var responseData map[string]interface{}
|
||||||
err = json.Unmarshal(body, &responseData)
|
err = json.Unmarshal(body, &responseData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -125,49 +129,56 @@ func (c *DeDiRegistryClient) Lookup(ctx context.Context, req *model.Subscription
|
|||||||
|
|
||||||
log.Debugf(ctx, "DeDi lookup request successful")
|
log.Debugf(ctx, "DeDi lookup request successful")
|
||||||
|
|
||||||
// Extract data using local variables
|
// Extract data field
|
||||||
data, ok := responseData["data"].(map[string]interface{})
|
data, ok := responseData["data"].(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("invalid response format: missing data field")
|
return nil, fmt.Errorf("invalid response format: missing data field")
|
||||||
}
|
}
|
||||||
|
|
||||||
schema, ok := data["schema"].(map[string]interface{})
|
// Extract details field
|
||||||
|
details, ok := data["details"].(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("invalid response format: missing schema field")
|
return nil, fmt.Errorf("invalid response format: missing details field")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract values using type assertions with error checking
|
// Extract required fields from details
|
||||||
entityName, ok := schema["entity_name"].(string)
|
keyID, _ := details["key_id"].(string)
|
||||||
if !ok || entityName == "" {
|
signingPublicKey, ok := details["signing_public_key"].(string)
|
||||||
return nil, fmt.Errorf("invalid or missing entity_name in response")
|
if !ok || signingPublicKey == "" {
|
||||||
|
return nil, fmt.Errorf("invalid or missing signing_public_key in response")
|
||||||
}
|
}
|
||||||
|
encrPublicKey, _ := details["encr_public_key"].(string)
|
||||||
entityURL, ok := schema["entity_url"].(string)
|
detailsStatus, ok := details["status"].(string)
|
||||||
if !ok || entityURL == "" {
|
if !ok {
|
||||||
return nil, fmt.Errorf("invalid or missing entity_url in response")
|
return nil, fmt.Errorf("missing status in response")
|
||||||
}
|
}
|
||||||
|
detailsCreated, _ := details["created"].(string)
|
||||||
|
detailsUpdated, _ := details["updated"].(string)
|
||||||
|
validFromStr, _ := details["valid_from"].(string)
|
||||||
|
validUntilStr, _ := details["valid_until"].(string)
|
||||||
|
|
||||||
publicKey, ok := schema["publicKey"].(string)
|
// Extract record_name as subscriber ID
|
||||||
if !ok || publicKey == "" {
|
recordName, _ := data["record_name"].(string)
|
||||||
return nil, fmt.Errorf("invalid or missing publicKey in response")
|
if recordName == "" {
|
||||||
|
recordName = subscriberID
|
||||||
}
|
}
|
||||||
|
|
||||||
state, _ := data["state"].(string)
|
|
||||||
createdAt, _ := data["created_at"].(string)
|
|
||||||
updatedAt, _ := data["updated_at"].(string)
|
|
||||||
|
|
||||||
// Convert to Subscription format
|
// Convert to Subscription format
|
||||||
subscription := model.Subscription{
|
subscription := model.Subscription{
|
||||||
Subscriber: model.Subscriber{
|
Subscriber: model.Subscriber{
|
||||||
SubscriberID: entityName,
|
SubscriberID: recordName,
|
||||||
URL: entityURL,
|
URL: req.URL,
|
||||||
Domain: req.Domain,
|
Domain: req.Domain,
|
||||||
Type: req.Type,
|
Type: req.Type,
|
||||||
},
|
},
|
||||||
SigningPublicKey: publicKey,
|
KeyID: keyID,
|
||||||
Status: state,
|
SigningPublicKey: signingPublicKey,
|
||||||
Created: parseTime(createdAt),
|
EncrPublicKey: encrPublicKey,
|
||||||
Updated: parseTime(updatedAt),
|
ValidFrom: parseTime(validFromStr),
|
||||||
|
ValidUntil: parseTime(validUntilStr),
|
||||||
|
Status: detailsStatus,
|
||||||
|
Created: parseTime(detailsCreated),
|
||||||
|
Updated: parseTime(detailsUpdated),
|
||||||
}
|
}
|
||||||
|
|
||||||
return []model.Subscription{subscription}, nil
|
return []model.Subscription{subscription}, nil
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ func TestValidate(t *testing.T) {
|
|||||||
ApiKey: "test-key",
|
ApiKey: "test-key",
|
||||||
NamespaceID: "test-namespace",
|
NamespaceID: "test-namespace",
|
||||||
RegistryName: "test-registry",
|
RegistryName: "test-registry",
|
||||||
RecordName: "test-record",
|
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
@@ -39,7 +38,6 @@ func TestValidate(t *testing.T) {
|
|||||||
ApiKey: "",
|
ApiKey: "",
|
||||||
NamespaceID: "test-namespace",
|
NamespaceID: "test-namespace",
|
||||||
RegistryName: "test-registry",
|
RegistryName: "test-registry",
|
||||||
RecordName: "test-record",
|
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
@@ -50,7 +48,6 @@ func TestValidate(t *testing.T) {
|
|||||||
ApiKey: "test-key",
|
ApiKey: "test-key",
|
||||||
NamespaceID: "",
|
NamespaceID: "",
|
||||||
RegistryName: "test-registry",
|
RegistryName: "test-registry",
|
||||||
RecordName: "test-record",
|
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
@@ -61,18 +58,6 @@ func TestValidate(t *testing.T) {
|
|||||||
ApiKey: "test-key",
|
ApiKey: "test-key",
|
||||||
NamespaceID: "test-namespace",
|
NamespaceID: "test-namespace",
|
||||||
RegistryName: "",
|
RegistryName: "",
|
||||||
RecordName: "test-record",
|
|
||||||
},
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "empty recordName",
|
|
||||||
config: &Config{
|
|
||||||
BaseURL: "https://test.com",
|
|
||||||
ApiKey: "test-key",
|
|
||||||
NamespaceID: "test-namespace",
|
|
||||||
RegistryName: "test-registry",
|
|
||||||
RecordName: "",
|
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
@@ -83,7 +68,6 @@ func TestValidate(t *testing.T) {
|
|||||||
ApiKey: "test-key",
|
ApiKey: "test-key",
|
||||||
NamespaceID: "test-namespace",
|
NamespaceID: "test-namespace",
|
||||||
RegistryName: "test-registry",
|
RegistryName: "test-registry",
|
||||||
RecordName: "test-record",
|
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
@@ -107,7 +91,6 @@ func TestNew(t *testing.T) {
|
|||||||
ApiKey: "test-key",
|
ApiKey: "test-key",
|
||||||
NamespaceID: "test-namespace",
|
NamespaceID: "test-namespace",
|
||||||
RegistryName: "test-registry",
|
RegistryName: "test-registry",
|
||||||
RecordName: "test-record",
|
|
||||||
Timeout: 30,
|
Timeout: 30,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +125,7 @@ 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/test-record" {
|
if r.URL.Path != "/dedi/lookup/test-namespace/test-registry/bap-network" {
|
||||||
t.Errorf("Unexpected path: %s", r.URL.Path)
|
t.Errorf("Unexpected path: %s", r.URL.Path)
|
||||||
}
|
}
|
||||||
// Verify Authorization header
|
// Verify Authorization header
|
||||||
@@ -150,16 +133,22 @@ func TestLookup(t *testing.T) {
|
|||||||
t.Errorf("Expected Bearer test-key, got %s", auth)
|
t.Errorf("Expected Bearer test-key, got %s", auth)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return mock response using map structure
|
// Return mock response using actual DeDI format
|
||||||
response := map[string]interface{}{
|
response := map[string]interface{}{
|
||||||
"message": "success",
|
"message": "Resource retrieved successfully",
|
||||||
"data": map[string]interface{}{
|
"data": map[string]interface{}{
|
||||||
"schema": map[string]interface{}{
|
"record_name": "bap-network",
|
||||||
"entity_name": "test.example.com",
|
"details": map[string]interface{}{
|
||||||
"entity_url": "https://test.example.com",
|
"key_id": "b692d295-5425-40f5-af77-d62646841dca",
|
||||||
"publicKey": "test-public-key",
|
"signing_public_key": "test-public-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": "active",
|
"state": "live",
|
||||||
"created_at": "2023-01-01T00:00:00Z",
|
"created_at": "2023-01-01T00:00:00Z",
|
||||||
"updated_at": "2023-01-01T00:00:00Z",
|
"updated_at": "2023-01-01T00:00:00Z",
|
||||||
},
|
},
|
||||||
@@ -174,7 +163,6 @@ func TestLookup(t *testing.T) {
|
|||||||
ApiKey: "test-key",
|
ApiKey: "test-key",
|
||||||
NamespaceID: "test-namespace",
|
NamespaceID: "test-namespace",
|
||||||
RegistryName: "test-registry",
|
RegistryName: "test-registry",
|
||||||
RecordName: "test-record",
|
|
||||||
Timeout: 30,
|
Timeout: 30,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,7 +172,11 @@ func TestLookup(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer closer()
|
defer closer()
|
||||||
|
|
||||||
req := &model.Subscription{}
|
req := &model.Subscription{
|
||||||
|
Subscriber: model.Subscriber{
|
||||||
|
SubscriberID: "bap-network",
|
||||||
|
},
|
||||||
|
}
|
||||||
results, err := client.Lookup(ctx, req)
|
results, err := client.Lookup(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Lookup() error = %v", err)
|
t.Errorf("Lookup() error = %v", err)
|
||||||
@@ -197,14 +189,43 @@ func TestLookup(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
subscription := results[0]
|
subscription := results[0]
|
||||||
if subscription.Subscriber.SubscriberID != "test.example.com" {
|
if subscription.Subscriber.SubscriberID != "bap-network" {
|
||||||
t.Errorf("Expected subscriber_id test.example.com, got %s", subscription.Subscriber.SubscriberID)
|
t.Errorf("Expected subscriber_id bap-network, got %s", subscription.Subscriber.SubscriberID)
|
||||||
}
|
}
|
||||||
if subscription.SigningPublicKey != "test-public-key" {
|
if subscription.SigningPublicKey != "test-public-key" {
|
||||||
t.Errorf("Expected signing_public_key test-public-key, got %s", subscription.SigningPublicKey)
|
t.Errorf("Expected signing_public_key test-public-key, got %s", subscription.SigningPublicKey)
|
||||||
}
|
}
|
||||||
if subscription.Status != "active" {
|
if subscription.Status != "SUBSCRIBED" {
|
||||||
t.Errorf("Expected status active, got %s", subscription.Status)
|
t.Errorf("Expected status SUBSCRIBED, got %s", subscription.Status)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Test empty subscriber ID
|
||||||
|
t.Run("empty subscriber ID", func(t *testing.T) {
|
||||||
|
config := &Config{
|
||||||
|
BaseURL: "https://test.com",
|
||||||
|
ApiKey: "test-key",
|
||||||
|
NamespaceID: "test-namespace",
|
||||||
|
RegistryName: "test-registry",
|
||||||
|
}
|
||||||
|
|
||||||
|
client, closer, err := New(ctx, config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("New() error = %v", err)
|
||||||
|
}
|
||||||
|
defer closer()
|
||||||
|
|
||||||
|
req := &model.Subscription{
|
||||||
|
Subscriber: model.Subscriber{
|
||||||
|
SubscriberID: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err = client.Lookup(ctx, req)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected error for empty subscriber ID, got nil")
|
||||||
|
}
|
||||||
|
if err.Error() != "subscriber_id is required for DeDi lookup" {
|
||||||
|
t.Errorf("Expected specific error message, got %v", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -221,7 +242,6 @@ func TestLookup(t *testing.T) {
|
|||||||
ApiKey: "test-key",
|
ApiKey: "test-key",
|
||||||
NamespaceID: "test-namespace",
|
NamespaceID: "test-namespace",
|
||||||
RegistryName: "test-registry",
|
RegistryName: "test-registry",
|
||||||
RecordName: "test-record",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client, closer, err := New(ctx, config)
|
client, closer, err := New(ctx, config)
|
||||||
@@ -230,7 +250,11 @@ func TestLookup(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer closer()
|
defer closer()
|
||||||
|
|
||||||
req := &model.Subscription{}
|
req := &model.Subscription{
|
||||||
|
Subscriber: model.Subscriber{
|
||||||
|
SubscriberID: "bap-network",
|
||||||
|
},
|
||||||
|
}
|
||||||
_, err = client.Lookup(ctx, req)
|
_, err = client.Lookup(ctx, req)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Expected error for 404 response, got nil")
|
t.Error("Expected error for 404 response, got nil")
|
||||||
@@ -238,13 +262,13 @@ func TestLookup(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Test missing required fields
|
// Test missing required fields
|
||||||
t.Run("missing entity_name", 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{}{
|
||||||
"schema": map[string]interface{}{
|
"details": map[string]interface{}{
|
||||||
"entity_url": "https://test.example.com",
|
"key_id": "test-key-id",
|
||||||
"publicKey": "test-public-key",
|
"status": "SUBSCRIBED",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -258,7 +282,6 @@ func TestLookup(t *testing.T) {
|
|||||||
ApiKey: "test-key",
|
ApiKey: "test-key",
|
||||||
NamespaceID: "test-namespace",
|
NamespaceID: "test-namespace",
|
||||||
RegistryName: "test-registry",
|
RegistryName: "test-registry",
|
||||||
RecordName: "test-record",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client, closer, err := New(ctx, config)
|
client, closer, err := New(ctx, config)
|
||||||
@@ -267,10 +290,14 @@ func TestLookup(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer closer()
|
defer closer()
|
||||||
|
|
||||||
req := &model.Subscription{}
|
req := &model.Subscription{
|
||||||
|
Subscriber: model.Subscriber{
|
||||||
|
SubscriberID: "bap-network",
|
||||||
|
},
|
||||||
|
}
|
||||||
_, err = client.Lookup(ctx, req)
|
_, err = client.Lookup(ctx, req)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Expected error for missing entity_name, got nil")
|
t.Error("Expected error for missing signing_public_key, got nil")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -287,7 +314,6 @@ func TestLookup(t *testing.T) {
|
|||||||
ApiKey: "test-key",
|
ApiKey: "test-key",
|
||||||
NamespaceID: "test-namespace",
|
NamespaceID: "test-namespace",
|
||||||
RegistryName: "test-registry",
|
RegistryName: "test-registry",
|
||||||
RecordName: "test-record",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client, closer, err := New(ctx, config)
|
client, closer, err := New(ctx, config)
|
||||||
@@ -296,7 +322,11 @@ func TestLookup(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer closer()
|
defer closer()
|
||||||
|
|
||||||
req := &model.Subscription{}
|
req := &model.Subscription{
|
||||||
|
Subscriber: model.Subscriber{
|
||||||
|
SubscriberID: "bap-network",
|
||||||
|
},
|
||||||
|
}
|
||||||
_, err = client.Lookup(ctx, req)
|
_, err = client.Lookup(ctx, req)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Expected error for invalid JSON, got nil")
|
t.Error("Expected error for invalid JSON, got nil")
|
||||||
@@ -310,7 +340,6 @@ func TestLookup(t *testing.T) {
|
|||||||
ApiKey: "test-key",
|
ApiKey: "test-key",
|
||||||
NamespaceID: "test-namespace",
|
NamespaceID: "test-namespace",
|
||||||
RegistryName: "test-registry",
|
RegistryName: "test-registry",
|
||||||
RecordName: "test-record",
|
|
||||||
Timeout: 1,
|
Timeout: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,7 +349,11 @@ func TestLookup(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer closer()
|
defer closer()
|
||||||
|
|
||||||
req := &model.Subscription{}
|
req := &model.Subscription{
|
||||||
|
Subscriber: model.Subscriber{
|
||||||
|
SubscriberID: "bap-network",
|
||||||
|
},
|
||||||
|
}
|
||||||
_, err = client.Lookup(ctx, req)
|
_, err = client.Lookup(ctx, req)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("Expected network error, got nil")
|
t.Error("Expected network error, got nil")
|
||||||
@@ -343,7 +376,6 @@ func TestLookup(t *testing.T) {
|
|||||||
ApiKey: "test-key",
|
ApiKey: "test-key",
|
||||||
NamespaceID: "test-namespace",
|
NamespaceID: "test-namespace",
|
||||||
RegistryName: "test-registry",
|
RegistryName: "test-registry",
|
||||||
RecordName: "test-record",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client, closer, err := New(ctx, config)
|
client, closer, err := New(ctx, config)
|
||||||
@@ -352,7 +384,11 @@ func TestLookup(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer closer()
|
defer closer()
|
||||||
|
|
||||||
req := &model.Subscription{}
|
req := &model.Subscription{
|
||||||
|
Subscriber: model.Subscriber{
|
||||||
|
SubscriberID: "bap-network",
|
||||||
|
},
|
||||||
|
}
|
||||||
_, 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")
|
||||||
|
|||||||
Reference in New Issue
Block a user