如何在尚未发布且尚未测试的情况下,为我的 JSON schema 的 $id 指定绝对 URL?

huangapple go评论67阅读模式
英文:

How do I give my JSON schema an absolute URL for its $id when I haven't published it yet because it hasn't been tested yet?

问题

我正在组织JSON模式,并且希望使用$ref来减少模式的重复。我将有许多模式,每个模式都会使用共同的子模式。我想在发布模式之前通过编写单元测试来测试模式的有效性,测试会断言在给定特定输入的情况下,输入是否被视为有效或无效。我会使用一个我信任的JSON模式库来进行测试,以确保库本身是正确的(这样我就只测试模式,而不是库本身)。

我感到困惑的是,在我发布模式之前(我希望在本地运行测试和CI/CD期间这样做),我需要使用相对本地路径来加载模式,像这样:

"pet": { "$ref": "file://./schemas/components/pet.schema.json" }

这是因为该pet模式尚未发布到URL上。它还没有通过自动化测试进行验证。这对于运行测试来说已经足够好了,而且对于将模式打包到Docker镜像中以便在应用程序启动时从磁盘加载模式也很有效。

但是,如果我将其中一个顶级模式(使用$ref)发布到绝对URL后交给其他人使用,他们的程序将无法加载,因为我在单元测试中使用的路径只适用于我自己。

我发现我必须使用绝对URL来发布我的模式,以便它们可以在消费程序中使用。我最终以这种方式发布了模式https://mattwelke.github.io/go-jsonschema-ref-docker-example/schemas/person.1-0-0.schema.json和https://mattwelke.github.io/go-jsonschema-ref-docker-example/schemas/components/pet.1-0-0.schema.json。我通过编写以下程序来测试它们在消费程序中的工作情况:

package main

import (
	"fmt"

	"github.com/xeipuuv/gojsonschema"
)

func main() {
	schemaLoader := gojsonschema.NewReferenceLoader("https://mattwelke.github.io/go-jsonschema-ref-docker-example/schemas/person.1-0-0.schema.json")

	jsonStr := `
	{
		"name": "Matt",
		"pet": {
			"name": "Shady"
		}
	}
	`

	documentLoader := gojsonschema.NewStringLoader(jsonStr)

	result, err := gojsonschema.Validate(schemaLoader, documentLoader)
	if err != nil {
		panic(fmt.Errorf("could not validate: %w", err))
	}

	if result.Valid() {
		fmt.Printf("The document is valid.\n")
	} else {
		fmt.Printf("The document is not valid. See errors:\n")
		for _, desc := range result.Errors() {
			fmt.Printf("- %s\n", desc)
		}
	}
}

这将产生以下预期输出:

The document is valid.

所以我对这个“先有鸡还是先有蛋”的情况感到困惑。

只要我在发布模式之前不进行单元测试,我就能够发布可供使用的模式。

只要满足以下条件,我就能够进行单元测试:

  • 我不想以经过单元测试验证的形式发布它们。
  • 我的应用程序可以通过HTTPS在启动时加载它们,而不是从磁盘加载它们。我对此有些担心,因为我不希望Web服务器成为应用程序启动失败的原因。

我希望能够了解如何同时实现这两个目标。

英文:

I'm putting together JSON schemas and I'd like to use $ref to DRY my schemas. I'll have many schemas that will each use common subschemas. I want to unit test my schemas before publishing them by writing unit tests that assert that, given certain input, the input is deemed valid or invalid, using a JSON schema library that I trust to be correct (so that I'm just testing my schemas, not the library).

Where I get confused is that in order to load my schemas before I've published them (which I want to do while running tests locally and during CI/CD), I need to use relative local paths like this:

"pet": { "$ref": "file://./schemas/components/pet.schema.json" }

That's because that pet schema hasn't been published to a URL yet. It hasn't been verified by automated tests that it's correct yet. This works well enough for running tests, and it also worked well for packaging inside a Docker image so that the schemas could be loaded from disk as the app starts up.

