-
-
Notifications
You must be signed in to change notification settings - Fork 418
Pass through required properties to allOf/anyOf/oneOf children #342
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
Changes from all commits
9628643
3ff93cf
5d8c187
8957081
9e47ef0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -186,16 +186,57 @@ const getObjectTypeContent = (schema) => { | |
| const complexTypeGetter = (schema) => getInlineParseContent(schema); | ||
| const filterContents = (contents, types) => _.filter(contents, (type) => !_.includes(types, type)); | ||
|
|
||
| const makeAddRequiredToChildSchema = (parentSchema) => (childSchema) => { | ||
| let required = childSchema.required || []; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LTS for Node 12 is ending in April 2022 so depending on the level of support
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I tried to avoid that because personally (unfortunately) I still use this with Node.js 12. IMO the best way to address this is to add a build step, but that's out of scope for this PR. |
||
| let properties = childSchema.properties || {}; | ||
|
|
||
| // Inherit all the required fields from the parent schema that are defined | ||
| // either on the parent schema or on the child schema | ||
| // TODO: any that are defined at grandparents or higher are ignored | ||
| required = required.concat( | ||
| (parentSchema.required || []).filter( | ||
| (key) => | ||
| !required.includes(key) && (_.keys(properties).includes(key) || _.keys(parentSchema.properties).includes(key)), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Is it necessary to filter out keys that do not exist in the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure, but given that logically |
||
| ), | ||
| ); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Goes back to my point about Node support but: You could use a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, honestly I was just trying to stick to the convention, the surrounding code uses lodash data structures so I used them too 🤷 - happy to change if the maintainers prefer. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep fair enough. Leave it up to the author :) |
||
|
|
||
| // Identify properties that are required in the child schema, but | ||
| // defined only in the parent schema (TODO: this only works one level deep) | ||
| const parentPropertiesRequiredByChild = required.filter( | ||
| (key) => !_.keys(childSchema.properties).includes(key) && _.keys(parentSchema.properties).includes(key), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It could be, but I think |
||
| ); | ||
|
|
||
| // Add such properties to the child so that they can be overriden and made required | ||
| properties = { | ||
| ...properties, | ||
| ...parentPropertiesRequiredByChild.reduce( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why only fields required by the child? To my knowledge There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe more importantly: Rather than overriding the original properties I believe the childSchema's properties and the parent schema's properties should be merged together so that both the child and parent's type conditions are validated against. As per: https://json-schema.org/understanding-json-schema/reference/combining.html#allof
Maybe it's just out of scope for this PR?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think that's referring to all of the subschemas, not the combination of parent/children. Although yes, my understanding is that it must be valid against parent and child.
These fields aren't being added to check that their shape is correct. The generated types for oneOf/anyOf already inherit from the parent schema type at the typescript level - so effectively they are already merged. The reason to add them here (duplicating the definitions from the parent) is so that they can be marked as required in typescript. Hence, this only includes fields that are defined in the parent, but made required in the child. Another option might be to do it in typescript, e.g. type ChildSchema = ParentSchema & {
child_field: string;
parent_field_made_optional_in_the_child: Exclude<undefined, ParentSchema['parent_field_made_optional_in_the_child']>
};This would handle fields from grandparent schemas, and
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'm not sure what you mean - open to suggestions if you have them. |
||
| (additionalProperties, key) => ({ | ||
| ...additionalProperties, | ||
| [key]: (parentSchema.properties || {})[key], | ||
| }), | ||
| {}, | ||
| ), | ||
| }; | ||
|
|
||
| return _.merge( | ||
| { | ||
| required: required, | ||
| properties: properties, | ||
| }, | ||
| childSchema, | ||
| ); | ||
| }; | ||
|
|
||
| const complexSchemaParsers = { | ||
| [SCHEMA_TYPES.COMPLEX_ONE_OF]: (schema) => { | ||
| // T1 | T2 | ||
| const combined = _.map(schema.oneOf, complexTypeGetter); | ||
| const combined = _.map(schema.oneOf.map(makeAddRequiredToChildSchema(schema)), complexTypeGetter); | ||
|
|
||
| return checkAndAddNull(schema, filterContents(combined, [TS_KEYWORDS.ANY]).join(" | ")); | ||
| }, | ||
| [SCHEMA_TYPES.COMPLEX_ALL_OF]: (schema) => { | ||
| // T1 & T2 | ||
| const combined = _.map(schema.allOf, complexTypeGetter); | ||
| const combined = _.map(schema.allOf.map(makeAddRequiredToChildSchema(schema)), complexTypeGetter); | ||
| return checkAndAddNull( | ||
| schema, | ||
| filterContents(combined, [...JS_EMPTY_TYPES, ...JS_PRIMITIVE_TYPES, TS_KEYWORDS.ANY]).join( | ||
|
|
@@ -205,7 +246,7 @@ const complexSchemaParsers = { | |
| }, | ||
| [SCHEMA_TYPES.COMPLEX_ANY_OF]: (schema) => { | ||
| // T1 | T2 | (T1 & T2) | ||
| const combined = _.map(schema.anyOf, complexTypeGetter); | ||
| const combined = _.map(makeAddRequiredToChildSchema(schema), complexTypeGetter); | ||
| const nonEmptyTypesCombined = filterContents(combined, [ | ||
| ...JS_EMPTY_TYPES, | ||
| ...JS_PRIMITIVE_TYPES, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10610,9 +10610,41 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa | |
| */ | ||
| gistsUpdate: ( | ||
| gistId: string, | ||
| data: (any | any | null) & { | ||
| data: ( | ||
| | { description: string } | ||
| | { | ||
| files: Record< | ||
| string, | ||
| ( | ||
| | { content: string } | ||
| | { filename: string | null } | ||
| | object | ||
| | ({ content: string } & { filename: string | null } & object) | ||
| ) & { content?: string; filename?: string | null } | ||
| >; | ||
| } | ||
| | ({ description: string } & { | ||
| files: Record< | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @PatrickShaw notice that |
||
| string, | ||
| ( | ||
| | { content: string } | ||
| | { filename: string | null } | ||
| | object | ||
| | ({ content: string } & { filename: string | null } & object) | ||
| ) & { content?: string; filename?: string | null } | ||
| >; | ||
| }) | ||
| ) & { | ||
| description?: string; | ||
| files?: Record<string, (any | any | object | null) & { content?: string; filename?: string | null }>; | ||
| files?: Record< | ||
| string, | ||
| ( | ||
| | { content: string } | ||
| | { filename: string | null } | ||
| | object | ||
| | ({ content: string } & { filename: string | null } & object) | ||
| ) & { content?: string; filename?: string | null } | ||
| >; | ||
| }, | ||
| params: RequestParams = {}, | ||
| ) => | ||
|
|
@@ -15917,9 +15949,21 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa | |
| owner: string, | ||
| repo: string, | ||
| data: ( | ||
| | { status?: "completed"; [key: string]: any } | ||
| | { status?: "queued" | "in_progress"; [key: string]: any } | ||
| | ({ status?: "completed"; [key: string]: any } & { status?: "queued" | "in_progress"; [key: string]: any }) | ||
| | { | ||
| status?: "completed"; | ||
| conclusion: "success" | "failure" | "neutral" | "cancelled" | "skipped" | "timed_out" | "action_required"; | ||
| name: string; | ||
| head_sha: string; | ||
| [key: string]: any; | ||
| } | ||
| | { status?: "queued" | "in_progress"; name: string; head_sha: string; [key: string]: any } | ||
| | ({ | ||
| status?: "completed"; | ||
| conclusion: "success" | "failure" | "neutral" | "cancelled" | "skipped" | "timed_out" | "action_required"; | ||
| name: string; | ||
| head_sha: string; | ||
| [key: string]: any; | ||
| } & { status?: "queued" | "in_progress"; name: string; head_sha: string; [key: string]: any }) | ||
| ) & { | ||
| name: string; | ||
| head_sha: string; | ||
|
|
@@ -15988,9 +16032,17 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa | |
| repo: string, | ||
| checkRunId: number, | ||
| data: ( | ||
| | { status?: "completed"; [key: string]: any } | ||
| | { | ||
| status?: "completed"; | ||
| conclusion: "success" | "failure" | "neutral" | "cancelled" | "skipped" | "timed_out" | "action_required"; | ||
| [key: string]: any; | ||
| } | ||
| | { status?: "queued" | "in_progress"; [key: string]: any } | ||
| | ({ status?: "completed"; [key: string]: any } & { status?: "queued" | "in_progress"; [key: string]: any }) | ||
| | ({ | ||
| status?: "completed"; | ||
| conclusion: "success" | "failure" | "neutral" | "cancelled" | "skipped" | "timed_out" | "action_required"; | ||
| [key: string]: any; | ||
| } & { status?: "queued" | "in_progress"; [key: string]: any }) | ||
| ) & { | ||
| name?: string; | ||
| details_url?: string; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Imo it would be simpler to read if you did:
Instead of
As it keeps default logic/values all in one place
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be. I personally prefer creating a new variable instead of reassigning function parameters (destructured or not). But happy to change depending on what the maintainers prefer.