reverting changes from schemav2validator plugin
This commit is contained in:
@@ -239,7 +239,6 @@ func (c *schemaCache) loadSchemaFromPath(ctx context.Context, schemaPath string,
|
|||||||
loadCtx, cancel := context.WithTimeout(ctx, timeout)
|
loadCtx, cancel := context.WithTimeout(ctx, timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
loader.Context = loadCtx
|
loader.Context = loadCtx
|
||||||
log.Debugf(ctx, "Fetching schema from URL: %s (timeout=%v)", schemaPath, timeout)
|
|
||||||
doc, err = loader.LoadFromURI(u)
|
doc, err = loader.LoadFromURI(u)
|
||||||
} else {
|
} else {
|
||||||
// Load from local file (file:// or path)
|
// Load from local file (file:// or path)
|
||||||
@@ -247,7 +246,6 @@ func (c *schemaCache) loadSchemaFromPath(ctx context.Context, schemaPath string,
|
|||||||
if u != nil && u.Scheme == "file" {
|
if u != nil && u.Scheme == "file" {
|
||||||
filePath = u.Path
|
filePath = u.Path
|
||||||
}
|
}
|
||||||
log.Debugf(ctx, "Loading schema from local file: %s", filePath)
|
|
||||||
doc, err = loader.LoadFromFile(filePath)
|
doc, err = loader.LoadFromFile(filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,13 +254,9 @@ func (c *schemaCache) loadSchemaFromPath(ctx context.Context, schemaPath string,
|
|||||||
return nil, fmt.Errorf("failed to load schema from %s: %w", schemaPath, err)
|
return nil, fmt.Errorf("failed to load schema from %s: %w", schemaPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf(ctx, "Successfully loaded schema from: %s", schemaPath)
|
|
||||||
|
|
||||||
// Validate loaded schema (non-blocking, just log warnings)
|
// Validate loaded schema (non-blocking, just log warnings)
|
||||||
if err := doc.Validate(ctx); err != nil {
|
if err := doc.Validate(ctx); err != nil {
|
||||||
log.Warnf(ctx, "Schema validation warnings for %s (may indicate unresolved $ref): %v", schemaPath, err)
|
log.Debugf(ctx, "Schema validation warnings for %s: %v", schemaPath, err)
|
||||||
} else {
|
|
||||||
log.Debugf(ctx, "Schema self-validation passed for: %s", schemaPath)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.set(urlHash, doc, ttl)
|
c.set(urlHash, doc, ttl)
|
||||||
@@ -321,12 +315,9 @@ func transformContextToSchemaURL(contextURL string) string {
|
|||||||
// findSchemaByType finds a schema in the document by @type value.
|
// findSchemaByType finds a schema in the document by @type value.
|
||||||
func findSchemaByType(ctx context.Context, 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 {
|
||||||
log.Errorf(ctx, fmt.Errorf("no schemas in document"), "Schema lookup failed for @type: %s — document has no components.schemas section", typeName)
|
|
||||||
return nil, fmt.Errorf("no schemas found in document")
|
return nil, fmt.Errorf("no schemas found in document")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf(ctx, "Looking up @type: %s in document with %d schema(s)", typeName, len(doc.Components.Schemas))
|
|
||||||
|
|
||||||
// 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)
|
log.Debugf(ctx, "Found schema by direct match: %s", typeName)
|
||||||
@@ -346,22 +337,6 @@ func findSchemaByType(ctx context.Context, doc *openapi3.T, typeName string) (*o
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log available schema names and x-jsonld.@type values to help diagnose the mismatch
|
|
||||||
available := make([]string, 0, len(doc.Components.Schemas))
|
|
||||||
for name, schema := range doc.Components.Schemas {
|
|
||||||
entry := name
|
|
||||||
if schema.Value != nil {
|
|
||||||
if xJsonld, ok := schema.Value.Extensions["x-jsonld"].(map[string]interface{}); ok {
|
|
||||||
if atType, ok := xJsonld["@type"].(string); ok {
|
|
||||||
entry = fmt.Sprintf("%s (x-jsonld.@type=%s)", name, atType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
available = append(available, entry)
|
|
||||||
}
|
|
||||||
log.Errorf(ctx, fmt.Errorf("no schema found for @type: %s", typeName),
|
|
||||||
"Schema lookup failed — @type %q not matched by name or x-jsonld.@type. Available schemas: %v", typeName, available)
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("no schema found for @type: %s", typeName)
|
return nil, fmt.Errorf("no schema found for @type: %s", typeName)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,18 +373,9 @@ func (c *schemaCache) validateReferencedObject(
|
|||||||
// Load schema with timeout (supports URL or local file)
|
// Load schema with timeout (supports URL or local file)
|
||||||
doc, err := c.loadSchemaFromPath(ctx, schemaPath, ttl, timeout)
|
doc, err := c.loadSchemaFromPath(ctx, schemaPath, ttl, timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(ctx, err, "Failed to load schema for @type: %s from URL: %s (derived from @context: %s)",
|
|
||||||
obj.Type, schemaPath, obj.Context)
|
|
||||||
return fmt.Errorf("at %s: %w", obj.Path, err)
|
return fmt.Errorf("at %s: %w", obj.Path, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log doc structure to help diagnose schema-not-found issues
|
|
||||||
if doc.Components == nil || doc.Components.Schemas == nil {
|
|
||||||
log.Warnf(ctx, "Schema doc loaded from %s has no components.schemas — @type %s cannot be resolved", schemaPath, obj.Type)
|
|
||||||
} else {
|
|
||||||
log.Debugf(ctx, "Schema doc loaded from %s contains %d schema(s) in components", schemaPath, len(doc.Components.Schemas))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find schema by @type
|
// Find schema by @type
|
||||||
schema, err := findSchemaByType(ctx, doc, obj.Type)
|
schema, err := findSchemaByType(ctx, doc, obj.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -157,27 +155,6 @@ func (v *schemav2Validator) initialise(ctx context.Context) error {
|
|||||||
return v.loadSpec(ctx)
|
return v.loadSpec(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// readFromURI fetches a URL and returns its raw bytes.
|
|
||||||
func readFromURI(ctx context.Context, u *url.URL) ([]byte, error) {
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to build request for %s: %w", u, err)
|
|
||||||
}
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to fetch %s: %w", u, err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
data, err := io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to read body from %s: %w", u, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf(ctx, "External ref resolved: %s (%d bytes, HTTP %d)", u, len(data), resp.StatusCode)
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// loadSpec loads the OpenAPI spec from URL or local path.
|
// loadSpec loads the OpenAPI spec from URL or local path.
|
||||||
func (v *schemav2Validator) loadSpec(ctx context.Context) error {
|
func (v *schemav2Validator) loadSpec(ctx context.Context) error {
|
||||||
loader := openapi3.NewLoader()
|
loader := openapi3.NewLoader()
|
||||||
@@ -185,30 +162,6 @@ func (v *schemav2Validator) loadSpec(ctx context.Context) error {
|
|||||||
// Allow external references
|
// Allow external references
|
||||||
loader.IsExternalRefsAllowed = true
|
loader.IsExternalRefsAllowed = true
|
||||||
|
|
||||||
// Log every URI kin-openapi resolves so we can trace the full $ref chain.
|
|
||||||
//
|
|
||||||
// Exception: json-schema.org meta-schema URLs are intercepted and short-
|
|
||||||
// circuited with an empty schema object. kin-openapi follows the $schema
|
|
||||||
// dialect URI declared in each Beckn schema file, which leads it deep into
|
|
||||||
// the JSON Schema 2020-12 meta-schema hierarchy. Those meta-schemas use
|
|
||||||
// boolean schemas (e.g. "additionalProperties": false, "items": false) that
|
|
||||||
// are valid JSON Schema but cannot be parsed by kin-openapi's OpenAPI Schema
|
|
||||||
// Object model. Since the meta-schemas carry no Beckn-specific content and
|
|
||||||
// are not used for validation, returning {} is safe and correct.
|
|
||||||
loader.ReadFromURIFunc = func(loader *openapi3.Loader, u *url.URL) ([]byte, error) {
|
|
||||||
if u.Host == "json-schema.org" {
|
|
||||||
log.Debugf(ctx, "Skipping json-schema.org meta-schema (not an OpenAPI schema): %s", u)
|
|
||||||
return []byte(`{}`), nil
|
|
||||||
}
|
|
||||||
log.Debugf(ctx, "Resolving external $ref: %s", u)
|
|
||||||
data, err := readFromURI(ctx, u)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf(ctx, err, "Failed to resolve external $ref: %s", u)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var doc *openapi3.T
|
var doc *openapi3.T
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@@ -232,10 +185,9 @@ func (v *schemav2Validator) loadSpec(ctx context.Context) error {
|
|||||||
return fmt.Errorf("failed to load OpenAPI document: %v", err)
|
return fmt.Errorf("failed to load OpenAPI document: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate spec — this also triggers resolution of all $refs including external ones.
|
// Validate spec (skip strict validation to allow JSON Schema keywords)
|
||||||
// Log the error but treat as non-fatal to allow JSON Schema keywords not in OpenAPI 3.0.
|
|
||||||
if err := doc.Validate(ctx); err != nil {
|
if err := doc.Validate(ctx); err != nil {
|
||||||
log.Errorf(ctx, err, "Spec validation error (external refs may not have resolved): %v", err)
|
log.Debugf(ctx, "Spec validation warnings (non-fatal): %v", err)
|
||||||
} else {
|
} else {
|
||||||
log.Debugf(ctx, "Spec validation passed")
|
log.Debugf(ctx, "Spec validation passed")
|
||||||
}
|
}
|
||||||
@@ -243,10 +195,6 @@ func (v *schemav2Validator) loadSpec(ctx context.Context) error {
|
|||||||
// Build action→schema index for O(1) lookup
|
// Build action→schema index for O(1) lookup
|
||||||
actionSchemas := v.buildActionIndex(ctx, doc)
|
actionSchemas := v.buildActionIndex(ctx, doc)
|
||||||
|
|
||||||
if len(actionSchemas) == 0 {
|
|
||||||
log.Errorf(ctx, fmt.Errorf("no actions indexed"), "No actions indexed from spec — external $refs may not have resolved. Check that IsExternalRefsAllowed=true and the referenced URLs are reachable and return valid YAML/JSON")
|
|
||||||
}
|
|
||||||
|
|
||||||
v.specMutex.Lock()
|
v.specMutex.Lock()
|
||||||
v.spec = &cachedSpec{
|
v.spec = &cachedSpec{
|
||||||
doc: doc,
|
doc: doc,
|
||||||
|
|||||||
Reference in New Issue
Block a user