543 - update: to use updated config structure
This commit is contained in:
@@ -17,7 +17,8 @@ Validates Beckn protocol requests against OpenAPI 3.1 specifications using kin-o
|
|||||||
schemaValidator:
|
schemaValidator:
|
||||||
id: schemav2validator
|
id: schemav2validator
|
||||||
config:
|
config:
|
||||||
url: https://example.com/openapi-spec.yaml
|
type: url
|
||||||
|
location: https://example.com/openapi-spec.yaml
|
||||||
cacheTTL: "3600"
|
cacheTTL: "3600"
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -25,9 +26,12 @@ schemaValidator:
|
|||||||
|
|
||||||
| Parameter | Type | Required | Default | Description |
|
| Parameter | Type | Required | Default | Description |
|
||||||
|-----------|------|----------|---------|-------------|
|
|-----------|------|----------|---------|-------------|
|
||||||
| `url` | string | Yes | - | URL or file path to OpenAPI 3.1 specification |
|
| `type` | string | Yes | - | Type of spec source: "url" or "file" ("dir" reserved for future) |
|
||||||
|
| `location` | string | Yes | - | URL or file path to OpenAPI 3.1 spec |
|
||||||
| `cacheTTL` | string | No | "3600" | Cache TTL in seconds before reloading spec |
|
| `cacheTTL` | string | No | "3600" | Cache TTL in seconds before reloading spec |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## How It Works
|
## How It Works
|
||||||
|
|
||||||
1. **Load Spec**: Loads OpenAPI spec from configured URL at startup
|
1. **Load Spec**: Loads OpenAPI spec from configured URL at startup
|
||||||
@@ -94,26 +98,30 @@ The loader will automatically fetch and resolve the external reference.
|
|||||||
|
|
||||||
## Example Usage
|
## Example Usage
|
||||||
|
|
||||||
### Local File
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
schemaValidator:
|
|
||||||
id: schemav2validator
|
|
||||||
config:
|
|
||||||
url: ./validation-scripts/l2-config/mobility_1.1.0_openapi_3.1.yaml
|
|
||||||
cacheTTL: "3600"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Remote URL
|
### Remote URL
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
schemaValidator:
|
schemaValidator:
|
||||||
id: schemav2validator
|
id: schemav2validator
|
||||||
config:
|
config:
|
||||||
url: https://raw.githubusercontent.com/beckn/protocol-specifications/master/api/beckn-2.0.0.yaml
|
type: url
|
||||||
|
location: https://raw.githubusercontent.com/beckn/protocol-specifications/master/api/beckn-2.0.0.yaml
|
||||||
cacheTTL: "7200"
|
cacheTTL: "7200"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Local File
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
schemaValidator:
|
||||||
|
id: schemav2validator
|
||||||
|
config:
|
||||||
|
type: file
|
||||||
|
location: ./validation-scripts/l2-config/mobility_1.1.0_openapi_3.1.yaml
|
||||||
|
cacheTTL: "3600"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
- `github.com/getkin/kin-openapi` - OpenAPI 3 parser and validator
|
- `github.com/getkin/kin-openapi` - OpenAPI 3 parser and validator
|
||||||
|
|||||||
@@ -18,21 +18,26 @@ func (vp schemav2ValidatorProvider) New(ctx context.Context, config map[string]s
|
|||||||
return nil, nil, errors.New("context cannot be nil")
|
return nil, nil, errors.New("context cannot be nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
url, ok := config["url"]
|
typeVal, hasType := config["type"]
|
||||||
if !ok || url == "" {
|
locVal, hasLoc := config["location"]
|
||||||
return nil, nil, errors.New("url not configured")
|
|
||||||
}
|
|
||||||
|
|
||||||
cacheTTL := 3600
|
if !hasType || typeVal == "" {
|
||||||
if ttlStr, ok := config["cacheTTL"]; ok {
|
return nil, nil, errors.New("type not configured")
|
||||||
if ttl, err := strconv.Atoi(ttlStr); err == nil && ttl > 0 {
|
}
|
||||||
cacheTTL = ttl
|
if !hasLoc || locVal == "" {
|
||||||
}
|
return nil, nil, errors.New("location not configured")
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := &schemav2validator.Config{
|
cfg := &schemav2validator.Config{
|
||||||
URL: url,
|
Type: typeVal,
|
||||||
CacheTTL: cacheTTL,
|
Location: locVal,
|
||||||
|
CacheTTL: 3600,
|
||||||
|
}
|
||||||
|
|
||||||
|
if ttlStr, ok := config["cacheTTL"]; ok {
|
||||||
|
if ttl, err := strconv.Atoi(ttlStr); err == nil && ttl > 0 {
|
||||||
|
cfg.CacheTTL = ttl
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return schemav2validator.New(ctx, cfg)
|
return schemav2validator.New(ctx, cfg)
|
||||||
|
|||||||
@@ -48,39 +48,64 @@ func TestProvider_New(t *testing.T) {
|
|||||||
errMsg: "context cannot be nil",
|
errMsg: "context cannot be nil",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "missing url",
|
name: "missing type",
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
config: map[string]string{},
|
config: map[string]string{"location": server.URL},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
errMsg: "url not configured",
|
errMsg: "type not configured",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty url",
|
name: "missing location",
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
config: map[string]string{"url": ""},
|
config: map[string]string{"type": "url"},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
errMsg: "url not configured",
|
errMsg: "location not configured",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty type",
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: map[string]string{"type": "", "location": server.URL},
|
||||||
|
wantErr: true,
|
||||||
|
errMsg: "type not configured",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty location",
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: map[string]string{"type": "url", "location": ""},
|
||||||
|
wantErr: true,
|
||||||
|
errMsg: "location not configured",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid config with default TTL",
|
name: "valid config with default TTL",
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
config: map[string]string{"url": server.URL},
|
config: map[string]string{"type": "url", "location": server.URL},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid config with custom TTL",
|
name: "valid config with custom TTL",
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
config: map[string]string{
|
config: map[string]string{
|
||||||
"url": server.URL,
|
"type": "url",
|
||||||
|
"location": server.URL,
|
||||||
"cacheTTL": "7200",
|
"cacheTTL": "7200",
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "valid file type",
|
||||||
|
ctx: context.Background(),
|
||||||
|
config: map[string]string{
|
||||||
|
"type": "file",
|
||||||
|
"location": "/tmp/spec.yaml",
|
||||||
|
},
|
||||||
|
wantErr: true, // file doesn't exist
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "invalid TTL falls back to default",
|
name: "invalid TTL falls back to default",
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
config: map[string]string{
|
config: map[string]string{
|
||||||
"url": server.URL,
|
"type": "url",
|
||||||
|
"location": server.URL,
|
||||||
"cacheTTL": "invalid",
|
"cacheTTL": "invalid",
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
@@ -89,7 +114,8 @@ func TestProvider_New(t *testing.T) {
|
|||||||
name: "negative TTL falls back to default",
|
name: "negative TTL falls back to default",
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
config: map[string]string{
|
config: map[string]string{
|
||||||
"url": server.URL,
|
"type": "url",
|
||||||
|
"location": server.URL,
|
||||||
"cacheTTL": "-100",
|
"cacheTTL": "-100",
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
@@ -98,7 +124,8 @@ func TestProvider_New(t *testing.T) {
|
|||||||
name: "zero TTL falls back to default",
|
name: "zero TTL falls back to default",
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
config: map[string]string{
|
config: map[string]string{
|
||||||
"url": server.URL,
|
"type": "url",
|
||||||
|
"location": server.URL,
|
||||||
"cacheTTL": "0",
|
"cacheTTL": "0",
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ type cachedSpec struct {
|
|||||||
|
|
||||||
// Config struct for Schemav2Validator.
|
// Config struct for Schemav2Validator.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
URL string
|
Type string // "url", "file", or "dir"
|
||||||
|
Location string // URL, file path, or directory path
|
||||||
CacheTTL int
|
CacheTTL int
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,8 +47,14 @@ func New(ctx context.Context, config *Config) (*schemav2Validator, func() error,
|
|||||||
if config == nil {
|
if config == nil {
|
||||||
return nil, nil, fmt.Errorf("config cannot be nil")
|
return nil, nil, fmt.Errorf("config cannot be nil")
|
||||||
}
|
}
|
||||||
if config.URL == "" {
|
if config.Type == "" {
|
||||||
return nil, nil, fmt.Errorf("config URL cannot be empty")
|
return nil, nil, fmt.Errorf("config type cannot be empty")
|
||||||
|
}
|
||||||
|
if config.Location == "" {
|
||||||
|
return nil, nil, fmt.Errorf("config location cannot be empty")
|
||||||
|
}
|
||||||
|
if config.Type != "url" && config.Type != "file" && config.Type != "dir" {
|
||||||
|
return nil, nil, fmt.Errorf("config type must be 'url', 'file', or 'dir'")
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.CacheTTL == 0 {
|
if config.CacheTTL == 0 {
|
||||||
@@ -156,20 +163,23 @@ func (v *schemav2Validator) loadSpec(ctx context.Context) error {
|
|||||||
var doc *openapi3.T
|
var doc *openapi3.T
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
urlOrPath := v.config.URL
|
switch v.config.Type {
|
||||||
|
case "url":
|
||||||
if strings.HasPrefix(urlOrPath, "http://") || strings.HasPrefix(urlOrPath, "https://") {
|
u, parseErr := url.Parse(v.config.Location)
|
||||||
u, parseErr := url.Parse(urlOrPath)
|
|
||||||
if parseErr != nil {
|
if parseErr != nil {
|
||||||
return fmt.Errorf("failed to parse URL: %v", parseErr)
|
return fmt.Errorf("failed to parse URL: %v", parseErr)
|
||||||
}
|
}
|
||||||
doc, err = loader.LoadFromURI(u)
|
doc, err = loader.LoadFromURI(u)
|
||||||
} else {
|
case "file":
|
||||||
doc, err = loader.LoadFromFile(urlOrPath)
|
doc, err = loader.LoadFromFile(v.config.Location)
|
||||||
|
case "dir":
|
||||||
|
return fmt.Errorf("directory loading not yet implemented")
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported type: %s", v.config.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(ctx, err, "Invalid URL or unreachable: %s", urlOrPath)
|
log.Errorf(ctx, err, "Failed to load from %s: %s", v.config.Type, v.config.Location)
|
||||||
return fmt.Errorf("failed to load OpenAPI document: %v", err)
|
return fmt.Errorf("failed to load OpenAPI document: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +197,7 @@ func (v *schemav2Validator) loadSpec(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
v.specMutex.Unlock()
|
v.specMutex.Unlock()
|
||||||
|
|
||||||
log.Debugf(ctx, "Loaded OpenAPI spec from %s", urlOrPath)
|
log.Debugf(ctx, "Loaded OpenAPI spec from %s: %s", v.config.Type, v.config.Location)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,7 +230,7 @@ func (v *schemav2Validator) reloadExpiredSpec(ctx context.Context) {
|
|||||||
if err := v.loadSpec(ctx); err != nil {
|
if err := v.loadSpec(ctx); err != nil {
|
||||||
log.Errorf(ctx, err, "Failed to reload spec")
|
log.Errorf(ctx, err, "Failed to reload spec")
|
||||||
} else {
|
} else {
|
||||||
log.Debugf(ctx, "Reloaded spec from %s", v.config.URL)
|
log.Debugf(ctx, "Reloaded spec from %s: %s", v.config.Type, v.config.Location)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,8 +63,10 @@ func TestNew(t *testing.T) {
|
|||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{"nil config", nil, true},
|
{"nil config", nil, true},
|
||||||
{"empty URL", &Config{URL: ""}, true},
|
{"empty type", &Config{Type: "", Location: "http://example.com"}, true},
|
||||||
{"invalid URL", &Config{URL: "http://invalid-domain-12345.com/spec.yaml"}, true},
|
{"empty location", &Config{Type: "url", Location: ""}, true},
|
||||||
|
{"invalid type", &Config{Type: "invalid", Location: "http://example.com"}, true},
|
||||||
|
{"invalid URL", &Config{Type: "url", Location: "http://invalid-domain-12345.com/spec.yaml"}, true},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@@ -83,7 +85,7 @@ func TestValidate_ActionExtraction(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
validator, _, err := New(context.Background(), &Config{URL: server.URL, CacheTTL: 3600})
|
validator, _, err := New(context.Background(), &Config{Type: "url", Location: server.URL, CacheTTL: 3600})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create validator: %v", err)
|
t.Fatalf("Failed to create validator: %v", err)
|
||||||
}
|
}
|
||||||
@@ -156,7 +158,7 @@ func TestValidate_NestedValidation(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
validator, _, err := New(context.Background(), &Config{URL: server.URL, CacheTTL: 3600})
|
validator, _, err := New(context.Background(), &Config{Type: "url", Location: server.URL, CacheTTL: 3600})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create validator: %v", err)
|
t.Fatalf("Failed to create validator: %v", err)
|
||||||
}
|
}
|
||||||
@@ -200,7 +202,7 @@ func TestLoadSpec_LocalFile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
tmpFile.Close()
|
tmpFile.Close()
|
||||||
|
|
||||||
validator, _, err := New(context.Background(), &Config{URL: tmpFile.Name(), CacheTTL: 3600})
|
validator, _, err := New(context.Background(), &Config{Type: "file", Location: tmpFile.Name(), CacheTTL: 3600})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to load local spec: %v", err)
|
t.Fatalf("Failed to load local spec: %v", err)
|
||||||
}
|
}
|
||||||
@@ -219,7 +221,7 @@ func TestCacheTTL_DefaultValue(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
validator, _, err := New(context.Background(), &Config{URL: server.URL})
|
validator, _, err := New(context.Background(), &Config{Type: "url", Location: server.URL})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create validator: %v", err)
|
t.Fatalf("Failed to create validator: %v", err)
|
||||||
}
|
}
|
||||||
@@ -235,7 +237,7 @@ func TestValidate_EdgeCases(t *testing.T) {
|
|||||||
}))
|
}))
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
|
|
||||||
validator, _, err := New(context.Background(), &Config{URL: server.URL, CacheTTL: 3600})
|
validator, _, err := New(context.Background(), &Config{Type: "url", Location: server.URL, CacheTTL: 3600})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create validator: %v", err)
|
t.Fatalf("Failed to create validator: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user