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

getSchema() should resolve nested if-else-then when retrieving a parent schema #61

Closed imolorhe closed 2 days ago

imolorhe commented 2 weeks ago

So far getSchema() works great with resolving dynamic schemas when retrieving the sub schema for a field (pointer) with dynamic schema properties.

I'm trying to use it to retrieve a parent pointer (/) that contains a field with dynamic schema properties. However it doesn't seem to work.

Here's a test case: https://github.com/imolorhe/json-schema-library/commit/cddc7a80f8170c2fe80c22fdc095bc581d0879b5#diff-303b209f971e42074a01747f6494e04707a20d6696bedc89be9247b732fb8d6c

Maybe the issue is that getSchema doesn't work with / root pointer at all?

sagold commented 1 week ago

Hi Samuel,

a json-pointer / is not a root-pointer. This pointer has to select a property with an empty string "":

A root-pointer is usually specified by an empty string "" or using the uri-fragment #. Personally, I prefer the latter as the intention is communicated more clearly.

Did this solve your issue?

imolorhe commented 1 week ago

Oh I wasn't aware of that. Thanks for the clarification! I'll take a look if this solves my issue but considering there are tests for the implementation, my guess is that it does.

imolorhe commented 1 week ago

Okay I tried it again but it still doesn't resolve the nested if-else-then.

Updated test case: https://github.com/imolorhe/json-schema-library/commit/ea7915d164dab391c3b9862f31c8b759e6a96bbc

What am I expecting?

I expect that since the data is provided, the schema should resolve/expand the nested allOf if..else..then, etc. to the schema that satisfy the criteria, such that I don't have to account for that myself

sagold commented 1 week ago

I see.

Unfortunately, getSchema does not behave this way. json-schema specification explicitly mentions that conditional schemas should not be merged as this will change the validation in some cases. Thus, returning a reduced schema would be an incomplete result.

Still, for building applications with json-schema we often need to retrieve an actual schema for specific data sets. For this use case a method reduceSchema has been introduced and is exposed by this library. Using this helper for your test should return the expected result:

import { reduceSchema, Draft07 } from "json-schema-library";
const schema = {
  type: "object",
  properties: { test: { type: "string" } },
  if: { properties: { test: { type: "string", minLength: 1 } } },
  then: { properties: { additionalValue: { description: "added", type: "string" } } }
}

const reducedSchema = reduceSchema(new Draft07(), schema, { test: "validates if" }, "");

// the following is true
expect(reducedSchema).to.deep.equal({
  type: 'object',
  properties: {
    test: { type: 'string' },
    additionalValue: { description: 'added', type: 'string' }
  }
});

But there is a catch: reduceSchema only reduces the current root leaving the children untouched for later resolution. This is true for all json-schema-library methods as this enables incremental processing of a schema and data.

So, using reduceSchema should help you, when being applied on each result. A method to reduce the complete schema for a given input-data does not exist yet.

Would this be sufficient for your use case?

imolorhe commented 6 days ago

It sounds like reduceSchema is similar to getSchema? 🤔 i.e. I already use getSchema for this but facing the issue of untouched children. However I think I can figure out a way around for my use case based on this understanding

imolorhe commented 2 days ago

Closing this for now. I was able to get the workaround to work for my use case (essentially I only care about resolving the direct nested children, so I just iterate over the direct properties children and combine the output).

Thanks again for the help!