From 60d355c4eed6f91106f214ec89a84bdbe8d18138 Mon Sep 17 00:00:00 2001 From: Leslie Fung Date: Thu, 11 Jul 2019 11:38:19 +1000 Subject: [PATCH] double support (#328) Added support for `Number` and `Double` data types --- .../__snapshots__/openapi2.spec.ts.snap | 4 +- .../__snapshots__/openapi3.spec.ts.snap | 4 +- lib/src/generators/contract/json-schema.ts | 1 + .../contract/openapi2-schema.spec.ts | 273 ++++---- .../generators/contract/openapi2-schema.ts | 8 +- .../contract/openapi3-schema.spec.ts | 584 +++++++++--------- .../generators/contract/openapi3-schema.ts | 8 +- lib/src/mockserver/dummy.ts | 1 + .../models/types/custom-primitive-types.ts | 9 + lib/src/models/types/index.ts | 4 +- lib/src/models/types/kinds.ts | 6 +- lib/src/parsers/utilities/type-parser.spec.ts | 20 +- lib/src/parsers/utilities/type-parser.ts | 6 + .../utilities/type-reference-resolver.ts | 7 +- lib/src/syntax/types.ts | 8 +- lib/src/utilities/extract-union-types.ts | 1 + lib/src/verifiers/utilities/is-url-safe.ts | 1 + 17 files changed, 508 insertions(+), 437 deletions(-) diff --git a/lib/src/generators/contract/__snapshots__/openapi2.spec.ts.snap b/lib/src/generators/contract/__snapshots__/openapi2.spec.ts.snap index 14a14e2ce..28770513c 100644 --- a/lib/src/generators/contract/__snapshots__/openapi2.spec.ts.snap +++ b/lib/src/generators/contract/__snapshots__/openapi2.spec.ts.snap @@ -161,7 +161,8 @@ exports[`OpenAPI 2 generator produces valid code: json 1`] = ` \\"type\\": \\"string\\" }, \\"age\\": { - \\"type\\": \\"number\\" + \\"type\\": \\"number\\", + \\"format\\": \\"float\\" }, \\"email\\": { \\"$ref\\": \\"#/definitions/Email\\" @@ -387,6 +388,7 @@ definitions: type: string age: type: number + format: float email: $ref: '#/definitions/Email' address: diff --git a/lib/src/generators/contract/__snapshots__/openapi3.spec.ts.snap b/lib/src/generators/contract/__snapshots__/openapi3.spec.ts.snap index 1ea695755..436d1a94c 100644 --- a/lib/src/generators/contract/__snapshots__/openapi3.spec.ts.snap +++ b/lib/src/generators/contract/__snapshots__/openapi3.spec.ts.snap @@ -208,7 +208,8 @@ exports[`OpenAPI 3 generator produces valid code: json 1`] = ` \\"type\\": \\"string\\" }, \\"age\\": { - \\"type\\": \\"number\\" + \\"type\\": \\"number\\", + \\"format\\": \\"float\\" }, \\"email\\": { \\"$ref\\": \\"#/components/schemas/Email\\" @@ -454,6 +455,7 @@ components: type: string age: type: number + format: float email: $ref: '#/components/schemas/Email' address: diff --git a/lib/src/generators/contract/json-schema.ts b/lib/src/generators/contract/json-schema.ts index 9561ffeed..fb9f41177 100644 --- a/lib/src/generators/contract/json-schema.ts +++ b/lib/src/generators/contract/json-schema.ts @@ -57,6 +57,7 @@ export function jsonTypeSchema(type: DataType): JsonSchemaType { const: type.value }; case TypeKind.FLOAT: + case TypeKind.DOUBLE: return { type: "number" }; diff --git a/lib/src/generators/contract/openapi2-schema.spec.ts b/lib/src/generators/contract/openapi2-schema.spec.ts index a9a0c7394..5bde94b50 100644 --- a/lib/src/generators/contract/openapi2-schema.spec.ts +++ b/lib/src/generators/contract/openapi2-schema.spec.ts @@ -26,131 +26,132 @@ describe("OpenAPI 2 generator", () => { test("boolean", () => { expect(openApi2TypeSchema(BOOLEAN)).toMatchInlineSnapshot(` -Object { - "type": "boolean", -} -`); + Object { + "type": "boolean", + } + `); }); test("boolean literal", () => { expect(openApi2TypeSchema(booleanLiteral(true))).toMatchInlineSnapshot(` -Object { - "enum": Array [ - true, - ], - "type": "boolean", -} -`); + Object { + "enum": Array [ + true, + ], + "type": "boolean", + } + `); expect(openApi2TypeSchema(booleanLiteral(false))).toMatchInlineSnapshot(` -Object { - "enum": Array [ - false, - ], - "type": "boolean", -} -`); + Object { + "enum": Array [ + false, + ], + "type": "boolean", + } + `); }); test("string", () => { expect(openApi2TypeSchema(STRING)).toMatchInlineSnapshot(` -Object { - "type": "string", -} -`); + Object { + "type": "string", + } + `); }); test("string literal", () => { expect(openApi2TypeSchema(stringLiteral("some literal"))) .toMatchInlineSnapshot(` -Object { - "enum": Array [ - "some literal", - ], - "type": "string", -} -`); + Object { + "enum": Array [ + "some literal", + ], + "type": "string", + } + `); }); test("float", () => { expect(openApi2TypeSchema(FLOAT)).toMatchInlineSnapshot(` -Object { - "type": "number", -} -`); + Object { + "format": "float", + "type": "number", + } + `); }); test("number literal", () => { expect(openApi2TypeSchema(numberLiteral(1.5))).toMatchInlineSnapshot(` -Object { - "enum": Array [ - 1.5, - ], - "type": "number", -} -`); + Object { + "enum": Array [ + 1.5, + ], + "type": "number", + } + `); expect(openApi2TypeSchema(numberLiteral(-23.1))).toMatchInlineSnapshot(` -Object { - "enum": Array [ - -23.1, - ], - "type": "number", -} -`); + Object { + "enum": Array [ + -23.1, + ], + "type": "number", + } + `); }); test("int32", () => { expect(openApi2TypeSchema(INT32)).toMatchInlineSnapshot(` -Object { - "format": "int32", - "type": "integer", -} -`); + Object { + "format": "int32", + "type": "integer", + } + `); }); test("int64", () => { expect(openApi2TypeSchema(INT64)).toMatchInlineSnapshot(` -Object { - "format": "int64", - "type": "integer", -} -`); + Object { + "format": "int64", + "type": "integer", + } + `); }); test("integer literal", () => { expect(openApi2TypeSchema(numberLiteral(0))).toMatchInlineSnapshot(` -Object { - "enum": Array [ - 0, - ], - "type": "integer", -} -`); + Object { + "enum": Array [ + 0, + ], + "type": "integer", + } + `); expect(openApi2TypeSchema(numberLiteral(122))).toMatchInlineSnapshot(` -Object { - "enum": Array [ - 122, - ], - "type": "integer", -} -`); + Object { + "enum": Array [ + 122, + ], + "type": "integer", + } + `); expect(openApi2TypeSchema(numberLiteral(-1000))).toMatchInlineSnapshot(` -Object { - "enum": Array [ - -1000, - ], - "type": "integer", -} -`); + Object { + "enum": Array [ + -1000, + ], + "type": "integer", + } + `); }); test("object", () => { expect(openApi2TypeSchema(objectType([]))).toMatchInlineSnapshot(` -Object { - "properties": Object {}, - "required": Array [], - "type": "object", -} -`); + Object { + "properties": Object {}, + "required": Array [], + "type": "object", + } + `); expect( openApi2TypeSchema( objectType([ @@ -162,18 +163,19 @@ Object { ]) ) ).toMatchInlineSnapshot(` -Object { - "properties": Object { - "singleField": Object { - "type": "number", - }, - }, - "required": Array [ - "singleField", - ], - "type": "object", -} -`); + Object { + "properties": Object { + "singleField": Object { + "format": "float", + "type": "number", + }, + }, + "required": Array [ + "singleField", + ], + "type": "object", + } + `); expect( openApi2TypeSchema( objectType([ @@ -195,51 +197,52 @@ Object { ]) ) ).toMatchInlineSnapshot(` -Object { - "properties": Object { - "field1": Object { - "type": "number", - }, - "field2": Object { - "type": "string", - }, - "field3": Object { - "type": "boolean", - }, - }, - "required": Array [ - "field1", - "field2", - ], - "type": "object", -} -`); + Object { + "properties": Object { + "field1": Object { + "format": "float", + "type": "number", + }, + "field2": Object { + "type": "string", + }, + "field3": Object { + "type": "boolean", + }, + }, + "required": Array [ + "field1", + "field2", + ], + "type": "object", + } + `); }); test("array", () => { expect(openApi2TypeSchema(arrayType(STRING))).toMatchInlineSnapshot(` -Object { - "items": Object { - "type": "string", - }, - "type": "array", -} -`); + Object { + "items": Object { + "type": "string", + }, + "type": "array", + } + `); }); test("union", () => { expect(openApi2TypeSchema(unionType([STRING]))).toMatchInlineSnapshot(` -Object { - "type": "string", -} -`); + Object { + "type": "string", + } + `); expect(openApi2TypeSchema(unionType([STRING, NULL]))) .toMatchInlineSnapshot(` -Object { - "type": "string", - "x-nullable": true, -} -`); + Object { + "type": "string", + "x-nullable": true, + } + `); expect(() => openApi2TypeSchema(unionType([STRING, FLOAT, BOOLEAN])) ).toThrowError("Unions are not supported in OpenAPI 2"); @@ -251,10 +254,10 @@ Object { referenceType("OtherType", "location", TypeKind.STRING) ) ).toMatchInlineSnapshot(` -Object { - "$ref": "#/definitions/OtherType", -} -`); + Object { + "$ref": "#/definitions/OtherType", + } + `); }); }); }); diff --git a/lib/src/generators/contract/openapi2-schema.ts b/lib/src/generators/contract/openapi2-schema.ts index 531ba2b1f..1527ee496 100644 --- a/lib/src/generators/contract/openapi2-schema.ts +++ b/lib/src/generators/contract/openapi2-schema.ts @@ -44,7 +44,13 @@ export function openApi2TypeSchema(type: DataType): OpenAPI2SchemaType { }; case TypeKind.FLOAT: return { - type: "number" + type: "number", + format: "float" + }; + case TypeKind.DOUBLE: + return { + type: "number", + format: "double" }; case TypeKind.NUMBER_LITERAL: return Math.round(type.value) === type.value diff --git a/lib/src/generators/contract/openapi3-schema.spec.ts b/lib/src/generators/contract/openapi3-schema.spec.ts index d822b3dfe..8bf74e4b9 100644 --- a/lib/src/generators/contract/openapi3-schema.spec.ts +++ b/lib/src/generators/contract/openapi3-schema.spec.ts @@ -26,134 +26,135 @@ describe("OpenAPI 3 generator", () => { test("boolean", () => { expect(openApi3TypeSchema([], BOOLEAN)).toMatchInlineSnapshot(` -Object { - "type": "boolean", -} -`); + Object { + "type": "boolean", + } + `); }); test("boolean literal", () => { expect(openApi3TypeSchema([], booleanLiteral(true))) .toMatchInlineSnapshot(` -Object { - "enum": Array [ - true, - ], - "type": "boolean", -} -`); + Object { + "enum": Array [ + true, + ], + "type": "boolean", + } + `); expect(openApi3TypeSchema([], booleanLiteral(false))) .toMatchInlineSnapshot(` -Object { - "enum": Array [ - false, - ], - "type": "boolean", -} -`); + Object { + "enum": Array [ + false, + ], + "type": "boolean", + } + `); }); test("string", () => { expect(openApi3TypeSchema([], STRING)).toMatchInlineSnapshot(` -Object { - "type": "string", -} -`); + Object { + "type": "string", + } + `); }); test("string literal", () => { expect(openApi3TypeSchema([], stringLiteral("some literal"))) .toMatchInlineSnapshot(` -Object { - "enum": Array [ - "some literal", - ], - "type": "string", -} -`); + Object { + "enum": Array [ + "some literal", + ], + "type": "string", + } + `); }); test("float", () => { expect(openApi3TypeSchema([], FLOAT)).toMatchInlineSnapshot(` -Object { - "type": "number", -} -`); + Object { + "format": "float", + "type": "number", + } + `); }); test("number literal", () => { expect(openApi3TypeSchema([], numberLiteral(1.5))).toMatchInlineSnapshot(` -Object { - "enum": Array [ - 1.5, - ], - "type": "number", -} -`); + Object { + "enum": Array [ + 1.5, + ], + "type": "number", + } + `); expect(openApi3TypeSchema([], numberLiteral(-23.1))) .toMatchInlineSnapshot(` -Object { - "enum": Array [ - -23.1, - ], - "type": "number", -} -`); + Object { + "enum": Array [ + -23.1, + ], + "type": "number", + } + `); }); test("int32", () => { expect(openApi3TypeSchema([], INT32)).toMatchInlineSnapshot(` -Object { - "format": "int32", - "type": "integer", -} -`); + Object { + "format": "int32", + "type": "integer", + } + `); }); test("int64", () => { expect(openApi3TypeSchema([], INT64)).toMatchInlineSnapshot(` -Object { - "format": "int64", - "type": "integer", -} -`); + Object { + "format": "int64", + "type": "integer", + } + `); }); test("integer literal", () => { expect(openApi3TypeSchema([], numberLiteral(0))).toMatchInlineSnapshot(` -Object { - "enum": Array [ - 0, - ], - "type": "integer", -} -`); + Object { + "enum": Array [ + 0, + ], + "type": "integer", + } + `); expect(openApi3TypeSchema([], numberLiteral(123))).toMatchInlineSnapshot(` -Object { - "enum": Array [ - 123, - ], - "type": "integer", -} -`); + Object { + "enum": Array [ + 123, + ], + "type": "integer", + } + `); expect(openApi3TypeSchema([], numberLiteral(-1000))) .toMatchInlineSnapshot(` -Object { - "enum": Array [ - -1000, - ], - "type": "integer", -} -`); + Object { + "enum": Array [ + -1000, + ], + "type": "integer", + } + `); }); test("object", () => { expect(openApi3TypeSchema([], objectType([]))).toMatchInlineSnapshot(` -Object { - "properties": Object {}, - "type": "object", -} -`); + Object { + "properties": Object {}, + "type": "object", + } + `); expect( openApi3TypeSchema( [], @@ -166,18 +167,19 @@ Object { ]) ) ).toMatchInlineSnapshot(` -Object { - "properties": Object { - "singleField": Object { - "type": "number", - }, - }, - "required": Array [ - "singleField", - ], - "type": "object", -} -`); + Object { + "properties": Object { + "singleField": Object { + "format": "float", + "type": "number", + }, + }, + "required": Array [ + "singleField", + ], + "type": "object", + } + `); expect( openApi3TypeSchema( [], @@ -190,15 +192,16 @@ Object { ]) ) ).toMatchInlineSnapshot(` -Object { - "properties": Object { - "singleOptionalField": Object { - "type": "number", - }, - }, - "type": "object", -} -`); + Object { + "properties": Object { + "singleOptionalField": Object { + "format": "float", + "type": "number", + }, + }, + "type": "object", + } + `); expect( openApi3TypeSchema( [], @@ -221,82 +224,85 @@ Object { ]) ) ).toMatchInlineSnapshot(` -Object { - "properties": Object { - "field1": Object { - "type": "number", - }, - "field2": Object { - "type": "string", - }, - "field3": Object { - "type": "boolean", - }, - }, - "required": Array [ - "field1", - "field2", - ], - "type": "object", -} -`); + Object { + "properties": Object { + "field1": Object { + "format": "float", + "type": "number", + }, + "field2": Object { + "type": "string", + }, + "field3": Object { + "type": "boolean", + }, + }, + "required": Array [ + "field1", + "field2", + ], + "type": "object", + } + `); }); test("array", () => { expect(openApi3TypeSchema([], arrayType(STRING))).toMatchInlineSnapshot(` -Object { - "items": Object { - "type": "string", - }, - "type": "array", -} -`); + Object { + "items": Object { + "type": "string", + }, + "type": "array", + } + `); }); test("union", () => { expect(openApi3TypeSchema([], unionType([STRING]))) .toMatchInlineSnapshot(` -Object { - "type": "string", -} -`); + Object { + "type": "string", + } + `); expect(openApi3TypeSchema([], unionType([STRING, NULL]))) .toMatchInlineSnapshot(` -Object { - "nullable": true, - "type": "string", -} -`); + Object { + "nullable": true, + "type": "string", + } + `); expect(openApi3TypeSchema([], unionType([STRING, FLOAT, NULL]))) .toMatchInlineSnapshot(` -Object { - "nullable": true, - "oneOf": Array [ - Object { - "type": "string", - }, - Object { - "type": "number", - }, - ], -} -`); + Object { + "nullable": true, + "oneOf": Array [ + Object { + "type": "string", + }, + Object { + "format": "float", + "type": "number", + }, + ], + } + `); expect(openApi3TypeSchema([], unionType([STRING, FLOAT, BOOLEAN]))) .toMatchInlineSnapshot(` -Object { - "oneOf": Array [ - Object { - "type": "string", - }, - Object { - "type": "number", - }, - Object { - "type": "boolean", - }, - ], -} -`); + Object { + "oneOf": Array [ + Object { + "type": "string", + }, + Object { + "format": "float", + "type": "number", + }, + Object { + "type": "boolean", + }, + ], + } + `); }); test("union of types without a discriminator", () => { @@ -334,17 +340,17 @@ Object { ]) ) ).toMatchInlineSnapshot(` -Object { - "oneOf": Array [ - Object { - "$ref": "#/components/schemas/Type1", - }, - Object { - "$ref": "#/components/schemas/Type2", - }, - ], -} -`); + Object { + "oneOf": Array [ + Object { + "$ref": "#/components/schemas/Type1", + }, + Object { + "$ref": "#/components/schemas/Type2", + }, + ], + } + `); }); test("union of types with a valid discriminator", () => { @@ -398,24 +404,24 @@ Object { ]) ) ).toMatchInlineSnapshot(` -Object { - "discriminator": Object { - "mapping": Object { - "type-1": "#/components/schemas/Type1", - "type-2": "#/components/schemas/Type2", - }, - "propertyName": "disc", - }, - "oneOf": Array [ - Object { - "$ref": "#/components/schemas/Type1", - }, - Object { - "$ref": "#/components/schemas/Type2", - }, - ], -} -`); + Object { + "discriminator": Object { + "mapping": Object { + "type-1": "#/components/schemas/Type1", + "type-2": "#/components/schemas/Type2", + }, + "propertyName": "disc", + }, + "oneOf": Array [ + Object { + "$ref": "#/components/schemas/Type1", + }, + Object { + "$ref": "#/components/schemas/Type2", + }, + ], + } + `); }); test("union of types with a valid discriminator which is a type alias", () => { @@ -481,24 +487,24 @@ Object { ]) ) ).toMatchInlineSnapshot(` - Object { - "discriminator": Object { - "mapping": Object { - "referenced-type-1": "#/components/schemas/Type1", - "referenced-type-2": "#/components/schemas/Type2", - }, - "propertyName": "disc", - }, - "oneOf": Array [ - Object { - "$ref": "#/components/schemas/Type1", - }, - Object { - "$ref": "#/components/schemas/Type2", - }, - ], - } - `); + Object { + "discriminator": Object { + "mapping": Object { + "referenced-type-1": "#/components/schemas/Type1", + "referenced-type-2": "#/components/schemas/Type2", + }, + "propertyName": "disc", + }, + "oneOf": Array [ + Object { + "$ref": "#/components/schemas/Type1", + }, + Object { + "$ref": "#/components/schemas/Type2", + }, + ], + } + `); }); test("union of types with several valid discriminators", () => { @@ -568,24 +574,24 @@ Object { ]) ) ).toMatchInlineSnapshot(` -Object { - "discriminator": Object { - "mapping": Object { - "type-1": "#/components/schemas/Type1", - "type-2": "#/components/schemas/Type2", - }, - "propertyName": "disc1", - }, - "oneOf": Array [ - Object { - "$ref": "#/components/schemas/Type1", - }, - Object { - "$ref": "#/components/schemas/Type2", - }, - ], -} -`); + Object { + "discriminator": Object { + "mapping": Object { + "type-1": "#/components/schemas/Type1", + "type-2": "#/components/schemas/Type2", + }, + "propertyName": "disc1", + }, + "oneOf": Array [ + Object { + "$ref": "#/components/schemas/Type1", + }, + Object { + "$ref": "#/components/schemas/Type2", + }, + ], + } + `); }); test("union of types with one discriminator that has conflicting values", () => { @@ -655,24 +661,24 @@ Object { ]) ) ).toMatchInlineSnapshot(` -Object { - "discriminator": Object { - "mapping": Object { - "type-1": "#/components/schemas/Type1", - "type-2": "#/components/schemas/Type2", - }, - "propertyName": "disc2", - }, - "oneOf": Array [ - Object { - "$ref": "#/components/schemas/Type1", - }, - Object { - "$ref": "#/components/schemas/Type2", - }, - ], -} -`); + Object { + "discriminator": Object { + "mapping": Object { + "type-1": "#/components/schemas/Type1", + "type-2": "#/components/schemas/Type2", + }, + "propertyName": "disc2", + }, + "oneOf": Array [ + Object { + "$ref": "#/components/schemas/Type1", + }, + Object { + "$ref": "#/components/schemas/Type2", + }, + ], + } + `); }); test("union of types with where one of the discriminators is optional", () => { @@ -712,17 +718,17 @@ Object { ]) ) ).toMatchInlineSnapshot(` -Object { - "oneOf": Array [ - Object { - "$ref": "#/components/schemas/Type1", - }, - Object { - "$ref": "#/components/schemas/Type2", - }, - ], -} -`); + Object { + "oneOf": Array [ + Object { + "$ref": "#/components/schemas/Type1", + }, + Object { + "$ref": "#/components/schemas/Type2", + }, + ], + } + `); }); test("union of inline object types", () => { @@ -753,33 +759,33 @@ Object { ]) ) ).toMatchInlineSnapshot(` -Object { - "oneOf": Array [ - Object { - "properties": Object { - "disc": Object { - "enum": Array [ - "type-1", - ], - "type": "string", - }, - }, - "type": "object", - }, - Object { - "properties": Object { - "disc": Object { - "enum": Array [ - "type-2", + Object { + "oneOf": Array [ + Object { + "properties": Object { + "disc": Object { + "enum": Array [ + "type-1", + ], + "type": "string", + }, + }, + "type": "object", + }, + Object { + "properties": Object { + "disc": Object { + "enum": Array [ + "type-2", + ], + "type": "string", + }, + }, + "type": "object", + }, ], - "type": "string", - }, - }, - "type": "object", - }, - ], -} -`); + } + `); }); test("type reference", () => { @@ -789,10 +795,10 @@ Object { referenceType("OtherType", "location", TypeKind.STRING) ) ).toMatchInlineSnapshot(` -Object { - "$ref": "#/components/schemas/OtherType", -} -`); + Object { + "$ref": "#/components/schemas/OtherType", + } + `); }); }); }); diff --git a/lib/src/generators/contract/openapi3-schema.ts b/lib/src/generators/contract/openapi3-schema.ts index 58f59251c..16a996d75 100644 --- a/lib/src/generators/contract/openapi3-schema.ts +++ b/lib/src/generators/contract/openapi3-schema.ts @@ -57,7 +57,13 @@ export function openApi3TypeSchema( }; case TypeKind.FLOAT: return { - type: "number" + type: "number", + format: "float" + }; + case TypeKind.DOUBLE: + return { + type: "number", + format: "double" }; case TypeKind.NUMBER_LITERAL: return Math.round(type.value) === type.value diff --git a/lib/src/mockserver/dummy.ts b/lib/src/mockserver/dummy.ts index 9126831c9..b9c04925a 100644 --- a/lib/src/mockserver/dummy.ts +++ b/lib/src/mockserver/dummy.ts @@ -19,6 +19,7 @@ export function generateData(types: TypeDefinition[], type: DataType): any { case TypeKind.STRING_LITERAL: return type.value; case TypeKind.FLOAT: + case TypeKind.DOUBLE: return randomDouble(100); case TypeKind.INT32: return randomInteger(100); diff --git a/lib/src/models/types/custom-primitive-types.ts b/lib/src/models/types/custom-primitive-types.ts index cfe3d062f..f550e12a3 100644 --- a/lib/src/models/types/custom-primitive-types.ts +++ b/lib/src/models/types/custom-primitive-types.ts @@ -3,6 +3,7 @@ import { TypeKind } from "./kinds"; export type CustomPrimitiveType = | Int32Type | Int64Type + | DoubleType | DateType | DateTimeType; @@ -37,3 +38,11 @@ export const DATETIME: DateTimeType = { export interface DateTimeType { kind: TypeKind.DATE_TIME; } + +export const DOUBLE: DoubleType = { + kind: TypeKind.DOUBLE +}; + +export interface DoubleType { + kind: TypeKind.DOUBLE; +} diff --git a/lib/src/models/types/index.ts b/lib/src/models/types/index.ts index 34033bcc4..e00572e9f 100644 --- a/lib/src/models/types/index.ts +++ b/lib/src/models/types/index.ts @@ -2,6 +2,7 @@ import { CustomPrimitiveType, DateTimeType, DateType, + DoubleType, Int32Type, Int64Type } from "./custom-primitive-types"; @@ -34,9 +35,10 @@ export type DataType = export type NumberLikeType = | FloatType - | NumberLiteral + | DoubleType | Int32Type | Int64Type + | NumberLiteral | ReferenceType; export type StringLikeType = diff --git a/lib/src/models/types/kinds.ts b/lib/src/models/types/kinds.ts index 96f9a6598..2b8493ddd 100644 --- a/lib/src/models/types/kinds.ts +++ b/lib/src/models/types/kinds.ts @@ -3,6 +3,7 @@ export enum TypeKind { BOOLEAN = "boolean", STRING = "string", FLOAT = "float", + DOUBLE = "double", INT32 = "int32", INT64 = "int64", DATE = "date", @@ -27,9 +28,10 @@ export const StringLikeKind = [ export const NumberLikeKind = [ TypeKind.FLOAT, - TypeKind.NUMBER_LITERAL, + TypeKind.DOUBLE, TypeKind.INT32, - TypeKind.INT64 + TypeKind.INT64, + TypeKind.NUMBER_LITERAL ]; export const BooleanLikeKind = [TypeKind.BOOLEAN, TypeKind.BOOLEAN_LITERAL]; diff --git a/lib/src/parsers/utilities/type-parser.spec.ts b/lib/src/parsers/utilities/type-parser.spec.ts index 2f58f2c49..955f736fc 100644 --- a/lib/src/parsers/utilities/type-parser.spec.ts +++ b/lib/src/parsers/utilities/type-parser.spec.ts @@ -87,6 +87,14 @@ describe("type node parser", () => { }); describe("internal custom primitive types", () => { + test("parses Number type", () => { + const typeNode = createTypeNode("Number"); + + expect(parseTypeNode(typeNode)).toStrictEqual({ + kind: TypeKind.FLOAT + }); + }); + test("parses Float type", () => { const typeNode = createTypeNode("Float"); @@ -95,6 +103,14 @@ describe("type node parser", () => { }); }); + test("parses Double type", () => { + const typeNode = createTypeNode("Double"); + + expect(parseTypeNode(typeNode)).toStrictEqual({ + kind: TypeKind.DOUBLE + }); + }); + test("parses Integer type", () => { const typeNode = createTypeNode("Integer"); @@ -111,7 +127,7 @@ describe("type node parser", () => { }); }); - test("parses Integer type", () => { + test("parses Int64 type", () => { const typeNode = createTypeNode("Int64"); expect(parseTypeNode(typeNode)).toStrictEqual({ @@ -377,7 +393,7 @@ function createTypeNode(...types: string[]): TypeNode { throw new Error("at least one type required"); } const content = ` - import { Float, Integer, Int32, Int64, String, Date, DateTime } from "@airtasker/spot" + import { Number, Float, Double, Integer, Int32, Int64, String, Date, DateTime } from "@airtasker/spot" import { TypeAlias } from "./alias" interface TestInterface { diff --git a/lib/src/parsers/utilities/type-parser.ts b/lib/src/parsers/utilities/type-parser.ts index 27d04e349..6468b20dd 100644 --- a/lib/src/parsers/utilities/type-parser.ts +++ b/lib/src/parsers/utilities/type-parser.ts @@ -17,6 +17,7 @@ import { DataType, DATE, DATETIME, + DOUBLE, FLOAT, INT32, INT64, @@ -72,6 +73,8 @@ export function parseTypeNode(typeNode: TypeNode): DataType { const SPOT_TYPE_ALIASES = [ "Date", "DateTime", + "Number", + "Double", "Float", "Integer", "Int32", @@ -108,8 +111,11 @@ function parseTypeReference( } } else if (declaration.getType().isNumber()) { switch (name) { + case "Number": case "Float": return FLOAT; + case "Double": + return DOUBLE; case "Integer": case "Int32": return INT32; diff --git a/lib/src/parsers/utilities/type-reference-resolver.ts b/lib/src/parsers/utilities/type-reference-resolver.ts index 991a9dd0d..e8cc429b1 100644 --- a/lib/src/parsers/utilities/type-reference-resolver.ts +++ b/lib/src/parsers/utilities/type-reference-resolver.ts @@ -1,3 +1,4 @@ +import assertNever from "assert-never"; import { uniqBy } from "lodash"; import { Project } from "ts-morph"; import { Locatable } from "../../models/locatable"; @@ -170,6 +171,7 @@ function retrieveTypeReferencesFromType( case TypeKind.BOOLEAN: case TypeKind.STRING: case TypeKind.FLOAT: + case TypeKind.DOUBLE: case TypeKind.INT32: case TypeKind.INT64: case TypeKind.DATE: @@ -212,9 +214,8 @@ function retrieveTypeReferencesFromType( "expected type reference to resolve to a type alias or an interface" ); } - default: { - throw new Error("unexpected type reference"); - } + default: + throw assertNever(dataType.referenceKind); } } else if (isObjectType(dataType)) { return dataType.properties.reduce( diff --git a/lib/src/syntax/types.ts b/lib/src/syntax/types.ts index cad1bdf53..bdc71d447 100644 --- a/lib/src/syntax/types.ts +++ b/lib/src/syntax/types.ts @@ -1,6 +1,12 @@ -/** A float */ +/** A number */ +export type Number = number; + +/** A floating point number */ export type Float = number; +/** A double precision floating point number */ +export type Double = number; + /** An integer */ export type Integer = number; diff --git a/lib/src/utilities/extract-union-types.ts b/lib/src/utilities/extract-union-types.ts index 194a5b6dd..adfd7d5d0 100644 --- a/lib/src/utilities/extract-union-types.ts +++ b/lib/src/utilities/extract-union-types.ts @@ -12,6 +12,7 @@ export function extractNestedUnionTypes( case TypeKind.BOOLEAN: case TypeKind.STRING: case TypeKind.FLOAT: + case TypeKind.DOUBLE: case TypeKind.INT32: case TypeKind.INT64: case TypeKind.DATE: diff --git a/lib/src/verifiers/utilities/is-url-safe.ts b/lib/src/verifiers/utilities/is-url-safe.ts index d0b668126..2d4ccb6e6 100644 --- a/lib/src/verifiers/utilities/is-url-safe.ts +++ b/lib/src/verifiers/utilities/is-url-safe.ts @@ -11,6 +11,7 @@ export function isUrlSafe(typeKind: NotReferenceTypeKind): boolean { case TypeKind.BOOLEAN: case TypeKind.STRING: case TypeKind.FLOAT: + case TypeKind.DOUBLE: case TypeKind.INT32: case TypeKind.INT64: case TypeKind.DATE: