Skip to content

Migrate to opis/json-schema v2#24

Open
dcgoodwin2112 wants to merge 2 commits into
masterfrom
opis-v2-migration
Open

Migrate to opis/json-schema v2#24
dcgoodwin2112 wants to merge 2 commits into
masterfrom
opis-v2-migration

Conversation

@dcgoodwin2112
Copy link
Copy Markdown

@dcgoodwin2112 dcgoodwin2112 commented May 7, 2026

Summary

Migrate to opis/json-schema v2 (^2.4). v1 has been unmaintained for years and is increasingly hard to keep on modern PHP/Drupal stacks. This is a coordinated upgrade with downstream DKAN core (PR GetDKAN/dkan#4706).

What changed

  • composer.json: opis/json-schema bumped from v1 to ^2.4.
  • New exception: RootedData\Exception\InvalidSchemaException (extends \InvalidArgumentException). Replaces references to v1's Opis\JsonSchema\Exception\InvalidSchemaException, which no longer exists.
  • Constructor (RootedJsonData::__construct): tightened to accept only is_object($schema) || is_bool($schema). v1's Schema::fromJsonString() quietly accepted bare strings; v2 has no analogous helper, so we require pre-parsed schemas (matching the JSON Schema spec, where a schema is an object or boolean).
  • validate(): pre-decodes the schema via json_decode($schema, false) before passing to Validator::validate(). v2's URI-first parse path would otherwise treat URL-shaped strings as remote refs, and bare booleans as URIs — both regressions vs. v1.
  • Error path walking: v2's root error is the container keyword (e.g. properties); leaf errors live under error()->subErrors(). The path-builder now walks to a leaf so error pointer + args remain meaningful.
  • Message formatting: Opis\JsonSchema\Errors\ErrorFormatter::formatErrorMessage() is used for the non-type fallback so v2 message templates (e.g. {missing}) are interpolated rather than emitted verbatim.

Breaking changes for consumers

Change Impact
Constructor rejects string/array/null schemas Callers that passed JSON strings must json_decode(..., false) first
Opis\JsonSchema\Schema is now an interface Code that referenced the v1 concrete class needs updating; in this library it's no longer imported
Error message wording differs slightly from v1 Consumers asserting on exact strings need to update fixtures
Opis\JsonSchema\Exception\InvalidSchemaException no longer exists Replaced with RootedData\Exception\InvalidSchemaException

JSON Schema draft compatibility

opis/json-schema v2 ships parsers for draft-06, draft-07, 2019-09, and 2020-12. Draft-04 is not supported — schemas declaring "$schema": "http://json-schema.org/draft-04/schema#" will throw Opis\JsonSchema\Exceptions\ParseException at validation time.

Why this matters for downstream consumers

Many existing schemas in the wild — including DKAN's own dataset/distribution/publisher schemas, derived from Project Open Data v1.1 — were authored against draft-04. These need to be migrated.

Migration cookbook

For each schema declaring draft-04:

  1. Bump $schema declaration:

    - "$schema": "http://json-schema.org/draft-04/schema#"
    + "$schema": "http://json-schema.org/draft-07/schema#"
  2. Rename id$id (the keyword was renamed at the draft-04 → draft-06 boundary):

    - "id": "https://example.com/schemas/foo.json"
    + "$id": "https://example.com/schemas/foo.json"
  3. Use absolute URIs for $id values. v2 rejects relative URIs at the schema root. Pick a stable namespace your project owns (https://schemas.example.com/...).

  4. exclusiveMinimum / exclusiveMaximum (if used): change from boolean modifier to numeric value. DKAN's schemas didn't use these, but custom schemas might:

    - "minimum": 0, "exclusiveMinimum": true
    + "exclusiveMinimum": 0

Why draft-07 (and not draft-06)?

The $id rename happens at the draft-04 → draft-06 boundary, so picking draft-06 instead of draft-07 would not have spared us the renaming work. Draft-07 additionally provides keywords DKAN actually uses (readOnly), so it's the natural target. Drafts 2019-09 and 2020-12 introduced further keyword splits (dependentRequired/dependentSchemas, $defs over definitions) that would have required broader rewrites without functional benefit.

Test plan

  • All 25 unit tests pass against opis/json-schema 2.6.0 (was 21 tests on v1; 4 new tests cover boolean schemas, schema-type rejection, and the formatErrorMessage fallback path)
  • composer install clean on PHP 8.1+
  • qlty quality gate passing
  • Verified end-to-end against DKAN core PR #4706 once both merge

Related

Co-authored-by: Copilot copilot@github.com

- Replace v1 Schema::fromJsonString with explicit json_decode + InvalidSchemaException
- Replace v1 getFirstError/keywordArgs with v2 error()/args(), walking subErrors() to a leaf
- Add local RootedData\Exception\InvalidSchemaException to replace the dropped opis class
- Update tests to import the local exception and catch the v2 SchemaException interface

All 21 existing tests pass against opis/json-schema 2.6.0.
- Reject non-object, non-boolean schemas at construction with clear errors
  (matches JSON Schema spec and v1 fromJsonString semantics)
- Pre-decode schema in validate() so v2 never URI-parses bare strings —
  fixes regression on boolean schemas (true/false) and avoids any
  unintended HTTP fetches for URL-shaped schemas
- Use ErrorFormatter::formatErrorMessage in the non-type fallback so v2
  message templates ({missing}, etc.) are properly interpolated
- Add tests for boolean schemas, schema type rejection, and the
  fallback message format
@dcgoodwin2112 dcgoodwin2112 marked this pull request as ready for review May 8, 2026 12:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant