question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

$ref does not appear to be inlining top level properties during validation

See original GitHub issue

Thank you for a lovely library! It has saved me and my team countless hours. You all rock.

What version of Ajv are you using? Does the issue happen if you use the latest version? 8.1.0

Ajv options object none

const Ajv = require('ajv');
const schema = require('./src/schema.json');
const config = require('./testy.json');

async function main() {
  const ajv = new Ajv();
  const validateSchema = ajv.compile(schema);
  const isValid = await validateSchema(config);
  if (!isValid) {
    console.error(validateSchema.errors);
  } else {
    console.log('👌');
  }
}
main();

JSON Schema

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Sync Repo Settings Config",
  "description": "Schema for defining the sync repo settings config",
  "additionalProperties": false,
  "type": "object",
  "$ref": "#/definitions/repoConfig",
  "definitions": {
    "repoConfig": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "name": {
          "description": "A simple label for describing the ruleset.",
          "type": "string"
        },
        "selector": {
          "description": "For use in the org/.github repository. A GitHub repo search query that identifies the repositories which should be managed by the given rule.",
          "type": "boolean"
        },
        "squashMergeAllowed": {
          "description": "Whether or not squash-merging is enabled on this repository.",
          "type": "boolean"
        },
        "rebaseMergeAllowed": {
          "description": "Whether or not rebase-merging is enabled on this repository.",
          "type": "boolean"
        },
        "mergeCommitAllowed": {
          "description": "Whether or not PRs are merged with a merge commit on this repository.",
          "type": "boolean"
        },
        "deleteBranchOnMerge": {
          "description": "Either true to allow automatically deleting head branches when pull requests are merged, or false to prevent automatic deletion.",
          "type": "boolean"
        },
        "branchProtectionRules": {
          "description": "Branch protection rules",
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "pattern": {
                "description": "Identifies the protection rule pattern.",
                "type": "string"
              },
              "dismissesStaleReviews": {
                "description": "Will new commits pushed to matching branches dismiss pull request review approvals.",
                "type": "boolean"
              },
              "isAdminEnforced": {
                "description": "Can admins overwrite branch protection.",
                "type": "boolean"
              },
              "requiredApprovingReviewCount": {
                "description": "Number of approving reviews required to update matching branches.",
                "type": "number"
              },
              "requiredStatusCheckContexts": {
                "description": "List of required status check contexts that must pass for commits to be accepted to matching branches.",
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "requiresCodeOwnerReviews": {
                "description": "Are reviews from code owners required to update matching branches.",
                "type": "boolean"
              },
              "requiresCommitSignatures": {
                "description": "Are commits required to be signed.",
                "type": "boolean"
              },
              "requiresStatusChecks": {
                "description": "Are status checks required to update matching branches.",
                "type": "boolean"
              },
              "requiresStrictStatusChecks": {
                "description": "Are branches required to be up to date before merging.",
                "type": "boolean"
              },
              "restrictsPushes": {
                "description": "Is pushing to matching branches restricted.",
                "type": "boolean"
              },
              "restrictsReviewDismissals": {
                "description": "Is dismissal of pull request reviews restricted.",
                "type": "boolean"
              }
            }
          }
        },
        "permissionRules": {
          "description": "List of explicit permissions to add (additive only)",
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "team": {
                "description": "Team slug to provide access.",
                "type": "string"
              },
              "permission": {
                "description": "Permission to provide the team.  Can be one of (pull|push|admin)",
                "type": "string",
                "enum": ["pull", "push", "admin"]
              }
            },
            "required": ["team", "permission"]
          }
        }
      }
    }
  }
}

Sample data

{
  "rebaseMergeAllowed": false,
  "branchProtectionRules": [
     {
        "requiresCodeOwnerReviews": true,
        "requiredStatusCheckContexts": [
           "check1",
           "check2"
        ]
     }
  ],
  "permissionRules": [
     {
        "team": "team1",
        "permission": "push"
     }
  ]
}

Your code

const Ajv = require('ajv');
const schema = require('./src/schema.json');
const config = require('./testy.json');

async function main() {
  const ajv = new Ajv();
  const validateSchema = ajv.compile(schema);
  const isValid = await validateSchema(config);
  if (!isValid) {
    console.error(validateSchema.errors);
  } else {
    console.log('👌');
  }
}
main();

Validation result, data AFTER validation, error messages

[
  {
    instancePath: '',
    schemaPath: '#/additionalProperties',
    keyword: 'additionalProperties',
    params: { additionalProperty: 'rebaseMergeAllowed' },
    message: 'must NOT have additional properties'
  }
]

