Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Align JSON Schema with TSON.is() validation checks #290

Closed
wants to merge 3 commits into from
Closed

Align JSON Schema with TSON.is() validation checks #290

wants to merge 3 commits into from

Conversation

sinclairzx81
Copy link
Contributor

@sinclairzx81 sinclairzx81 commented Oct 31, 2022

This PR updates 4 schemas for Ajv (1) and TypeBox (3) to attain better alignment for the internal validation rules implemented for TSON.is(). This PR also updates TypeBox from 0.24.52 to 0.25.2 which has since included additional support for additionalProperties: TSchema (which enables extended support for { __cache: undefined } additional properties).

const T = Type.Object({ ... }, { additionalProperties: Type.Undefined() }) // supported

Note: additionalProperties: { ...schema } is recognized by the JSON Schema specification, however additionalProperties: { type: 'undefined' } is not. The TypeCompiler provides support through extension types, but would be discouraged in any typical implementation. This may have implication for TSON.application<T, 'ajv'> as there would be no way to produce an equivalent schema that would be understood by Ajv. For consideration.

This PR is a follow on from this query #289 (comment)

Components typescript-json typebox ajv io-ts zod class-validator
object (simple) 491268.8152353048 1010331.129476584 464905.2867550907 27677.038626609443 3022.5481209899176 149.86376021798367
object (hierarchical) 99931.05340160936 110351.29271379497 35057.6385002798 6390.613586559533 350.0361794500724 47.31182795698925
object (recursive) 75845.6043956044 73286.49935885693 32307.69230769231 4270.117044623263 57.09251101321587 30.493439290334504
object (union, explicit) 16388.641425389756 10634.278254382694 6122.272317403066 2265.261627906977 26.533996683250415 83.27165062916359
object (union, implicit) 14381.92365269461 1915.6626506024097 Failed Failed 14.51352804156961 59.749620637329286
array (recursive) 5601.809954751131 5254.431314623338 1708.40108401084 388.5053181563725 6.784771956275915 2.7437351381013357
array (union, explicit) 3335.500650195059 1758.8705182983467 615.4128440366972 268.5829366532406 2.719361856417694 31.25591968175791
array (union, implicit) 1509.127789046653 796.6101694915255 439.93325917686315 Failed 1.6011385874399573 22.654332641117616
ultimate union 442.2092172640819 156.30614444843695 Failed Failed 0.46375019322924715 Failed

This should get TypeBox and Ajv as close to inline with the current test suites and spoiler data.

Submitting for Review

Copy link
Owner

@samchon samchon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Look, above image is my README content and is() function allows superfluous properties. Such superfluous properties are even allowed in the TypeScript compiler. About the addtionalProperties: false option, haven't I implemented and benchmarked through equals() function with TypeBoxObjectUnionImplicitEquals DTO? Please stop trying endless request about that addtionalProperties: false option.

interface IMember {
    id: string;
    name: string;
}
const elem = {
    id: "some-id",
    name: "some-name",
    account: "some-account", // superfluous property
};
const member: IMember = elem; // possible

@samchon
Copy link
Owner

samchon commented Oct 31, 2022

image

Anyway, when benchmarking, I've found that TypeBox fails on assertType() function when validating ObjectRecursive type.

I think it needs your check.

@samchon samchon self-assigned this Oct 31, 2022
@samchon samchon added invalid This doesn't seem right wontfix This will not be worked on labels Oct 31, 2022
@samchon
Copy link
Owner

samchon commented Oct 31, 2022

Also, about the #258 (comment), is there any reason that do not separating object type as independent component?

In my case, I'm utilizing the JSON schema type to automating frontend deveolopment like below (would be published soon).

Therefore, stop separating object type would be a cautious subject for me.

image

@sinclairzx81
Copy link
Contributor Author

Look, above image is my README content and is() function allows superfluous properties. Such superfluous properties are even allowed in the TypeScript compiler. About the addtionalProperties: false option, haven't I implemented and benchmarked through equals() function with TypeBoxObjectUnionImplicitEquals DTO? Please stop trying endless request about that addtionalProperties: false option.

