如何将 $ref 与 json-schema 和顶级属性一起使用

How to use $ref with json-schema and top level properties

你好朋友 :) 我有一个 JSON 架构,如下所示:

{
  "$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",
      "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"]
          }
        }
      }
    }
  }
}

我正在尝试创建一个可重复使用的 repoConfig 架构,它也可以在我的 json 文档的根目录中表示。我很挑剔,关闭了 additionalProperties 设置,只是为了让 100% 一切都按预期工作。当我尝试验证此文档时:

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

我在验证期间从 ajv npm 模块收到以下错误:

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

如果我获取在我的共享 repoConfig 对象中定义的 properties 的集合,并将它们直接内联到我的模式文档的根目录中,验证器将按预期工作。

在草稿 7 及更早版本中,您不能将 $ref 与任何其他关键字结合使用。要解决这个问题,您可以将 $ref 包装在 allOf:

"allOf": [ { "$ref": "#/definitions/repoConfig" } ],
... rest of your schema...

虽然 Ether 的回答是正确的,但实际上您在这里有一个 XY problem

(我认为 ajv 实际上在根级别允许 $refdefinitions。其合法性尚有争议且不清楚。)

您的问题在于您使用 additionalProperties,因为它无法“看穿”涂抹器,例如 $ref

Validation with "additionalProperties" applies only to the child values of instance names that do not match any names in "properties", and do not match any regular expression in "patternProperties".

https://datatracker.ietf.org/doc/html/draft-handrews-json-schema-validation-01#section-6.5.6

简单来说,propertiespatternProperties additionalProperties 相同的架构对象 .

如果需要继续使用JSON Schema的draft-07,那么需要重新定义同层的properties(可以给它们取值true ).

如果您可以移动到更新版本的 JSON 模式,您可以使用 unevaluatedProperties,简单来说,它可以“看穿”应用程序关键字。它有点复杂,但它会按照您的预期运行。