首页 > 解决方案 > 如何将 $ref 与 json-schema 和顶级属性一起使用

问题描述

你好朋友 :) 我有一个 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",
      "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"
      }
   ]
}

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

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

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

标签: jsonjsonschemaajv

解决方案


虽然 Ether 的答案是正确的,但实际上这里有一个XY 问题

(我认为 ajv 实际上允许$ref并且definitions在根级别。这样做的合法性是有争议且不清楚的。)

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

使用“additionalProperties”进行验证仅适用于与“properties”中的任何名称不匹配且不匹配“patternProperties”中的任何正则表达式的实例名称的子值。

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

简而言之,propertiesandpatternProperties 在同一模式对象中与additionalProperties only

如果您需要继续使用 JSON Schema 的 draft-07,那么您需要properties在同一级别重新定义 (您可以给它们赋值true)。

如果您可以迁移到更新版本的 JSON Schema,您可以使用unevaluatedProperties,简单来说,可以“透视”应用程序关键字。它有点复杂,但它会按照您的预期行事。


推荐阅读