What results did you expect? Using $ref at the top level, I expected it to identify the properties in repoConfig as laid out. I have additionalProperties set to false, and that seems to be causing the validation errors to come up.

Are you going to resolve the issue? I can always try, but I suspect it will be over my head 🤣 I admit to being fairly new JSON Schema, and I’m not 100% sure I’m doing everything right. I also posted this question to Stack Overflow: https://stackoverflow.com/questions/67248783/how-to-use-ref-with-json-schema-and-top-level-properties

The main reason I filed an issue here is that according to https://www.jsonschemavalidator.net/, everything I’m doing looks to be correct - but again, I am super new at this.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
epoberezkincommented, Apr 26, 2021

No reason to remove “type”: “object”.

additionalProperties do not take into account properties ins sub- or referenced schemas, it considers as “additional” all properties not defined in the current schema object. unevaluatedProperties can be closer to what you are looking for.

1reaction
seriousmecommented, Apr 25, 2021

At top level your file contains a single literal, e.g. 5 or "Hello world"

You can’t have a JSON file that contains:

{ "data": "string"},
"otherProperty": true

or even:

{ "data": "string"},
5

as that is invalid JSON (see https://www.json.org/json-en.html)

By using the $ref at toplevel you import the object schema from “#/definitions/repoConfig” at toplevel. So after dereferencing the $refs in your schema ((what all schema validators do) your schema becomes:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Sync Repo Settings Config",
  "description": "Schema for defining the sync repo settings config",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "name": {
      "description": "A simple label for describing the ruleset.",
      "type": "string"
    },
    "selector": {
      "description": "For use in the org/.github repository. A GitHub repo search query that identifies the repositories which should be managed by the given rule.",
      "type": "boolean"
    },
    "squashMergeAllowed": {
      "description": "Whether or not squash-merging is enabled on this repository.",
      "type": "boolean"
    },
    "rebaseMergeAllowed": {
      "description": "Whether or not rebase-merging is enabled on this repository.",
      "type": "boolean"
    },
    "mergeCommitAllowed": {
      "description": "Whether or not PRs are merged with a merge commit on this repository.",
      "type": "boolean"
    },
    "deleteBranchOnMerge": {
      "description": "Either true to allow automatically deleting head branches when pull requests are merged, or false to prevent automatic deletion.",
      "type": "boolean"
    },
    "branchProtectionRules": {
      "description": "Branch protection rules",
      "type": "array",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "pattern": {
            "description": "Identifies the protection rule pattern.",
            "type": "string"
          },
          "dismissesStaleReviews": {
            "description": "Will new commits pushed to matching branches dismiss pull request review approvals.",
            "type": "boolean"
          },
          "isAdminEnforced": {
            "description": "Can admins overwrite branch protection.",
            "type": "boolean"
          },
          "requiredApprovingReviewCount": {
            "description": "Number of approving reviews required to update matching branches.",
            "type": "number"
          },
     ... etc

The additional properties works on the object. (see also https://json-schema.org/draft/2020-12/json-schema-core.html#additionalProperties)

So if you add another "additionalProperties": "true" you are overriding the false specified in the repoConfig definition. If you leave the `“type”:“object” at toplevel you basically get:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Sync Repo Settings Config",
  "description": "Schema for defining the sync repo settings config",
  "type": "object",
  "additionalProperties": false,
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "name": {
      "description": "A simple label for describing the ruleset.",
      "type": "string"
    },
... etc
}

Which, as far as I know is invalid JSON as the same property is used twice. I do admit that AJV might have been able to flag this better as an error 😉

Does this help ?

Kind regards, Hans

Read more comments on GitHub >

github_iconTop Results From Across the Web

Error Explanations for The W3C Markup Validation Service
One possible cause for this message is that you have attempted to put a block-level element (such as "<p>" or "<table>") inside an...
Read more >
Validation-and-Serialization - Fastify
The shared schemas can be reused through the JSON Schema $ref keyword. ... properties attributes are forgone and the parameters are listed at...
Read more >
Model validation in ASP.NET Core MVC | Microsoft Learn
Model-bound top-level nodes are validated in addition to validating model properties. In the following example from the sample app, ...
Read more >
OpenAPI Specification - Version 3.0.3 - Swagger
(OAS 2.0 documents contain a top-level version field named swagger and value "2.0" .) ... Types that are not accompanied by a format...
Read more >
<iframe>: The Inline Frame element - HTML - MDN Web Docs
The width of the frame in CSS pixels. Default is 300 . Deprecated attributes. These attributes are deprecated and may no longer be...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found