-
-
Notifications
You must be signed in to change notification settings - Fork 395
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
Conversation
@@ -15947,13 +15947,13 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa | |||
| { status?: "queued" | "in_progress"; [key: string]: any } | |||
| ({ status?: "completed"; [key: string]: any } & { status?: "queued" | "in_progress"; [key: string]: any }) | |||
) & { | |||
name: string; | |||
name?: 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.
This is correct
head_sha: string; | ||
details_url?: string; | ||
external_id?: string; | ||
status?: "queued" | "in_progress" | "completed"; | ||
started_at?: string; | ||
conclusion?: "success" | "failure" | "neutral" | "cancelled" | "skipped" | "timed_out" | "action_required"; | ||
conclusion: "success" | "failure" | "neutral" | "cancelled" | "skipped" | "timed_out" | "action_required"; |
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.
This unfortunately is incorrect. This is the abbridged/relevant schema:
"schema": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name of the check. For example, \"code-coverage\"."
},
"status": {
"type": "string",
"description": "The current status. Can be one of `queued`, `in_progress`, or `completed`.",
"enum": ["queued", "in_progress", "completed"]
},
"conclusion": {
"type": "string",
"description": "**Required if you provide `completed_at` or a `status` of `completed`**. The final conclusion of the check. Can be one of `success`, `failure`, `neutral`, `cancelled`, `skipped`, `timed_out`, or `action_required`. \n**Note:** Providing `conclusion` will automatically set the `status` parameter to `completed`. Only GitHub can change a check run conclusion to `stale`.",
"enum": [
"success",
"failure",
"neutral",
"cancelled",
"skipped",
"timed_out",
"action_required"
]
}
},
"anyOf": [
{
"properties": {
"status": {
"enum": ["completed"]
}
},
"required": ["conclusion"],
"additionalProperties": true
},
{
"properties": {
"status": {
"enum": ["queued", "in_progress"]
}
},
"additionalProperties": true
}
]
},
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.
As in, conclusion
is required, but only if status
is completed
. But the generated type indicates that it is required in all cases, which is incorrect.
src/schema.js
Outdated
@@ -181,13 +181,19 @@ const filterContents = (contents, types) => _.filter(contents, (type) => !_.incl | |||
const complexSchemaParsers = { | |||
[SCHEMA_TYPES.COMPLEX_ONE_OF]: (schema) => { | |||
// T1 | T2 | |||
const combined = _.map(schema.oneOf, complexTypeGetter); | |||
const combined = _.map( | |||
schema.oneOf.map((s) => _.merge({ required: schema.required }, s)), |
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.
This logic isn't quite right, because it assumes that schema.required
makes sense in the parent schema. But like the example in "full-swagger-scheme` shows, this isn't always the case.
Fixing this properly would require the logic in complexSchemaParsers
to be lifted up a bit (i.e. if there is an anyOf
/oneOf
/allOf
, then the entire type of the parent schema needs to be mapped, not just the fields in the anyOf
.
But maybe a good first step is just to filter schema.required
down to those fields that are directly defined in the nested schema
. That way it would at least tighten the type for simple cases, without introducing this bug.
c00448e
to
5d8c187
Compare
The last commit fixes the bug I mentioned with |
@@ -178,16 +178,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 comment
The 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 swagger-typescript-api
provides to Node you could use null coalescing (i.e. ??
)
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.
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.
@@ -178,16 +178,57 @@ const getObjectTypeContent = (schema) => { | |||
const complexTypeGetter = (schema) => getInlineParseContent(schema); | |||
const filterContents = (contents, types) => _.filter(contents, (type) => !_.includes(types, type)); | |||
|
|||
const makeAddRequiredToChildSchema = (parentSchema) => (childSchema) => { |
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:
({ required = [], properties = [], ...other }) =>
Instead of
(childSchema) =>
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.
// 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 comment
The reason will be displayed to describe this comment to others. Learn more.
I believe childSchema.properties
should be just properties
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.
It could be, but I think childSchema.properties
makes more sense here, since it's referring to the properties that are in the child schema, not the properties
variable that will be modifiied and returned (which includes properties that are not in the child schema).
(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 comment
The 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 Set
instead of !required.includes(key)
. I think it'd be slightly clearer in terms of what you're trying to achieve (I assume avoiding duplicates)
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.
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Yep fair enough. Leave it up to the author :)
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 comment
The reason will be displayed to describe this comment to others. Learn more.
_.keys(properties).includes(key) || _.keys(parentSchema.properties).includes(key)
Is it necessary to filter out keys that do not exist in the properties
? I wouldn't have thought it would cause any issues if they were added
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.
I'm not sure, but given that logically required
is a set, the most correct thing seems to be to avoid duplicates.
// 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 comment
The reason will be displayed to describe this comment to others. Learn more.
Why only fields required by the child?
To my knowledge allOf
should apply ALL fields, regardless of whether they're optional/required. Are optional fields being handled elsewhere in this code base and if so shouldn't this code live in the same spot as that logic?
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.
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
the given data must be valid against all of the given subschemas
Maybe it's just out of scope for this PR?
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.
As per: https://json-schema.org/understanding-json-schema/reference/combining.html#allof
the given data must be valid against all of the given subschemas
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.
Why only fields required by the child?
the childSchema's properties and the parent schema's properties should be merged together
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 n
parent schemas - but it requires the parent schema to have a name, which currently it doesn't.
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.
Are optional fields being handled elsewhere in this code base and if so shouldn't this code live in the same spot as that logic?
I'm not sure what you mean - open to suggestions if you have them.
>; | ||
} | ||
| ({ description: string } & { | ||
files: Record< |
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.
@PatrickShaw notice that files
has exactly the same shape as the one on (new) line 10665. The entire definition is duplicated here - not because the shape is different, just so that it can be made a compulsory property. The type from the parent schema starts on line 10663 - and it has no name, so we can't refer to it here.
Hello @hedgepigdaniel ! |
* Feature: allow passing "patch" option to "swagger2openapi" (#283) Co-authored-by: cassel <[email protected]> * Loads prettier config from user's project (#286) * fix: problem with missing HttpResponse type with --to-js option * fix: missing extractRequestBody field in type * fix(reject): fix reject and exit with code 1 if error (#295) * fix(reject): fix reject and exit with code 1 if error * feat: add a parameter to sort types and types properties (#299) Co-authored-by: rcatoio <[email protected]> * feat: add an option to disable throwing an error when request.ok is not true (#297) Co-authored-by: rcatoio <[email protected]> Co-authored-by: Sergey S. Volkov <[email protected]> * fix: fix missing `title` of object type (#303) * chore: refresh auto generated code; chore: add axios to dev deps * bump: up to 9.3.0 * bump: up to 9.3.1; fix: axios * chore: fix typo (#376) * 304 Support readOnly properties (#365) * allow mutually exclusive input options (#327) Co-authored-by: Sergey S. Volkov <[email protected]> * Update fetch-http-client.eta (#329) * BREAKING_CHANGE: add --extract-response-body option; bump: version to 9.4.0 * feat: added authorization token to headers Co-authored-by: MarcinFilipek <[email protected]> * feat: --extract-response-error option * chore: refresh code gen tests * fix: change COMPLEX_NOT_OF to COMPLEX_NOT (#356) * Fix request format and allow for files in request - With code review changes (#350) * fix pick format allow for file array in form data requests * fixed code review suggestions Co-authored-by: Daniele De Matteo <[email protected]> Co-authored-by: Sergey S. Volkov <[email protected]> * Pass through required properties to allOf/anyOf/oneOf children (#342) * Pass through required properties to allOf children * extend fix to anyOf and oneOf * fix: account for children making parent fields required Co-authored-by: Anders Cassidy <[email protected]> Co-authored-by: Daniel Playfair Cal <[email protected]> Co-authored-by: Sergey S. Volkov <[email protected]> * Add option to change main Api class name (#320) * Update api.eta * Update index.d.ts * Update index.js * Update index.js * Update config.js Co-authored-by: Sergey S. Volkov <[email protected]> * fix: missing stringifyFormItem method; bump: up version to 10.0.0 * docs: update docs, changelog, fix: problem with complex types (makeAddRequiredToChildSchema) Co-authored-by: Xavier Cassel <[email protected]> Co-authored-by: cassel <[email protected]> Co-authored-by: Adrian Wieprzkowicz <[email protected]> Co-authored-by: EvgenBabenko <[email protected]> Co-authored-by: RoCat <[email protected]> Co-authored-by: rcatoio <[email protected]> Co-authored-by: 卡色 <[email protected]> Co-authored-by: 江麻妞 <[email protected]> Co-authored-by: Kasper Moskwiak <[email protected]> Co-authored-by: Ben Watkins <[email protected]> Co-authored-by: bonukai <[email protected]> Co-authored-by: baggoedw <[email protected]> Co-authored-by: Marcus Dunn <[email protected]> Co-authored-by: Daniele De Matteo <[email protected]> Co-authored-by: Daniel Playfair Cal <[email protected]> Co-authored-by: Anders Cassidy <[email protected]> Co-authored-by: Daniel Playfair Cal <[email protected]>
@all-contributors please add @hedgepigdaniel for code bugs and add @PatrickShaw for review |
I could not determine your intention. Basic usage: @all-contributors please add @Someone for code, doc and infra For other usages see the documentation |
@all-contributors please add @hedgepigdaniel for code bugs |
I've put up a pull request to add @hedgepigdaniel! 🎉 |
@all-contributors please add @PatrickShaw for review |
I've put up a pull request to add @PatrickShaw! 🎉 |
Including #336, but:
anyOf
/oneOf
.