But then, if I were to give one of the top level schemas to someone (that leverages $ref) after publishing it to an absolute URL, it wouldn't load in their program because of that path that I used that worked only for my unit testing.

I found that I had to publish my schemas using absolute URLs in order for them to be used in consuming programs. I ended up publishing the schemas https://mattwelke.github.io/go-jsonschema-ref-docker-example/schemas/person.1-0-0.schema.json and https://mattwelke.github.io/go-jsonschema-ref-docker-example/schemas/components/pet.1-0-0.schema.json that way. I tested that they worked fine in a consuming program by writing the program:

package main

import (
	"fmt"

	"github.com/xeipuuv/gojsonschema"
)

func main() {
	schemaLoader := gojsonschema.NewReferenceLoader("https://mattwelke.github.io/go-jsonschema-ref-docker-example/schemas/person.1-0-0.schema.json")

	jsonStr := `
	{
		"name": "Matt",
		"pet": {
			"name": "Shady"
		}
	}
	`

	documentLoader := gojsonschema.NewStringLoader(jsonStr)

	result, err := gojsonschema.Validate(schemaLoader, documentLoader)
	if err != nil {
		panic(fmt.Errorf("could not validate: %w", err))
	}

	if result.Valid() {
		fmt.Printf("The document is valid.\n")
	} else {
		fmt.Printf("The document is not valid. See errors:\n")
		for _, desc := range result.Errors() {
			fmt.Printf("- %s\n", desc)
		}
	}
}

Which resulted in the following expected output:

The document is valid.

So I'm confused about this "chicken and egg" situation.

I was able to publish schemas that could be used, as long as I didn't unit test them before publishing them.

And I was able to unit test schemas as long as:

  • I didn't want to publish them in the form that was verified by the unit testing to be correct.
  • I'm okay with my application loading them via HTTPS as it starts up instead of loading them from disk. I'm worried about this because I don't want a web server to be a point of failure for my app starting up.

I would appreciate some insight in how one might accomplish both goals.

答案1

得分: 3

你的初始假设是错误的。在$id关键字中使用的URI可以是任意标识符,它们不需要通过网络或磁盘在指定位置可解析。实际上,JSON Schema的实现不能假设能够在指定位置找到模式文档:它们必须支持能够在本地加载文档并将其与指定的标识符关联起来。

$id关键字用于标识模式资源的规范URI。需要注意的是,这个URI是一个标识符,不一定是一个网络定位符。对于可通过网络访问的URL,模式不一定可以从其规范URI下载。

因此,你可以给你的模式文档任何你喜欢的标识符,比如你预计在最终发布模式供公众使用时使用的URI,并使用该标识符进行本地测试。

任何不支持这样做的实现都违反了规范,应该将此作为错误报告给维护者。

英文:

> Where I get confused is that in order to load my schemas before I've published them (which I want to do while running tests locally and during CI/CD), I need to use relative local paths

Your initial assumption is false. URIs used in the $id keyword can be arbitrary identifiers -- they do not need to be resolvable via the network or disk at the stated location. In fact, it is an error for a JSON Schema implementation to assume to find schema documents at the stated location: they MUST support being able to load documents locally and associate them with the stated identifier:

> The "$id" keyword identifies a schema resource with its canonical URI.
>
> Note that this URI is an identifier and not necessarily a network locator. In the case of a network-addressable URL, a schema need not be downloadable from its canonical URI.

source

> A schema need not be downloadable from the address if it is a network-addressable URL, and implementations SHOULD NOT assume they should perform a network operation when they encounter a network-addressable URI.

source

Therefore, you can give your schema document any identifier you like, such as the URI you anticipate using when you eventually publish your schema for public consumption, and perform local testing using that identifier.

Any implementation that does not support doing this is in violation of the specification, and this should be reported to its maintainers as a bug.

huangapple
  • 本文由 发表于 2022年3月7日 06:56:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/71374864.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定