Skip to content

Commit

Permalink
support openapi oneOf Objects (github#36353)
Browse files Browse the repository at this point in the history
  • Loading branch information
rachmari authored Apr 12, 2023
1 parent 874f0ea commit cc0feb3
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 22 deletions.
13 changes: 9 additions & 4 deletions components/parameter-table/ChildBodyParametersRows.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type Props = {
childParamsGroups: ChildParameter[]
parentName: string
parentType: string
oneOfObject?: boolean
}

export function ChildBodyParametersRows({
Expand All @@ -20,17 +21,21 @@ export function ChildBodyParametersRows({
parentName,
parentType,
childParamsGroups,
oneOfObject = false,
}: Props) {
const { t } = useTranslation(['parameter_table', 'products'])

return (
<tr className={cx(styles.childBodyParametersRows, 'color-bg-subtle border-top-0')}>
<td colSpan={4} className="has-nested-table">
<details className="box px-3 ml-1 mb-0" open={open}>
<summary role="button" aria-expanded="false" className="mb-2 keyboard-focus">
<span id={`${slug}-${parentName}-${parentType}`}>
Properties of <code>{parentName}</code>
</span>
{oneOfObject ? (
<span id={`${slug}-${parentName}-${parentType}`}>Can be one of these objects:</span>
) : (
<span id={`${slug}-${parentName}-${parentType}`}>
Properties of <code>{parentName}</code>
</span>
)}
</summary>
<table id={`${parentName}-object`} className="mb-4 color-bg-subtle">
<thead className="visually-hidden">
Expand Down
13 changes: 10 additions & 3 deletions components/parameter-table/ParameterRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export function ParameterRow({
parentType={Array.isArray(rowParams.type) ? rowParams.type.join(' or ') : rowParams.type}
childParamsGroups={rowParams.childParamsGroups}
open={rowParams.name === clickedBodyParameterName}
oneOfObject={rowParams.oneOfObject}
/>
)}

Expand All @@ -164,9 +165,15 @@ export function ParameterRow({
onToggle={bodyParamExpandCallback}
>
<summary role="button" aria-expanded="false" className="mb-2 keyboard-focus">
<span id={`${slug}-${rowParams.name}`}>
Properties of <code>{rowParams.name}</code>
</span>
{rowParams.oneOfObject ? (
<span id={`${slug}-${rowParams.name}`}>
Can be one of these objects: <code>{rowParams.name}</code>
</span>
) : (
<span id={`${slug}-${rowParams.name}`}>
Properties of <code>{rowParams.name}</code>
</span>
)}
</summary>
</details>
</td>
Expand Down
1 change: 1 addition & 0 deletions components/parameter-table/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ export interface ChildParameter {
enum?: Array<string | null>
default?: string | boolean | number | undefined | string[]
childParamsGroups?: ChildParameter[]
oneOfObject?: boolean
}
48 changes: 35 additions & 13 deletions src/rest/scripts/utils/get-body-params.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,31 @@ export async function getBodyParams(schema, topLevel = false) {
keyParam.childParamsGroups.push(...(await getBodyParams(param.additionalProperties, false)))
childParamsGroups.push(keyParam)
} else if (paramType && paramType.includes('array')) {
const arrayType = param.items.type
if (arrayType) {
paramType.splice(paramType.indexOf('array'), 1, `array of ${arrayType}s`)
}
if (arrayType === 'object') {
childParamsGroups.push(...(await getBodyParams(param.items, false)))
if (param.items && param.items.oneOf) {
if (param.items.oneOf.every((object) => object.type === 'object')) {
paramType.splice(paramType.indexOf('array'), 1, `array of objects`)
param.oneOfObject = true

for (const oneOfParam of param.items.oneOf) {
const objParam = {
type: 'object',
name: oneOfParam.title,
description: await renderContent(oneOfParam.description),
isRequired: oneOfParam.required,
childParamsGroups: [],
}
objParam.childParamsGroups.push(...(await getBodyParams(oneOfParam, false)))
childParamsGroups.push(objParam)
}
}
} else {
const arrayType = param.items.type
if (arrayType) {
paramType.splice(paramType.indexOf('array'), 1, `array of ${arrayType}s`)
}
if (arrayType === 'object') {
childParamsGroups.push(...(await getBodyParams(param.items, false)))
}
}
} else if (paramType && paramType.includes('object')) {
childParamsGroups.push(...(await getBodyParams(param, false)))
Expand Down Expand Up @@ -138,13 +157,12 @@ export async function getBodyParams(schema, topLevel = false) {
const oneOfDescriptions = descriptions.length ? descriptions[0].description : ''
if (!param.description) param.description = oneOfDescriptions

// This is a workaround for an operation that incorrectly defines allOf for a
// body parameter. As a workaround, we will use the first object in the list of
// the allOf array. Otherwise, fallback to the first item in the array.
// This isn't ideal, and in the case of an actual allOf occurrence, we should
// handle it differently by merging all of the properties. There is currently
// only one occurrence for the operation id repos/update-information-about-pages-site
// See Ecosystem API issue number #3332 for future plans to fix this in the OpenAPI
// This is a workaround for an operation that incorrectly defines anyOf
// for a body parameter. As a workaround, we will use the first object
// in the list of the anyOf array. Otherwise, fallback to the first item
// in the array. There is currently only one occurrence for the operation
// id repos/update-information-about-pages-site. See Ecosystem API issue
// number #3332 for future plans to fix this in the OpenAPI
} else if (param && param.anyOf && Object.keys(param).length === 1) {
const firstObject = Object.values(param.anyOf).find((item) => item.type === 'object')
if (firstObject) {
Expand Down Expand Up @@ -212,6 +230,10 @@ async function getTransformedParam(param, paramType, props) {
paramDecorated.enum = param.enum
}

if (param.oneOfObject) {
paramDecorated.oneOfObject = true
}

// we also want to catch default values of `false` for booleans
if (param.default !== undefined) {
paramDecorated.default = param.default
Expand Down
5 changes: 4 additions & 1 deletion src/rest/scripts/utils/sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ async function formatRestData(operations) {
async function updateRestConfigData(schemas) {
const restConfigFilename = 'src/rest/lib/config.json'
const restConfigData = JSON.parse(await readFile(restConfigFilename, 'utf8'))
const restApiVersionData = restConfigData['api-versions']
const restApiVersionData = restConfigData['api-versions'] || {}
// If the version isn't one of the OpenAPI version,
// then it's an api-versioned schema
for (const schema of schemas) {
Expand All @@ -118,6 +118,9 @@ async function updateRestConfigData(schemas) {
const openApiVer = OPENAPI_VERSION_NAMES.find((ver) => schemaBaseName.startsWith(ver))
const date = schemaBaseName.split(`${openApiVer}-`)[1]

if (!restApiVersionData[openApiVer]) {
restApiVersionData[openApiVer] = []
}
if (!restApiVersionData[openApiVer].includes(date)) {
const dates = restApiVersionData[openApiVer]
dates.push(date)
Expand Down
2 changes: 1 addition & 1 deletion tests/linting/lint-files.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ const automatedConfigFiles = walk(`src`, { includeBasePath: true, globs: ['**/li
const automatedIgnorePaths = (
await Promise.all(
automatedConfigFiles.map(async (p) => {
return JSON.parse(await fs.readFile(p, 'utf8')).linterIgnore
return JSON.parse(await fs.readFile(p, 'utf8')).linterIgnore || []
})
)
)
Expand Down

0 comments on commit cc0feb3

Please sign in to comment.