update: logs and comments also ref refreshLoop method
This commit is contained in:
@@ -48,7 +48,7 @@ type cachedDomainSchema struct {
|
|||||||
|
|
||||||
// validateExtendedSchemas validates all objects with @context against their schemas.
|
// validateExtendedSchemas validates all objects with @context against their schemas.
|
||||||
func (v *schemav2Validator) validateExtendedSchemas(ctx context.Context, body interface{}) error {
|
func (v *schemav2Validator) validateExtendedSchemas(ctx context.Context, body interface{}) error {
|
||||||
// Extract "message" object - only scan inside message, not root
|
// Extract "message" object - only scan inside message
|
||||||
bodyMap, ok := body.(map[string]interface{})
|
bodyMap, ok := body.(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("body is not a valid JSON object")
|
return fmt.Errorf("body is not a valid JSON object")
|
||||||
@@ -291,28 +291,30 @@ func findReferencedObjects(data interface{}, path string) []referencedObject {
|
|||||||
|
|
||||||
// transformContextToSchemaURL transforms @context URL to schema URL.
|
// transformContextToSchemaURL transforms @context URL to schema URL.
|
||||||
func transformContextToSchemaURL(contextURL string) string {
|
func transformContextToSchemaURL(contextURL string) string {
|
||||||
// Hardcoded transformation: context.jsonld -> attributes.yaml
|
// transformation: context.jsonld -> attributes.yaml
|
||||||
return strings.Replace(contextURL, "context.jsonld", "attributes.yaml", 1)
|
return strings.Replace(contextURL, "context.jsonld", "attributes.yaml", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// findSchemaByType finds a schema in the document by @type value.
|
// findSchemaByType finds a schema in the document by @type value.
|
||||||
func findSchemaByType(doc *openapi3.T, typeName string) (*openapi3.SchemaRef, error) {
|
func findSchemaByType(ctx context.Context, doc *openapi3.T, typeName string) (*openapi3.SchemaRef, error) {
|
||||||
if doc.Components == nil || doc.Components.Schemas == nil {
|
if doc.Components == nil || doc.Components.Schemas == nil {
|
||||||
return nil, fmt.Errorf("no schemas found in document")
|
return nil, fmt.Errorf("no schemas found in document")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try direct match by schema name
|
// Try direct match by schema name
|
||||||
if schema, exists := doc.Components.Schemas[typeName]; exists {
|
if schema, exists := doc.Components.Schemas[typeName]; exists {
|
||||||
|
log.Debugf(ctx, "Found schema by direct match: %s", typeName)
|
||||||
return schema, nil
|
return schema, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: Try x-jsonld.@type match
|
// Fallback: Try x-jsonld.@type match
|
||||||
for _, schema := range doc.Components.Schemas {
|
for name, schema := range doc.Components.Schemas {
|
||||||
if schema.Value == nil {
|
if schema.Value == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if xJsonld, ok := schema.Value.Extensions["x-jsonld"].(map[string]interface{}); ok {
|
if xJsonld, ok := schema.Value.Extensions["x-jsonld"].(map[string]interface{}); ok {
|
||||||
if atType, ok := xJsonld["@type"].(string); ok && atType == typeName {
|
if atType, ok := xJsonld["@type"].(string); ok && atType == typeName {
|
||||||
|
log.Debugf(ctx, "Found schema by x-jsonld.@type match: %s (mapped to %s)", typeName, name)
|
||||||
return schema, nil
|
return schema, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -358,7 +360,7 @@ func (c *schemaCache) validateReferencedObject(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find schema by @type
|
// Find schema by @type
|
||||||
schema, err := findSchemaByType(doc, obj.Type)
|
schema, err := findSchemaByType(ctx, doc, obj.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(ctx, err, "Schema not found for @type: %s at path: %s", obj.Type, obj.Path)
|
log.Errorf(ctx, err, "Schema not found for @type: %s at path: %s", obj.Type, obj.Path)
|
||||||
return fmt.Errorf("at %s: %w", obj.Path, err)
|
return fmt.Errorf("at %s: %w", obj.Path, err)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ type schemav2Validator struct {
|
|||||||
config *Config
|
config *Config
|
||||||
spec *cachedSpec
|
spec *cachedSpec
|
||||||
specMutex sync.RWMutex
|
specMutex sync.RWMutex
|
||||||
schemaCache *schemaCache // NEW: cache for referenced schemas
|
schemaCache *schemaCache // cache for extended schemas
|
||||||
}
|
}
|
||||||
|
|
||||||
// cachedSpec holds a cached OpenAPI spec.
|
// cachedSpec holds a cached OpenAPI spec.
|
||||||
@@ -43,7 +43,7 @@ type Config struct {
|
|||||||
Location string // URL, file path, or directory path
|
Location string // URL, file path, or directory path
|
||||||
CacheTTL int
|
CacheTTL int
|
||||||
|
|
||||||
// NEW: Extended Schema configuration
|
// Extended Schema configuration
|
||||||
EnableExtendedSchema bool
|
EnableExtendedSchema bool
|
||||||
ExtendedSchemaConfig ExtendedSchemaConfig
|
ExtendedSchemaConfig ExtendedSchemaConfig
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ func New(ctx context.Context, config *Config) (*schemav2Validator, func() error,
|
|||||||
config: config,
|
config: config,
|
||||||
}
|
}
|
||||||
|
|
||||||
// NEW: Initialize extended schema cache if enabled
|
// Initialize extended schema cache if enabled
|
||||||
if config.EnableExtendedSchema {
|
if config.EnableExtendedSchema {
|
||||||
maxSize := 100
|
maxSize := 100
|
||||||
if config.ExtendedSchemaConfig.MaxCacheSize > 0 {
|
if config.ExtendedSchemaConfig.MaxCacheSize > 0 {
|
||||||
@@ -136,13 +136,13 @@ func (v *schemav2Validator) Validate(ctx context.Context, reqURL *url.URL, data
|
|||||||
return v.formatValidationError(err)
|
return v.formatValidationError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf(ctx, "Core schema validation passed for action: %s", action)
|
log.Debugf(ctx, "base schema validation passed for action: %s", action)
|
||||||
|
|
||||||
// NEW: Extended Schema validation (if enabled)
|
// Extended Schema validation (if enabled)
|
||||||
if v.config.EnableExtendedSchema && v.schemaCache != nil {
|
if v.config.EnableExtendedSchema && v.schemaCache != nil {
|
||||||
log.Debugf(ctx, "Starting Extended Schema validation for action: %s", action)
|
log.Debugf(ctx, "Starting Extended Schema validation for action: %s", action)
|
||||||
if err := v.validateExtendedSchemas(ctx, jsonData); err != nil {
|
if err := v.validateExtendedSchemas(ctx, jsonData); err != nil {
|
||||||
// Extended Schema failure - return error (same behavior as core schema)
|
// Extended Schema failure - return error.
|
||||||
log.Debugf(ctx, "Extended Schema validation failed for action %s: %v", action, err)
|
log.Debugf(ctx, "Extended Schema validation failed for action %s: %v", action, err)
|
||||||
return v.formatValidationError(err)
|
return v.formatValidationError(err)
|
||||||
}
|
}
|
||||||
@@ -214,8 +214,10 @@ func (v *schemav2Validator) refreshLoop(ctx context.Context) {
|
|||||||
coreTicker := time.NewTicker(time.Duration(v.config.CacheTTL) * time.Second)
|
coreTicker := time.NewTicker(time.Duration(v.config.CacheTTL) * time.Second)
|
||||||
defer coreTicker.Stop()
|
defer coreTicker.Stop()
|
||||||
|
|
||||||
// NEW: Ticker for extended schema cleanup
|
// Ticker for extended schema cleanup
|
||||||
var refTicker *time.Ticker
|
var refTicker *time.Ticker
|
||||||
|
var refTickerCh <-chan time.Time // Default nil, blocks forever
|
||||||
|
|
||||||
if v.config.EnableExtendedSchema {
|
if v.config.EnableExtendedSchema {
|
||||||
ttl := v.config.ExtendedSchemaConfig.CacheTTL
|
ttl := v.config.ExtendedSchemaConfig.CacheTTL
|
||||||
if ttl <= 0 {
|
if ttl <= 0 {
|
||||||
@@ -223,16 +225,16 @@ func (v *schemav2Validator) refreshLoop(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
refTicker = time.NewTicker(time.Duration(ttl) * time.Second)
|
refTicker = time.NewTicker(time.Duration(ttl) * time.Second)
|
||||||
defer refTicker.Stop()
|
defer refTicker.Stop()
|
||||||
|
refTickerCh = refTicker.C
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if refTicker != nil {
|
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
case <-coreTicker.C:
|
case <-coreTicker.C:
|
||||||
v.reloadExpiredSpec(ctx)
|
v.reloadExpiredSpec(ctx)
|
||||||
case <-refTicker.C:
|
case <-refTickerCh:
|
||||||
if v.schemaCache != nil {
|
if v.schemaCache != nil {
|
||||||
count := v.schemaCache.cleanupExpired()
|
count := v.schemaCache.cleanupExpired()
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
@@ -240,14 +242,6 @@ func (v *schemav2Validator) refreshLoop(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return
|
|
||||||
case <-coreTicker.C:
|
|
||||||
v.reloadExpiredSpec(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user