diff --git a/README.md b/README.md index acd6882..1a04161 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Node client for generating crude GraphQL specs from Swagger OpenAPI. | Enum | ✅ | | ID | ✅ | | Implements (`allOf`) | ✅ | -| Polymorphism\* (`oneOf`) | N/A | +| Polymorphism (`oneOf`) | ✅ | | Non-nullable | ✅ | | Primitives (string, boolean, number) | ✅ | | Query | 🚫 | @@ -19,9 +19,6 @@ Node client for generating crude GraphQL specs from Swagger OpenAPI. To compare actual generated output, see the [example](./example) folder. -_\* Polymorphism isn’t supported by GraphQL by design. If this encounters one -in Swagger, it’ll assign the first type it encounters and throw a warning._ - ## Usage ### CLI diff --git a/src/swagger-2.ts b/src/swagger-2.ts index 3f00574..fcae3e8 100644 --- a/src/swagger-2.ts +++ b/src/swagger-2.ts @@ -43,6 +43,7 @@ function snakeCase(name: string) { function parse(spec: Swagger2) { const queue: [string, Swagger2Definition][] = []; const enumQueue: [string, (string | number)[]][] = []; + const unionQueue: [string, Swagger2Definition[]][] = []; const output: string[] = []; const { definitions } = spec; @@ -87,13 +88,9 @@ function parse(spec: Swagger2) { return `[${TYPES[items.type]}]`; } - if (Array.isArray(value.oneOf)) { - if (value.oneOf.length > 1) { - console.warn( - `Multiple types given for ${nestedName}. Using ${JSON.stringify(value.oneOf[0])}.` - ); - } - return getType(value.oneOf[0], ''); + if (Array.isArray(value.oneOf) && value.oneOf.length > 0) { + unionQueue.push([nestedName, value.oneOf]); + return nestedName; } if (value.properties) { @@ -123,6 +120,11 @@ function parse(spec: Swagger2) { output.push('}'); } + function buildNextUnion([ID, types]: [string, Swagger2Definition[]]) { + const union = types.map(type => getType(type, '')).join(' | '); + output.push(`union ${ID} = ${union}`); + } + function buildNextObject() { const nextObject = queue.pop(); if (!nextObject) { @@ -191,6 +193,14 @@ function parse(spec: Swagger2) { buildNextEnum(nextEnum); } } + + // Clean up unionQueue + while (unionQueue.length > 0) { + const nextUnion = unionQueue.pop(); + if (nextUnion) { + buildNextUnion(nextUnion); + } + } } // Begin parsing top-level entries diff --git a/tests/swagger-2.test.ts b/tests/swagger-2.test.ts index 3baceac..3346a0c 100644 --- a/tests/swagger-2.test.ts +++ b/tests/swagger-2.test.ts @@ -180,6 +180,30 @@ describe('Swagger 2 spec', () => { expect(graphqlGen(swagger)).toBe(format(graphql)); }); + + it('converts oneOf to union types', () => { + const swagger: Swagger2 = { + definitions: { + Record: { + properties: { + rand: { + oneOf: [{ type: 'string' }, { type: 'integer' }], + type: 'array', + }, + }, + type: 'object', + }, + }, + }; + + const graphql = format(` + type Record { + rand: RecordRand + } + union RecordRand = String | Int`); + + expect(graphqlGen(swagger)).toBe(format(graphql)); + }); }); describe('complex structures', () => { @@ -266,29 +290,6 @@ describe('Swagger 2 spec', () => { expect(graphqlGen(swagger)).toBe(format(graphql)); }); - - it('handles oneOf (kinda)', () => { - const swagger: Swagger2 = { - definitions: { - Record: { - properties: { - rand: { - oneOf: [{ type: 'string' }, { type: 'number' }], - type: 'array', - }, - }, - type: 'object', - }, - }, - }; - - const graphql = format(` - type Record { - rand: String - }`); - - expect(graphqlGen(swagger)).toBe(format(graphql)); - }); }); describe('other output', () => {