@@ -166,8 +166,43 @@ module.exports = class Operation {
166166 }
167167
168168 async renderBodyParameterDescriptions ( ) {
169- const bodyParamsObject = get ( this , 'requestBody.content.application/json.schema.properties' , { } )
170- const requiredParams = get ( this , 'requestBody.content.application/json.schema.required' , [ ] )
169+ let bodyParamsObject = get ( this , 'requestBody.content.application/json.schema.properties' , { } )
170+ let requiredParams = get ( this , 'requestBody.content.application/json.schema.required' , [ ] )
171+ const oneOfObject = get ( this , 'requestBody.content.application/json.schema.oneOf' , undefined )
172+
173+ // oneOf is an array of input parameter options, so we need to either
174+ // use the first option or munge the options together.
175+ if ( oneOfObject ) {
176+ const firstOneOfObject = oneOfObject [ 0 ]
177+ const allOneOfAreObjects = oneOfObject
178+ . filter ( elem => elem . type === 'object' )
179+ . length === oneOfObject . length
180+
181+ // TODO: Remove this check
182+ // This operation shouldn't have a oneOf in this case, it needs to be
183+ // removed from the schema in the github/github repo.
184+ if ( this . operationId === 'checks/create' ) {
185+ delete bodyParamsObject . oneOf
186+ } else if ( allOneOfAreObjects ) {
187+ // When all of the oneOf objects have the `type: object` we
188+ // need to display all of the parameters.
189+ // This merges all of the properties and required values into the
190+ // first requestBody object.
191+ for ( let i = 1 ; i < oneOfObject . length ; i ++ ) {
192+ Object . assign ( firstOneOfObject . properties , oneOfObject [ i ] . properties )
193+ requiredParams = firstOneOfObject . required
194+ . concat ( oneOfObject [ i ] . required )
195+ }
196+ bodyParamsObject = firstOneOfObject . properties
197+ } else if ( oneOfObject ) {
198+ // When a oneOf exists but the `type` differs, the case has historically
199+ // been that the alternate option is an array, where the first option
200+ // is the array as a property of the object. We need to ensure that the
201+ // first option listed is the most comprehensive and preferred option.
202+ bodyParamsObject = firstOneOfObject . properties
203+ requiredParams = firstOneOfObject . required
204+ }
205+ }
171206
172207 this . bodyParameters = await getBodyParams ( bodyParamsObject , requiredParams )
173208 }
@@ -211,16 +246,57 @@ async function getBodyParams (paramsObject, requiredParams) {
211246 param . rawType = param . type
212247 param . rawDescription = param . description
213248
214- // e.g. array of strings
215- param . type = param . type === 'array'
216- ? `array of ${ param . items . type } s`
217- : param . type
249+ // Stores the types listed under the `Type` column in the `Parameters`
250+ // table in the REST API docs. When the parameter contains oneOf
251+ // there are multiple acceptable parameters that we should list.
252+ const paramArray = [ ]
253+
254+ const oneOfArray = param . oneOf
255+ const isOneOfObjectOrArray = oneOfArray
256+ ? oneOfArray . filter ( elem => elem . type !== 'object' || elem . type !== 'array' )
257+ : false
258+
259+ // When oneOf has the type array or object, the type is defined
260+ // in a child object
261+ if ( oneOfArray && isOneOfObjectOrArray . length > 0 ) {
262+ // Store the defined types
263+ paramArray . push ( oneOfArray
264+ . filter ( elem => elem . type )
265+ . map ( elem => elem . type )
266+ )
267+
268+ // If an object doesn't have a description, it is invalid
269+ const oneOfArrayWithDescription = oneOfArray . filter ( elem => elem . description )
270+
271+ // Use the parent description when set, otherwise enumerate each
272+ // description in the `Description` column of the `Parameters` table.
273+ if ( ! param . description && oneOfArrayWithDescription . length > 1 ) {
274+ param . description = oneOfArray
275+ . filter ( elem => elem . description )
276+ . map ( elem => `**Type ${ elem . type } ** - ${ elem . description } ` )
277+ . join ( '\n\n' )
278+ } else if ( ! param . description && oneOfArrayWithDescription . length === 1 ) {
279+ // When there is only on valid description, use that one.
280+ param . description = oneOfArrayWithDescription [ 0 ] . description
281+ }
282+ }
283+
284+ // Arrays require modifying the displayed type (e.g., array of strings)
285+ if ( param . type === 'array' ) {
286+ if ( param . items . type ) paramArray . push ( `array of ${ param . items . type } s` )
287+ if ( param . items . oneOf ) {
288+ paramArray . push ( param . items . oneOf
289+ . map ( elem => `array of ${ elem . type } s` )
290+ )
291+ }
292+ } else if ( param . type ) {
293+ paramArray . push ( param . type )
294+ }
218295
219- // e.g. object or null
220- param . type = param . nullable
221- ? `${ param . type } or null`
222- : param . type
296+ if ( param . nullable ) paramArray . push ( 'nullable' )
223297
298+ param . type = paramArray . flat ( ) . join ( ' or ' )
299+ param . description = param . description || ''
224300 const isRequired = requiredParams && requiredParams . includes ( param . name )
225301 const requiredString = isRequired ? '**Required**. ' : ''
226302 param . description = await renderContent ( requiredString + param . description )
@@ -245,8 +321,9 @@ async function getBodyParams (paramsObject, requiredParams) {
245321}
246322
247323async function getChildParamsGroup ( param ) {
248- // only objects and arrays of objects ever have child params
249- if ( ! ( param . rawType === 'array' || param . rawType === 'object' ) ) return
324+ // only objects, arrays of objects, anyOf, allOf, and oneOf have child params
325+ if ( ! ( param . rawType === 'array' || param . rawType === 'object' || param . oneOf ) ) return
326+ if ( param . oneOf && ! param . oneOf . filter ( param => param . type === 'object' || param . type === 'array' ) ) return
250327 if ( param . items && param . items . type !== 'object' ) return
251328
252329 const childParamsObject = param . rawType === 'array' ? param . items . properties : param . properties
0 commit comments