Skip to content

Commit 530dee6

Browse files
Matthias ZronekAlan-Cha
authored andcommitted
fixed graphql type generation and link object connections for oneOf schemas with allOf children
Signed-off-by: Matthias Zronek <[email protected]>
1 parent acfc9ae commit 530dee6

File tree

2 files changed

+146
-133
lines changed

2 files changed

+146
-133
lines changed

packages/openapi-to-graphql/src/oas_3_tools.ts

Lines changed: 128 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,132 @@ export function resolveRef<T = any>(ref: string, oas: Oas3): T {
269269
return jsonptr.JsonPointer.get(oas, ref) as T
270270
}
271271

272+
/**
273+
* Recursively traverse a schema and resolve allOf by appending the data to the
274+
* parent schema
275+
*/
276+
export function resolveAllOf<TSource, TContext, TArgs>(
277+
schema: SchemaObject | ReferenceObject,
278+
references: { [reference: string]: SchemaObject },
279+
data: PreprocessingData<TSource, TContext, TArgs>,
280+
oas: Oas3
281+
): SchemaObject {
282+
// Dereference schema
283+
if ('$ref' in schema && typeof schema.$ref === 'string') {
284+
if (schema.$ref in references) {
285+
return references[schema.$ref]
286+
}
287+
288+
const reference = schema.$ref
289+
290+
schema = resolveRef(schema.$ref, oas) as SchemaObject
291+
references[reference] = schema
292+
}
293+
294+
/**
295+
* TODO: Is there a better method to copy the schema?
296+
*
297+
* Copy the schema
298+
*/
299+
const collapsedSchema: SchemaObject = JSON.parse(JSON.stringify(schema))
300+
301+
// Resolve allOf
302+
if (Array.isArray(collapsedSchema.allOf)) {
303+
collapsedSchema.allOf.forEach((memberSchema) => {
304+
const collapsedMemberSchema = resolveAllOf(
305+
memberSchema,
306+
references,
307+
data,
308+
oas
309+
)
310+
311+
// Collapse type if applicable
312+
if (collapsedMemberSchema.type) {
313+
if (!collapsedSchema.type) {
314+
collapsedSchema.type = collapsedMemberSchema.type
315+
316+
// Check for incompatible schema type
317+
} else if (collapsedSchema.type !== collapsedMemberSchema.type) {
318+
handleWarning({
319+
mitigationType: MitigationTypes.UNRESOLVABLE_SCHEMA,
320+
message:
321+
`Resolving 'allOf' field in schema '${JSON.stringify(
322+
collapsedSchema
323+
)}' ` + `results in incompatible schema type.`,
324+
data,
325+
log: preprocessingLog
326+
})
327+
}
328+
}
329+
330+
// Collapse properties if applicable
331+
if ('properties' in collapsedMemberSchema) {
332+
if (!('properties' in collapsedSchema)) {
333+
collapsedSchema.properties = {}
334+
}
335+
336+
Object.entries(collapsedMemberSchema.properties).forEach(
337+
([propertyName, property]) => {
338+
if (!(propertyName in collapsedSchema.properties)) {
339+
collapsedSchema.properties[propertyName] = property
340+
341+
// Conflicting property
342+
} else {
343+
handleWarning({
344+
mitigationType: MitigationTypes.UNRESOLVABLE_SCHEMA,
345+
message:
346+
`Resolving 'allOf' field in schema '${JSON.stringify(
347+
collapsedSchema
348+
)}' ` +
349+
`results in incompatible property field '${propertyName}'.`,
350+
data,
351+
log: preprocessingLog
352+
})
353+
}
354+
}
355+
)
356+
}
357+
358+
// Collapse oneOf if applicable
359+
if ('oneOf' in collapsedMemberSchema) {
360+
if (!('oneOf' in collapsedSchema)) {
361+
collapsedSchema.oneOf = []
362+
}
363+
364+
collapsedMemberSchema.oneOf.forEach((oneOfProperty) => {
365+
collapsedSchema.oneOf.push(oneOfProperty)
366+
})
367+
}
368+
369+
// Collapse anyOf if applicable
370+
if ('anyOf' in collapsedMemberSchema) {
371+
if (!('anyOf' in collapsedSchema)) {
372+
collapsedSchema.anyOf = []
373+
}
374+
375+
collapsedMemberSchema.anyOf.forEach((anyOfProperty) => {
376+
collapsedSchema.anyOf.push(anyOfProperty)
377+
})
378+
}
379+
380+
// Collapse required if applicable
381+
if ('required' in collapsedMemberSchema) {
382+
if (!('required' in collapsedSchema)) {
383+
collapsedSchema.required = []
384+
}
385+
386+
collapsedMemberSchema.required.forEach((requiredProperty) => {
387+
if (!collapsedSchema.required.includes(requiredProperty)) {
388+
collapsedSchema.required.push(requiredProperty)
389+
}
390+
})
391+
}
392+
})
393+
}
394+
395+
return collapsedSchema
396+
}
397+
272398
/**
273399
* Returns the base URL to use for the given operation.
274400
*/
@@ -650,7 +776,8 @@ function GetOneOfTargetGraphQLType<TSource, TContext, TArgs>(
650776
// Target GraphQL types of all the member schemas
651777
const memberTargetTypes: TargetGraphQLType[] = []
652778
schema.oneOf.forEach((memberSchema) => {
653-
const memberTargetType = getSchemaTargetGraphQLType(memberSchema, data, oas)
779+
const collapsedMemberSchema = resolveAllOf(memberSchema, {}, data, oas);
780+
const memberTargetType = getSchemaTargetGraphQLType(collapsedMemberSchema, data, oas)
654781

655782
if (memberTargetType !== null) {
656783
memberTargetTypes.push(memberTargetType)

packages/openapi-to-graphql/src/preprocessor.ts

Lines changed: 18 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -699,11 +699,23 @@ export function createDataDef<TSource, TContext, TArgs>(
699699
// Found existing data definition and fetch it
700700
const existingDataDef = data.defs[index]
701701

702-
collapseLinksIntoDataDefinition({
703-
additionalLinks: saneLinks,
704-
existingDataDef,
705-
data
706-
})
702+
if (existingDataDef.targetGraphQLType === TargetGraphQLType.oneOfUnion && Array.isArray(existingDataDef.subDefinitions) // Special handling for oneOf. Sub definitions are always an array (see createOneOfUnion)
703+
) {
704+
existingDataDef.subDefinitions.forEach((def) => {
705+
collapseLinksIntoDataDefinition({
706+
additionalLinks: saneLinks,
707+
existingDataDef: def,
708+
data,
709+
})
710+
}
711+
)
712+
} else {
713+
collapseLinksIntoDataDefinition({
714+
additionalLinks: saneLinks,
715+
existingDataDef,
716+
data,
717+
})
718+
}
707719

708720
return existingDataDef
709721
}
@@ -734,7 +746,7 @@ export function createDataDef<TSource, TContext, TArgs>(
734746
* Recursively resolve allOf so type, properties, anyOf, oneOf, and
735747
* required are resolved
736748
*/
737-
const collapsedSchema = resolveAllOf(schema, {}, data, oas) as SchemaObject
749+
const collapsedSchema = Oas3Tools.resolveAllOf(schema, {}, data, oas) as SchemaObject
738750

739751
const targetGraphQLType = Oas3Tools.getSchemaTargetGraphQLType(
740752
collapsedSchema,
@@ -1253,132 +1265,6 @@ function addObjectPropertiesToDataDef<TSource, TContext, TArgs>(
12531265
}
12541266
}
12551267

1256-
/**
1257-
* Recursively traverse a schema and resolve allOf by appending the data to the
1258-
* parent schema
1259-
*/
1260-
function resolveAllOf<TSource, TContext, TArgs>(
1261-
schema: SchemaObject | ReferenceObject,
1262-
references: { [reference: string]: SchemaObject },
1263-
data: PreprocessingData<TSource, TContext, TArgs>,
1264-
oas: Oas3
1265-
): SchemaObject {
1266-
// Dereference schema
1267-
if ('$ref' in schema && typeof schema.$ref === 'string') {
1268-
if (schema.$ref in references) {
1269-
return references[schema.$ref]
1270-
}
1271-
1272-
const reference = schema.$ref
1273-
1274-
schema = Oas3Tools.resolveRef(schema.$ref, oas) as SchemaObject
1275-
references[reference] = schema
1276-
}
1277-
1278-
/**
1279-
* TODO: Is there a better method to copy the schema?
1280-
*
1281-
* Copy the schema
1282-
*/
1283-
const collapsedSchema: SchemaObject = JSON.parse(JSON.stringify(schema))
1284-
1285-
// Resolve allOf
1286-
if (Array.isArray(collapsedSchema.allOf)) {
1287-
collapsedSchema.allOf.forEach((memberSchema) => {
1288-
const collapsedMemberSchema = resolveAllOf(
1289-
memberSchema,
1290-
references,
1291-
data,
1292-
oas
1293-
)
1294-
1295-
// Collapse type if applicable
1296-
if (collapsedMemberSchema.type) {
1297-
if (!collapsedSchema.type) {
1298-
collapsedSchema.type = collapsedMemberSchema.type
1299-
1300-
// Check for incompatible schema type
1301-
} else if (collapsedSchema.type !== collapsedMemberSchema.type) {
1302-
handleWarning({
1303-
mitigationType: MitigationTypes.UNRESOLVABLE_SCHEMA,
1304-
message:
1305-
`Resolving 'allOf' field in schema '${JSON.stringify(
1306-
collapsedSchema
1307-
)}' ` + `results in incompatible schema type.`,
1308-
data,
1309-
log: preprocessingLog
1310-
})
1311-
}
1312-
}
1313-
1314-
// Collapse properties if applicable
1315-
if ('properties' in collapsedMemberSchema) {
1316-
if (!('properties' in collapsedSchema)) {
1317-
collapsedSchema.properties = {}
1318-
}
1319-
1320-
Object.entries(collapsedMemberSchema.properties).forEach(
1321-
([propertyName, property]) => {
1322-
if (!(propertyName in collapsedSchema.properties)) {
1323-
collapsedSchema.properties[propertyName] = property
1324-
1325-
// Conflicting property
1326-
} else {
1327-
handleWarning({
1328-
mitigationType: MitigationTypes.UNRESOLVABLE_SCHEMA,
1329-
message:
1330-
`Resolving 'allOf' field in schema '${JSON.stringify(
1331-
collapsedSchema
1332-
)}' ` +
1333-
`results in incompatible property field '${propertyName}'.`,
1334-
data,
1335-
log: preprocessingLog
1336-
})
1337-
}
1338-
}
1339-
)
1340-
}
1341-
1342-
// Collapse oneOf if applicable
1343-
if ('oneOf' in collapsedMemberSchema) {
1344-
if (!('oneOf' in collapsedSchema)) {
1345-
collapsedSchema.oneOf = []
1346-
}
1347-
1348-
collapsedMemberSchema.oneOf.forEach((oneOfProperty) => {
1349-
collapsedSchema.oneOf.push(oneOfProperty)
1350-
})
1351-
}
1352-
1353-
// Collapse anyOf if applicable
1354-
if ('anyOf' in collapsedMemberSchema) {
1355-
if (!('anyOf' in collapsedSchema)) {
1356-
collapsedSchema.anyOf = []
1357-
}
1358-
1359-
collapsedMemberSchema.anyOf.forEach((anyOfProperty) => {
1360-
collapsedSchema.anyOf.push(anyOfProperty)
1361-
})
1362-
}
1363-
1364-
// Collapse required if applicable
1365-
if ('required' in collapsedMemberSchema) {
1366-
if (!('required' in collapsedSchema)) {
1367-
collapsedSchema.required = []
1368-
}
1369-
1370-
collapsedMemberSchema.required.forEach((requiredProperty) => {
1371-
if (!collapsedSchema.required.includes(requiredProperty)) {
1372-
collapsedSchema.required.push(requiredProperty)
1373-
}
1374-
})
1375-
}
1376-
})
1377-
}
1378-
1379-
return collapsedSchema
1380-
}
1381-
13821268
type MemberSchemaData = {
13831269
allTargetGraphQLTypes: TargetGraphQLType[]
13841270
allProperties: { [key: string]: SchemaObject | ReferenceObject }[]

0 commit comments

Comments
 (0)