Skip to content

Commit

Permalink
core: change from arrays to indexed objects
Browse files Browse the repository at this point in the history
The code supports both Maps and POJOs, as I couldn’t decide on the best solution. Handlebars doesn’t properly support Maps yet, so I guess it’s POJOs for now (see handlebars-lang/handlebars.js#1679)

I changed to indexed types over arrays as we can iterate over objects just as well (and in insertion order, see https://www.stefanjudis.com/today-i-learned/property-order-is-predictable-in-javascript-objects-since-es2015/), and with the benefit of being able to look things up by key; including in Handlebars.
  • Loading branch information
karlvr committed Apr 16, 2020
1 parent d91570a commit 5f9f4d8
Show file tree
Hide file tree
Showing 17 changed files with 374 additions and 134 deletions.
13 changes: 8 additions & 5 deletions packages/core/src/__tests__/collection-model.spec.ts
@@ -1,4 +1,5 @@
import { createTestDocument } from './common'
import * as idx from '../indexed-type'

test('array model', async() => {
const result = await createTestDocument('collection-models/array-model-v3.yml')
Expand All @@ -7,9 +8,10 @@ test('array model', async() => {
const op1 = group1.operations[0]

expect(op1.returnType).toBeUndefined()
expect(op1.queryParams?.length).toEqual(1)
expect(idx.size(op1.queryParams!)).toEqual(1)

const queryParam1 = op1.queryParams![0]
const queryParams = idx.values(op1.queryParams!)
const queryParam1 = queryParams[0]
expect(queryParam1.name).toEqual('statuses')
expect(queryParam1.nativeType.toString()).toEqual('array Statuses_enum')
})
Expand All @@ -21,9 +23,10 @@ test('map model', async() => {
const op1 = group1.operations[0]

expect(op1.returnType).toBeUndefined()
expect(op1.queryParams?.length).toEqual(1)

const queryParam1 = op1.queryParams![0]
expect(idx.size(op1.queryParams!)).toEqual(1)

const queryParams = idx.values(op1.queryParams!)
const queryParam1 = queryParams[0]
expect(queryParam1.name).toEqual('statuses')
expect(queryParam1.nativeType.toString()).toEqual('map Statuses_model')
})
13 changes: 8 additions & 5 deletions packages/core/src/__tests__/default-value.spec.ts
@@ -1,21 +1,24 @@
import { createTestDocument } from './common'
import { CodegenPropertyType } from '@openapi-generator-plus/types'
import * as idx from '../indexed-type'

test('array property', async() => {
const result = await createTestDocument('default-value/arrays-v3.yml')

expect(result.models.length).toEqual(1)
expect(idx.size(result.models)).toEqual(1)

const model1 = result.models[0]
const models = idx.values(result.models)
const model1 = models[0]
expect(model1.name).toEqual('Test')
expect(model1.properties?.length).toEqual(2)
expect(idx.size(model1.properties!)).toEqual(2)

const prop1 = model1.properties![0]
const model1Properties = idx.values(model1.properties!)
const prop1 = model1Properties[0]
expect(prop1.name).toBe('arrayProperty')
expect(prop1.propertyType).toEqual(CodegenPropertyType.ARRAY)
expect(prop1.defaultValue).toEqual({ value: [], literalValue: '[]' })

const prop2 = model1.properties![1]
const prop2 = model1Properties[1]
expect(prop2.name).toBe('notRequiredArrayProperty')
expect(prop2.propertyType).toEqual(CodegenPropertyType.ARRAY)
expect(prop2.defaultValue).toEqual({ literalValue: 'undefined' })
Expand Down
@@ -1,4 +1,5 @@
import { createTestDocument } from './common'
import * as idx from '../indexed-type'

test('inline model name conflict', async() => {
const result = await createTestDocument('inline-model-name-conflict-v2.yml')
Expand All @@ -9,9 +10,11 @@ test('inline model name conflict', async() => {
expect(op1.returnType).toEqual('object')
expect(op1.returnNativeType?.toString()).toEqual('getTest1_200_response_model1')

expect(result.models.length).toEqual(2)
expect(idx.size(result.models)).toEqual(2)

const model1 = result.models[0]
const models = idx.values(result.models)
const model1 = models[0]
expect(model1.name).toEqual('getTest1_200_response_model')
expect(model1.properties![0].name).toEqual('prop2')
const model1Properties = idx.values(model1.properties!)
expect(model1Properties[0].name).toEqual('prop2')
})
31 changes: 19 additions & 12 deletions packages/core/src/__tests__/maps.spec.ts
@@ -1,27 +1,32 @@
import { createTestDocument } from './common'
import { CodegenPropertyType } from '@openapi-generator-plus/types'
import * as idx from '../indexed-type'

test('string map', async() => {
const result = await createTestDocument('maps/string-map-v2.yml')

expect(result.models.length).toEqual(1)
expect(idx.size(result.models)).toEqual(1)

const model1 = result.models[0]
const models = idx.values(result.models)
const model1 = models[0]
expect(model1.name).toEqual('model1')
expect(model1.properties?.length).toEqual(1)
expect(model1.properties![0].propertyType).toEqual(CodegenPropertyType.MAP)
expect(idx.size(model1.properties!)).toEqual(1)
const model1Properties = idx.values(model1.properties!)
expect(model1Properties![0].propertyType).toEqual(CodegenPropertyType.MAP)
})

test('object map', async() => {
const result = await createTestDocument('maps/object-map-v2.yml')

expect(result.models.length).toEqual(2)
expect(idx.size(result.models)).toEqual(2)

const model1 = result.models[0]
const models = idx.values(result.models)
const model1 = models[0]
expect(model1.name).toEqual('model1')
expect(model1.properties?.length).toEqual(1)
expect(idx.size(model1.properties!)).toEqual(1)

const prop1 = model1.properties![0]
const model1Properties = idx.values(model1.properties!)
const prop1 = model1Properties![0]
expect(prop1.propertyType).toEqual(CodegenPropertyType.MAP)
expect(prop1.nativeType.toString()).toEqual('map model2')

Expand All @@ -34,13 +39,15 @@ test('object map', async() => {
test('object map with no map parents', async() => {
const result = await createTestDocument('maps/object-map-v2.yml')

expect(result.models.length).toEqual(2)
expect(idx.size(result.models)).toEqual(2)

const model1 = result.models[0]
const models = idx.values(result.models)
const model1 = models[0]
expect(model1.name).toEqual('model1')
expect(model1.properties?.length).toEqual(1)
expect(idx.size(model1.properties!)).toEqual(1)

const prop1 = model1.properties![0]
const model1Properties = idx.values(model1.properties!)
const prop1 = model1Properties![0]
expect(prop1.propertyType).toEqual(CodegenPropertyType.MAP)
expect(prop1.nativeType.toString()).toEqual('map model2')

Expand Down
6 changes: 4 additions & 2 deletions packages/core/src/__tests__/odd-models.spec.ts
@@ -1,11 +1,13 @@
import { createTestDocument } from './common'
import * as idx from '../indexed-type'

test('array of strings', async() => {
const result = await createTestDocument('odd-models/array-of-strings-v2.yml', {
collectionModelsAllowed: true,
})

const model1 = result.models[0]
const models = idx.values(result.models)
const model1 = models[0]
expect(model1.name).toEqual('ArrayOfStrings')
expect(model1.nativeType.toString()).toEqual('ArrayOfStrings')
expect(model1.parent).toBeUndefined()
Expand All @@ -17,7 +19,7 @@ test('uuid', async() => {
const result = await createTestDocument('odd-models/uuid-v2.yml')

/* We don't parse the UUID type as a model */
expect(result.models.length).toEqual(0)
expect(idx.size(result.models)).toEqual(0)

/* Note that there doesn't seem to be a way to _use_ schemas like this actually */
})
28 changes: 17 additions & 11 deletions packages/core/src/__tests__/one-of.spec.ts
@@ -1,13 +1,15 @@
import { createTestDocument } from './common'
import * as idx from '../indexed-type'

test('one of discriminator', async() => {
const result = await createTestDocument('one-of/one-of-discriminator.yml')

const model1 = result.models[0]
const model4 = result.models[3]
const models = idx.values(result.models)
const model1 = models[0]
const model4 = models[3]

expect(model1.name).toEqual('Cat')
expect(model1.implements!.length).toBe(1)
expect(idx.size(model1.implements!)).toBe(1)
expect(model4.name).toEqual('MyResponseType')
expect(model4.discriminator!.name).toEqual('petType')
expect(model4.discriminator!.references.length).toEqual(3)
Expand All @@ -18,14 +20,17 @@ test('one of discriminator', async() => {
test('one of no discriminator', async() => {
const result = await createTestDocument('one-of/one-of-no-discriminator.yml')

const combinedModel = result.models[3]
const models = idx.values(result.models)
const combinedModel = models[3]
expect(combinedModel.name).toEqual('MyResponseType')
expect(combinedModel.isInterface).toBeFalsy()
expect(combinedModel.properties![0].name).toEqual('name')
expect(combinedModel.properties![1].name).toEqual('bark')
expect(combinedModel.properties![2].name).toEqual('lovesRocks')

const model1 = result.models[0]
const combinedModelProperties = idx.values(combinedModel.properties!)
expect(combinedModelProperties![0].name).toEqual('name')
expect(combinedModelProperties![1].name).toEqual('bark')
expect(combinedModelProperties![2].name).toEqual('lovesRocks')

const model1 = models[0]
expect(model1.isInterface).toBe(true)
})

Expand All @@ -37,12 +42,13 @@ test('one of discriminator missing property', async() => {
test('one of subclasses discriminator', async() => {
const result = await createTestDocument('one-of/one-of-subclasses-discriminator.yml')

const model1 = result.models[0]
const model4 = result.models[3]
const models = idx.values(result.models)
const model1 = models[0]
const model4 = models[3]

expect(model1.name).toEqual('Cat')
expect(model4.name).toEqual('Pet')
expect(model4.children?.length).toEqual(3)
expect(idx.size(model4.children!)).toEqual(3)
expect(model4.discriminator!.references.length).toEqual(3)
expect(model4.isInterface).toBeFalsy()
})
5 changes: 3 additions & 2 deletions packages/core/src/__tests__/openapiv2.spec.ts
@@ -1,4 +1,5 @@
import { createTestDocument, createTestResult } from './common'
import * as idx from '../indexed-type'

test('parse info', async() => {
const result = await createTestDocument('openapiv2-1.yml')
Expand All @@ -23,7 +24,7 @@ test('parse groups', async() => {
const op1 = group1.operations[0]
expect(op1.name).toEqual('getTest1')
expect(op1.parameters).toBeDefined()
expect(op1.parameters!.length).toEqual(1)
expect(idx.size(op1.parameters!)).toEqual(1)

expect(op1.returnType).toEqual('object')
expect(op1.returnNativeType?.toString()).toEqual('Test1Response')
Expand All @@ -34,7 +35,7 @@ test('parse groups', async() => {

const op2 = group2.operations[0]
expect(op2.name).toEqual(state.generator.toOperationName('/test2', 'GET', state)) /* Uses default name */
expect(op2.parameters!.length).toEqual(1)
expect(idx.size(op2.parameters!)).toEqual(1)
expect(op2.returnType).not.toBeDefined()
expect(op2.returnNativeType).not.toBeDefined()
})
7 changes: 4 additions & 3 deletions packages/core/src/__tests__/openapiv3.spec.ts
@@ -1,4 +1,5 @@
import { createTestDocument, createTestResult } from './common'
import * as idx from '../indexed-type'

test('process document', async() => {
const result = await createTestDocument('openapiv3-1.yml')
Expand All @@ -23,15 +24,15 @@ test('parse operation params', async() => {
const op1 = group1.operations[0]
expect(op1.name).toEqual('getTest1')
expect(op1.parameters).toBeDefined()
expect(op1.parameters!.length).toEqual(2)
expect(idx.size(op1.parameters!)).toEqual(2)
})

test('parse operation body params', async() => {
const result = await createTestDocument('openapiv3-1.yml')

const group2 = result.groups[1]
const op2 = group2.operations[0]
expect(op2.parameters!.length).toEqual(0)
expect(op2.parameters).toBeUndefined()
expect(op2.requestBody).toBeDefined()
expect(op2.requestBody!.type).toEqual('object')
expect(op2.requestBody!.nativeType?.toString()).toEqual('Test2Request')
Expand Down Expand Up @@ -72,7 +73,7 @@ test('parse groups', async() => {

const op2 = group2.operations[0]
expect(op2.name).toEqual(state.generator.toOperationName('/test2', 'POST', state)) /* Uses default name */
// expect(op2.parameters!.length).toEqual(1)
// expect(op2.allParams!.length).toEqual(1)
// expect(op2.returnType).not.toBeDefined()
// expect(op2.returnNativeType).not.toBeDefined()
})
12 changes: 8 additions & 4 deletions packages/core/src/__tests__/parameter-inline-models.spec.ts
@@ -1,19 +1,23 @@
import { createTestDocument } from './common'
import * as idx from '../indexed-type'

test('inline response model', async() => {
const result = await createTestDocument('parameter-inline-models-v2.yml')

const group1 = result.groups[0]
const op1 = group1.operations[0]

expect(op1.parameters!.length).toEqual(1)
expect(idx.size(op1.parameters!)).toEqual(1)

const param1 = op1.parameters![0]
const params = idx.values(op1.parameters!)

const param1 = params[0]
expect(param1.name).toEqual('arg1')
expect(param1.nativeType.toString()).toEqual('getTest1_arg1_enum')

expect(result.models.length).toEqual(1)
expect(idx.size(result.models)).toEqual(1)

const model1 = result.models[0]
const models = idx.values(result.models)
const model1 = models[0]
expect(model1.name).toEqual('getTest1_arg1_enum')
})
6 changes: 4 additions & 2 deletions packages/core/src/__tests__/response-inline-models.spec.ts
@@ -1,4 +1,5 @@
import { createTestDocument } from './common'
import * as idx from '../indexed-type'

test('inline response model', async() => {
const result = await createTestDocument('response-inline-models-v2.yml')
Expand All @@ -9,8 +10,9 @@ test('inline response model', async() => {
expect(op1.returnType).toEqual('object')
expect(op1.returnNativeType?.toString()).toEqual('getTest1_200_response_model')

expect(result.models.length).toEqual(1)
expect(idx.size(result.models)).toEqual(1)

const model1 = result.models[0]
const models = idx.values(result.models)
const model1 = models[0]
expect(model1.name).toEqual('getTest1_200_response_model')
})
3 changes: 2 additions & 1 deletion packages/core/src/index.ts
Expand Up @@ -4,6 +4,7 @@ import { defaultGeneratorOptions } from './generators'
import SwaggerParser from '@apidevtools/swagger-parser'
import { toSpecVersion } from './utils'
import { InternalCodegenState } from './types'
import * as idx from './indexed-type'

/**
* Construct a CodegenGenerator from the given constructor.
Expand Down Expand Up @@ -41,7 +42,7 @@ export function createCodegenDocument<O>(input: CodegenInput, state: CodegenStat
usedModelFullyQualifiedNames: {},
modelsBySchema: new Map(),
reservedNames: {},
models: [],
models: idx.create(),
specVersion: toSpecVersion(input.root),
}

Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/indexed-type.ts
@@ -0,0 +1,6 @@
/*
Enable switching between POJOs and Maps for our indexed object type.
You must also make the switch in @openapi-generator-plus/types types.ts
*/
export * from './objects'
// export * from './maps'

0 comments on commit 5f9f4d8

Please sign in to comment.