Skip to content

Adapter: validate resourceAttributes against the schema packs, and name the plugins after their capabilities #16

Description

@ameersohel45

What needs to be done

Two related pieces of work on the provider adapter, done together because the second is what makes the first legible.

Enforce the capability schema packs on resourceAttributes. Base validation checks the Beckn v2 envelope and treats resourceAttributes as a free-form object, so nothing checks a capability's own attributes at all — a wrong unit, a missing required attribute or an undefined field all pass, and become a consumer's problem to discover later. The schemav2validator plugin already has an extended layer for exactly this, and it was configured but switched off.

Name the capability plugins after their capabilities. weather and mandi name a domain; the payloads they serve name a capability. Renaming them to WeatherObservation and MandiPrice puts the binding key, the schema pack and the plugin that answers for it into one vocabulary.

Why extended validation could not simply be switched on

Two separate blockers, both needing a change rather than a config flag.

The validator removed the key the packs require. validateReferencedObject stripped both @context and @type from every object before validating it. That suited a schema which closes itself with additionalProperties: false and never mentions either key — which is what the plugin's existing tests use, so the behaviour could not just be reversed. The OAN packs do the opposite: they declare @type and list it in allOf[].required. So a conforming payload was rejected for a missing @type that it had in fact sent and the validator had just taken out. No configuration could bridge that.

The fix is to decide per key, by asking the resolved schema whether it declares that key — as a property or in required, anywhere in its composition tree, since the packs declare @type one level down inside allOf. Both schema styles then validate, with no config switch and without either style having to know about the other. not and if are deliberately not walked: naming a property under not forbids it and under if only selects a branch, so neither means the key is permitted.

Keeping @type also means its const is now checked, so a resource claiming a @type the capability does not declare no longer passes silently.

The @context on the wire did not resolve. The payloads declared https://schemas.openagrinet.global/..., which does not resolve, and a failed schema fetch rejects the payload — so enabling the layer would have refused every request. A local mounted directory of schema files was tried first and then removed: the published packs do serve context.jsonld, so pointing @context at them makes the fetch the validator already knows how to do work, and the mount, the fetch script and the startup dependency all become unnecessary.

The validator swaps context.jsonld for attributes.yaml to reach the schema beside it, so the revision a payload is judged against comes from the payload rather than from adapter config. Nothing in this repo can go stale against what the network publishes. extendedSchema_allowedDomains is narrowed to the host @context actually resolves to and is now load-bearing: an @context on any other host is refused before a fetch is attempted.

What extended validation does and does not enforce

Worth recording, because a green result is otherwise misleading. kin-openapi v0.144.0 has If, Then and Else struct fields, so the keywords look supported, but visitJSON never dispatches them — verified empirically rather than inferred from the struct.

Enforced: types, string formats, enum, const, required, minItems, additionalProperties, not, and allOf/anyOf/oneOf.

Not enforced: if/then/else, which in the OAN packs is every rule predicated on informationMode.

This matters beyond a footnote. The packs require informationMode and allow only OnDemand or Direct. OnDemand forbids exactly the fields a select must carry to be a question at all — location for weather, market and validity for mandi — while Direct requires answer data a consumer does not have. The packs have no query mode, so our select requests are not pack-conformant on paper and pass only because the forbidding lives under then. That is a gap to raise on the schema packs, not something to work around in the payloads.

Renaming

The .so basename is the plugin id the adapter config refers to, so the package directories, the package clauses, install/build-plugins.sh and the providerSteps and steps entries in config/oan-provider-adapter.yaml all move together. Both package docs claimed "one package per schema pack family", which the new names contradict, so they now say what is true: one package per capability, named for the capability, with the binding keys it answers to still configuration.

This is a breaking change for any deployment: a plugin id that is not one of the built-in step names is looked up among the loaded plugins, so an adapter running a config that names WeatherObservation against an image built before the rename exits with unrecognized step: WeatherObservation. Config and image have to be rolled forward and back together.

Verification

The full suite passes — 63 packages — with four new tests covering the per-key strip, including a pack-shaped schema that requires @type and one that rejects a wrong @type.

End to end against a running stack, with the renamed plugins loading from a freshly built image: publish and select pass for both capabilities, the first payload logs fetching from network and later ones LRU cache hit, and the local collection is 51 of 51.

Negative controls, so a pass is not just an absence of checking: a value outside an enum, a bare date in a date-time field, and a field undefined in a closed sub-object are each rejected with SCH_SCHEMA_VALIDATION_FAILED and the JSON path that failed — the last of those resolving through a schema.beckn.io $ref — and an @context on a host outside the allowlist is rejected with SCH_INVALID_JSONLD_CONTEXT before any fetch.

Known and not addressed here

The published weather mapping emits parameters[].aggregation, which the pack forbids: its parameter item is {parameter, value, unit} with additionalProperties: false, and the parameter enum has no minimum or maximum variants, so a daily minimum and maximum temperature cannot be told apart at all. Nothing validates responses today, so it does not bite until a consumer checks. Fixing it needs a change to the packs rather than to the mapping.

Where

Branch feat/8-capability-schema-conformance, four commits:

  • 3853264 fix(schemav2validator): keep the JSON-LD keys a schema declares
  • a1db4e7 refactor(plugins): name the capability plugins after their capabilities
  • 16a63e5 feat(config): validate resource attributes against their schema packs
  • 0769807 refactor(config): resolve capability schemas from the payload's @context

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions