sagold / json-schema-library

Customizable and hackable json-validator and json-schema utilities for traversal, data generation and validation
MIT License
164 stars 19 forks source link

How to properly obtain a template of a def? #25

Closed david-pa closed 1 year ago

david-pa commented 1 year ago

I have a schema I'm working with that is something like the following:

const mySchema = {
  properties: {
    inputs: {
      items: {
        $ref: '#/$defs/Input'
      },
      type: 'array',
    }
  },
  additionalProperties: false,
  type: 'object',
  required: [
    'inputs'
  ],
  $defs: {
    Input: {
      properties: {
        name: {
          type: 'string'
        },
        value: {
          oneOf: [
            {
              type: 'string'
            },
            {
              type: 'number'
            },
        }
      },
      additionalProperties: false,
      type: 'object',
      required: ['name']
    }
  }
};

When I use:

const newSchema = new Draft04(mySchema);
const template = newSchema.getTemplate();

I get back a JSON object that looks something like:

{
    inputs: []
}

What I need to do now is grab a template of the Input definition referenced in the schema's $defs section, but I'm not quite sure how to do that. I thought that something like

const compiledSchema = newSchema.compileSchema({ $ref: "/$defs/Input" });
const inputSchema = compiledSchema.getRef();

would work, but that didn't yield any results. All that happened was that compiledSchema and inputSchema were the exact same sets of data, no schema to be found. What I'm expecting to get back is something like:

{
    name: ''
}

But I'd also need to know how to get something like

{
    name: '',
    value: ''
}

as value in this definition isn't required, but I may have access to that data depending on the context and will want to fill it in.

sagold commented 1 year ago

Hi David.

$defs has been introduced with draft-07. Before that, you had to use property definitions for this.

Either use the contructor Draft07 or change $defs to definitions.

Does this solve your issue?

david-pa commented 1 year ago

Does the $defs need to be changed to definitions within the schema, the .compileSchema() call, or both? Is Draft07 compatible with a Draft04 schema, or would there be some data misinterpretation?

david-pa commented 1 year ago

Using

const newSchema = new Draft07(mySchema);
const compiledSchema = newSchema.compileSchema({ $ref: "/$defs/Input" });
const inputSchema = compiledSchema.getRef();

It seems like using Draft07 as the constructor in the above code ended up returning inputSchema as:

{
    $defs: { Input: {...}},
    $ref: '$defs/Input'
}

This still isn't quite what I was expecting per the documentation, though.

sagold commented 1 year ago

Hi David.

json-schema specifications evolve over time and contain breaking changes in between. To learn about json-schema, please refer to the json-schema website. They also have a very helpful tutorial page and a reference to all the schema specifications.

I suggest you stick to the latest schema version, for json-schema-library this is Draft07:

you get an empty array, because getTemplate only creates a minimal data that validates. This also protects against the creation of inifinite long lists, etc:

import { Draft07 } from "json-schema-library";
const draft = new Draft07(mySchema);
draft.getTemplate(); 
// {"inputs":[]}

you can define the number of items to be created:

mySchema.properties.inputs.minLength = 1;
draft.setSchema(mySchema);
draft.getTemplate(); 
// {"inputs":[{"name":"","value":""}]}

or you can pass a number of items:

draft.setSchema(mySchema);
draft.getTemplate({ inputs: [{}, {}] }); 
// {"inputs":[{"name":"","value":""},{"name":"","value":""}]}

you can also pass a subschema to getTemplate directly:

draft.getTemplate(null, mySchema.$defs.Input); 
// { name:"first", value:"" }

and you can use getTemplate to merge input data

draft.getTemplate({ "inputs": [{ name: "first" }]}); 
// { "inputs": [{ name:"first", value:"" }]}

you can also get the schema via a json-pointer:

const InputSchema = draft.getSchema().getRef("#/$defs/Input");

This should help you to get started.

Notes

david-pa commented 1 year ago

This clears up the issues I was having, thank you!

sagold commented 1 year ago

Thank you for the confirmation.

Cheers

david-pa commented 1 year ago

@sagold I do have one last thing that has popped up! I am having an issue where I have something defined in $defs such that I can get back the schema via

exampleSchema = mySchema.getSchema().getRef('#/$defs/Example');

but if I try to put that into a call like so:

mySchema.getTemplate(null, exampleSchema);

all I get back is undefined. I'm not sure what the issue is, but I can't even get a template using mySchema.getTemplate(null, baseSchema.$defs.Example);, which has worked so far for other definitions.

Is there a common reason why something like this may happen?

EDIT: The issue was a missing type declaration of object in the schema file! Adding that fixed it.

sagold commented 1 year ago

Hi David.

Can you post your schema? Maybe there is an issue with the schema or the support of it in getTemplate. I cannot tell without the actual schema.