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.