@samchon Appreciated, but in order for this test to be run, you NEED additionalProperties: false (at a minimum) for the Directory and SharedDirectory schemas. Consider the following which omits { additionalProperties: false } from these schemas.

Allow ALL Superfluous Properties

const Directory = (bucket: TSchema) => Type.Object({
  id: Type.Number(),
  name: Type.String(),
  path: Type.String(),
  children: Type.Array(bucket),
});

const SharedDirectory = (bucket: TSchema) => Type.Object({
  id: Type.Number(),
  name: Type.String(),
  path: Type.String(),
  children: Type.Array(bucket),
  access: Type.Union([ // 'read' or 'write' ONLY 
    Type.Literal("read"), 
    Type.Literal("write")
  ]), 
});
    
const Union = Type.Union([Directory, SharedDirectory])

const Result = Union.Check({  // const Result = true ????? wrong...
  id:       1,
  name:     'folder',
  path:     '/test/folder',
  children: [],
  access:   'foobar'          // <---- wait, this shouldn't be allowed !!!!
})

Explanation

The reason this test fails is because Directory IS allowed additional properties (including an additional property called access that can be set to foobar). This overrides the access union on SharedDirectory causing the test to yield a true when the expectation is for this test to return false (by TSON's definition).

To further elaborate, consider the following tests run in isolation.

Directory.Check({
  id: 1,
  name: 'folder',
  path: '/test/folder',
  children: [],
  access: 'foobar'          // <---- Ok (additional property allowed)
})

SharedDirectory.Check([
  id: 1,
  name: 'folder',
  path: '/test/folder',
  children: [],
  access: 'foobar'          // <----Fail (not allowed)
])

Union.Check([
  id: 1,
  name: 'folder',
  path: '/test/folder',
  children: [],
  access: 'foobar'           // <---- Ok (because Directory is Ok) (so this test fails)
])

I think this is what your union specialization algorithm is trying to accommodate for, but JSON Schema would outright treat this as ambiguous (and require an additionalProperties: false at a minimum to constrain appropriately)

Will submit an update to remove additionalProperties: false from each schema where possible. But cannot remove for schemas that result in ambiguous validation.

@sinclairzx81
Copy link
Contributor Author

Refer to bd119aa for closest JSON schema approximation while allowing for superfluous properties on SharedDirectory

@sinclairzx81
Copy link
Contributor Author

sinclairzx81 commented Oct 31, 2022

Therefore, stop separating object type would be a cautious subject for me.

Yes, that's fair. if it's not something on the TSON roadmap, no problem.

@sinclairzx81
Copy link
Contributor Author

@samchon Have fixed up assertTypeBox functions inline with comments #268 (comment), and resolved a bug in two of these functions which were re-compiling the same schema on every validation pass.

Anyway, when benchmarking, I've found that TypeBox fails on assertType() function when validating ObjectRecursive type.....I think it needs your check.

So, I've looked at the failing tests, and honestly, I'm having a difficult time making heads or tails out of what data is being run for what schema, or what the validation constraints should be (and I really don't have much time to look any deeper I'm afraid). However, I have swapped the failing schemas for assertType to use the Equals schemas, which allows the tests to pass (I'm not sure why) but it could mean one of two things:

  • AssertType is asserting with Equals rules (not Is)
  • The AssertType test is not constrained enough to fail when using the Equals schemas (also likely)

With this said, all the TB tests are passing and the Ajv tests could be made to pass with an equivalent set Equals of schematics. But to be honest, I don't think swapping over to the Equals schemas should be necessary if AssertType is validating via the is checks, as these rules should be the same). I don't know if there is a requirement for a third set of schemas to align with TSON internal validation (validate, is and equals), but if there is, I can't really dedicate any more time trying to identify where the disparities are.

Will leave this PR here for review, If the updates are not acceptable, I'm happy to have this issue closed off (but would advice keeping the assertTypeBox fixes, especially the ones which were causing re-compilation per validate)

Cheers
S

@sinclairzx81
Copy link
Contributor Author

sinclairzx81 commented Nov 1, 2022

Going to close this PR and consider these schemas non-reconcilable.

Good luck with your project.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
invalid This doesn't seem right wontfix This will not be worked on
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants