- Added initial setup

This commit is contained in:
Ferdi Koomen 2019-11-05 10:39:26 +01:00
parent 9f5586523f
commit 6ffbe926d5
164 changed files with 9211 additions and 1 deletions

9
.editorconfig Executable file
View File

@ -0,0 +1,9 @@
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 4

21
.eslintrc.json Normal file
View File

@ -0,0 +1,21 @@
{
"parser": "@typescript-eslint/parser",
"extends": [
"plugin:@typescript-eslint/recommended",
"prettier/@typescript-eslint",
"plugin:prettier/recommended"
],
"env": {
"es6": true,
"node": true,
"jest": true
},
"rules": {
"@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/no-non-null-assertion": 0,
"@typescript-eslint/explicit-function-return-type": 0,
"prettier/prettier": [
"error"
]
}
}

14
.gitignore vendored Normal file
View File

@ -0,0 +1,14 @@
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
junit.xml
.DS_Store
.tmp
.idea
.vscode
*.iml
dist
archive
coverage
test/tmp

7
.prettierrc.json Normal file
View File

@ -0,0 +1,7 @@
{
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"printWidth": 500,
"tabWidth": 4
}

5
.travis.yml Normal file
View File

@ -0,0 +1,5 @@
language: node_js
node_js:
- node
script:
- npm run test

View File

@ -1 +1,65 @@
# openapi-typescript-codegen
# OpenAPI Typescript Codegen
[![NPM](https://badgen.net/npm/v/openapi-typescript-codegen)](https://www.npmjs.com/package/openapi-typescript-codegen)
[![License](https://badgen.net/npm/license/openapi-typescript-codegen)](https://www.npmjs.com/package/openapi-typescript-codegen)
[![Dependencies](https://badgen.net/david/dep/ferdikoomen/openapi-typescript-codegen)](https://david-dm.org/ferdikoomen/openapi-typescript-codegen)
[![Build Size](https://badgen.net/bundlephobia/minzip/openapi-typescript-codegen)](https://bundlephobia.com/result?p=openapi-typescript-codegen)
[![Build Status](https://badgen.net/travis/ferdikoomen/openapi-typescript-codegen/master)](https://travis-ci.org/ferdikoomen/openapi-typescript-codegen)
[![Quality](https://badgen.net/lgtm/grade/javascript/g/ferdikoomen/openapi-typescript-codegen)](https://lgtm.com/projects/g/ferdikoomen/openapi-typescript-codegen)
> NodeJS library that generates Typescript or Javascript clients based on the OpenAPI specification.
#### Why?
- Frontend ❤️ OpenAPI, but we do not want to use JAVA codegen in our builds.
- Quick, lightweight, robust and framework agnostic.
- Supports generation of Typescript and Javascript clients.
- Supports generations of fetch and XHR http clients.
- Supports OpenAPI specification v2.0 and v3.0.
- Supports JSON and YAML files for input.
## Known issues:
- If you use enums inside your models / definitions then those enums are now
inside a namespace with the same name as your model. This is called declaration
merging. However Babel 7 now support compiling of Typescript and right now they
do not support namespaces.
## Installation
```
npm install openapi-typescript-codegen --save-dev
```
## Example
**package.json**
```json
{
"scripts": {
"generate": "openapi ./api/openapi.json ./dist"
}
...
}
```
Command line
```
npm install openapi-typescript-codegen -g
openapi ./api/openapi.json ./dist
```
NodeJS API:
```
const OpenAPI = require('openapi-typescript-codegen');
const result = OpenAPI.generate(
'./api/openapi.json',
'./dist'
);
console.log(result);
```

29
bin/index.js Executable file
View File

@ -0,0 +1,29 @@
#!/usr/bin/env node
'use strict';
const path = require('path');
const program = require('commander');
const pkg = require('../package.json');
program
.version(pkg.version)
.option('--input [value]', 'Path to swagger specification', './spec.json')
.option('--output [value]', 'Output directory', './generated')
.option('--language', 'Language to generate [typescript, javascript]', 'typescript')
.option('--http-client', 'HTTP client to generate [fetch, xhr]', 'fetch')
.parse(process.argv);
const SwaggerCodegen = require(path.resolve(__dirname, '../dist/index.js'));
if (SwaggerCodegen) {
const result = SwaggerCodegen.generate(
program.input,
program.output,
program.language,
program.httpClient
);
console.log(result);
}

17
jest.config.js Normal file
View File

@ -0,0 +1,17 @@
module.exports = {
testRegex: '\\.spec\\.tsx?$',
testEnvironment: 'node',
transform: {
'\\.tsx?$': 'ts-jest'
},
globals: {
'ts-jest': {
compiler: 'typescript',
tsConfig: {
declaration: false,
declarationMap: false,
sourceMap: false
}
}
}
};

BIN
logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

86
package.json Normal file
View File

@ -0,0 +1,86 @@
{
"name": "openapi-typescript-codegen",
"version": "0.0.1",
"description": "NodeJS library that generates Typescript or Javascript clients based on the OpenAPI specification.",
"author": "Ferdi Koomen",
"homepage": "https://github.com/ferdikoomen/openapi-typescript-codegen",
"repository": {
"type": "git",
"url": "git+https://github.com/ferdikoomen/openapi-typescript-codegen.git"
},
"bugs": {
"url": "https://github.com/ferdikoomen/openapi-typescript-codegen/issues"
},
"license": "MIT",
"keywords": [
"openapi",
"swagger",
"codegen",
"generator",
"client",
"typescript",
"javascript",
"yaml",
"json",
"fetch",
"xhr",
"node"
],
"maintainers": [
{
"name": "Ferdi Koomen",
"email": "info@madebyferdi.com"
}
],
"main": "dist/index.js",
"module": "dist/index.js",
"bin": {
"openapi": "bin/index.js"
},
"files": [
"bin/index.js",
"dist/index.js",
"dist/**/*.js",
"src/templates/javascript/*.hbs",
"src/templates/typescript/*.hbs"
],
"scripts": {
"clean": "rimraf \"./dist\" \"./test/tmp\"",
"build": "tsc",
"start": "tsc && node ./test/index.js",
"test": "jest ./src",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"eslint": "eslint \"./src/**/*.ts\"",
"eslint:fix": "eslint \"./src/**/*.ts\" --fix",
"prettier": "prettier \"./src/**/*.ts\" --check",
"prettier:fix": "prettier \"./src/**/*.ts\" --write"
},
"devDependencies": {
"@types/jest": "24.0.20",
"@types/js-yaml": "3.12.1",
"@types/mkdirp": "0.5.2",
"@types/node": "12.11.7",
"@types/rimraf": "2.0.3",
"@types/yup": "0.26.24",
"@typescript-eslint/eslint-plugin": "2.5.0",
"@typescript-eslint/parser": "2.5.0",
"camelcase": "5.3.1",
"chalk": "2.4.2",
"commander": "3.0.2",
"eslint": "6.6.0",
"eslint-config-prettier": "6.5.0",
"eslint-plugin-prettier": "3.1.1",
"handlebars": "4.4.5",
"jest": "24.9.0",
"jest-cli": "24.9.0",
"js-yaml": "3.13.1",
"mkdirp": "0.5.1",
"path": "0.12.7",
"prettier": "1.18.2",
"rimraf": "3.0.0",
"ts-jest": "24.1.0",
"typescript": "3.6.4",
"yup": "0.27.0"
}
}

11
src/client/interfaces/Client.d.ts vendored Normal file
View File

@ -0,0 +1,11 @@
import { Model } from './Model';
import { Service } from './Service';
import { Schema } from './Schema';
export interface Client {
version: string;
server: string;
models: Map<string, Model>;
schemas: Map<string, Schema>;
services: Map<string, Service>;
}

14
src/client/interfaces/Model.d.ts vendored Normal file
View File

@ -0,0 +1,14 @@
import { ModelProperty } from './ModelProperty';
import { ModelEnum } from './ModelEnum';
export interface Model {
name: string;
base: string;
type: string;
template: string;
description: string | null;
extends: string | null;
imports: string[];
properties: ModelProperty[];
enums: ModelEnum[];
}

View File

@ -0,0 +1,7 @@
import { ModelEnumProperty } from './ModelEnumProperty';
export interface ModelEnum {
name: string;
value: string;
values: ModelEnumProperty[];
}

View File

@ -0,0 +1,5 @@
export interface ModelEnumProperty {
type: string;
name: string;
value: string | number;
}

View File

@ -0,0 +1,8 @@
import { ModelProperty } from './ModelProperty';
import { ModelEnum } from './ModelEnum';
export interface ModelProperties {
imports: string[];
properties: ModelProperty[];
enums: ModelEnum[];
}

View File

@ -0,0 +1,10 @@
export interface ModelProperty {
name: string;
type: string;
base: string;
template: string | null;
description: string | null;
required: boolean;
readOnly: boolean;
imports: string[];
}

5
src/client/interfaces/Schema.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
export interface Schema {
name: string;
base: string;
imports: [];
}

5
src/client/interfaces/Service.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
export interface Service {
name: string;
base: string;
imports: [];
}

6
src/client/interfaces/Type.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
export interface Type {
type: string;
base: string;
template: string | null;
imports: string[];
}

65
src/index.ts Normal file
View File

@ -0,0 +1,65 @@
import * as path from 'path';
import { parse as parseV2 } from './openApi/v2';
import { parse as parseV3 } from './openApi/v3';
import { readHandlebarsTemplates } from './utils/readHandlebarsTemplates';
import { getOpenApiSpec } from './utils/getOpenApiSpec';
import { writeClient } from './utils/writeClient';
import * as os from 'os';
import chalk from 'chalk';
import { getOpenApiVersion, OpenApiVersion } from './utils/getOpenApiVersion';
export enum Language {
TYPESCRIPT = 'typescript',
JAVASCRIPT = 'javascript',
}
export enum HttpClient {
FETCH = 'fetch',
XHR = 'xhr',
}
/**
* Generate the OpenAPI client. This method will read the OpenAPI specification and based on the
* given language it will generate the client, including the types models, validation schemas,
* service layer, etc.
* @param input The relative location of the OpenAPI spec.
* @param output The relative location of the output directory
* @param language: The language that should be generated (Typescript or Javascript)
* @param httpClient: The selected httpClient (fetch or XHR)
*/
export function generate(input: string, output: string, language: Language = Language.TYPESCRIPT, httpClient: HttpClient = HttpClient.FETCH): void {
const inputPath = path.resolve(process.cwd(), input);
const outputPath = path.resolve(process.cwd(), output);
console.log(chalk.bold.green('Generate:'));
console.log(chalk.grey(' Input:'), input);
console.log(chalk.grey(' Output:'), output);
console.log(chalk.grey(' Language:'), language);
console.log(chalk.grey(' HTTP client:'), httpClient);
console.log(os.EOL);
try {
const openApi = getOpenApiSpec(inputPath);
const openApiVersion = getOpenApiVersion(openApi);
const templates = readHandlebarsTemplates(language);
switch (language) {
case Language.JAVASCRIPT:
case Language.TYPESCRIPT:
// Generate and write version 2 client
if (openApiVersion === OpenApiVersion.V2) {
const clientV2 = parseV2(openApi);
writeClient(clientV2, language, templates, outputPath);
}
// Generate and write version 3 client
if (openApiVersion === OpenApiVersion.V3) {
const clientV3 = parseV3(openApi);
writeClient(clientV3, language, templates, outputPath);
}
}
} catch (e) {
console.error(e);
process.exit(1);
}
}

16
src/openApi/v2/index.ts Normal file
View File

@ -0,0 +1,16 @@
import { OpenApi } from './interfaces/OpenApi';
import { Client } from '../../client/interfaces/Client';
import { getServer } from './parser/getServer';
import { getServices } from './parser/getServices';
import { getModels } from './parser/getModels';
import { getSchemas } from './parser/getSchemas';
export function parse(openApi: OpenApi): Client {
return {
version: openApi.info.version,
server: getServer(openApi),
models: getModels(openApi),
schemas: getSchemas(openApi),
services: getServices(openApi),
};
}

31
src/openApi/v2/interfaces/OpenApi.d.ts vendored Normal file
View File

@ -0,0 +1,31 @@
import { Dictionary } from '../../../utils/types';
import { OpenApiExternalDocs } from './OpenApiExternalDocs';
import { OpenApiInfo } from './OpenApiInfo';
import { OpenApiParameter } from './OpenApiParameter';
import { OpenApiPath } from './OpenApiPath';
import { OpenApiResponse } from './OpenApiResponse';
import { OpenApiSchema } from './OpenApiSchema';
import { OpenApiSecurityRequirement } from './OpenApiSecurityRequirement';
import { OpenApiSecurityScheme } from './OpenApiSecurityScheme';
import { OpenApiTag } from './OpenApiTag';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md
*/
export interface OpenApi {
swagger: string;
info: OpenApiInfo;
host?: string;
basePath?: string;
schemes?: string[];
consumes?: string[];
produces?: string[];
paths: Dictionary<OpenApiPath>;
definitions?: Dictionary<OpenApiSchema>;
parameters?: Dictionary<OpenApiParameter>;
responses?: Dictionary<OpenApiResponse>;
securityDefinitions?: Dictionary<OpenApiSecurityScheme>;
security?: OpenApiSecurityRequirement[];
tags?: OpenApiTag[];
externalDocs?: OpenApiExternalDocs;
}

View File

@ -0,0 +1,8 @@
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#contactObject
*/
export interface OpenApiContact {
name?: string;
url?: string;
email?: string;
}

View File

@ -0,0 +1,6 @@
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#exampleObject
*/
export interface OpenApiExample {
[mimetype: string]: any;
}

View File

@ -0,0 +1,7 @@
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#externalDocumentationObject
*/
export interface OpenApiExternalDocs {
description?: string;
url: string;
}

View File

@ -0,0 +1,26 @@
import { Dictionary } from '../../../utils/types';
import { OpenApiItems } from './OpenApiItems';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#headerObject
*/
export interface OpenApiHeader {
description?: string;
type: 'string' | 'number' | 'integer' | 'boolean' | 'array';
format?: 'int32' | 'int64' | 'float' | 'double' | 'string' | 'boolean' | 'byte' | 'binary' | 'date' | 'date-time' | 'password';
items?: Dictionary<OpenApiItems>;
collectionFormat?: 'csv' | 'ssv' | 'tsv' | 'pipes';
default?: any;
maximum?: number;
exclusiveMaximum?: boolean;
minimum?: number;
exclusiveMinimum?: boolean;
maxLength?: number;
minLength?: number;
pattern?: string;
maxItems?: number;
minItems?: number;
uniqueItems?: boolean;
enum?: string[] | number[];
multipleOf?: number;
}

View File

@ -0,0 +1,14 @@
import { OpenApiContact } from './OpenApiContact';
import { OpenApiLicense } from './OpenApiLicense';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#infoObject
*/
export interface OpenApiInfo {
title: string;
description?: string;
termsOfService?: string;
contact?: OpenApiContact;
license?: OpenApiLicense;
version: string;
}

View File

@ -0,0 +1,22 @@
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#itemsObject
*/
export interface OpenApiItems {
type?: 'string' | 'number' | 'integer' | 'boolean' | 'array';
format?: 'int32' | 'int64' | 'float' | 'double' | 'string' | 'boolean' | 'byte' | 'binary' | 'date' | 'date-time' | 'password';
items?: OpenApiItems;
collectionFormat?: 'csv' | 'ssv' | 'tsv' | 'pipes';
default?: any;
maximum?: number;
exclusiveMaximum?: number;
minimum?: number;
exclusiveMinimum?: number;
maxLength?: number;
minLength?: number;
pattern?: string;
maxItems?: number;
minItems?: number;
uniqueItems?: number;
enum?: string[] | number[];
multipleOf?: number;
}

View File

@ -0,0 +1,7 @@
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#licenseObject
*/
export interface OpenApiLicense {
name: string;
url?: string;
}

View File

@ -0,0 +1,23 @@
import { OpenApiExternalDocs } from './OpenApiExternalDocs';
import { OpenApiParameter } from './OpenApiParameter';
import { OpenApiReference } from './OpenApiReference';
import { OpenApiResponses } from './OpenApiResponses';
import { OpenApiSecurityRequirement } from './OpenApiSecurityRequirement';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#operationObject
*/
export interface OpenApiOperation {
tags?: string[];
summary?: string;
description?: string;
externalDocs?: OpenApiExternalDocs;
operationId?: string;
consumes?: string[];
produces?: string[];
parameters?: OpenApiParameter[] | OpenApiReference[];
responses: OpenApiResponses;
schemes: ('http' | 'https' | 'ws' | 'wss')[];
deprecated?: boolean;
security?: OpenApiSecurityRequirement[];
}

View File

@ -0,0 +1,31 @@
import { OpenApiItems } from './OpenApiItems';
import { OpenApiSchema } from './OpenApiSchema';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#parameterObject
*/
export interface OpenApiParameter {
name: string;
in: 'path' | 'query' | 'header' | 'formData' | 'body';
description?: string;
required?: boolean;
schema?: OpenApiSchema;
type?: 'string' | 'number' | 'integer' | 'boolean' | 'array' | 'file';
format?: 'int32' | 'int64' | 'float' | 'double' | 'string' | 'boolean' | 'byte' | 'binary' | 'date' | 'date-time' | 'password';
allowEmptyValue?: boolean;
items?: OpenApiItems;
collectionFormat?: 'csv' | 'ssv' | 'tsv' | 'pipes' | 'multi';
default?: any;
maximum?: number;
exclusiveMaximum?: boolean;
minimum?: number;
exclusiveMinimum?: boolean;
maxLength?: number;
minLength?: number;
pattern?: string;
maxItems?: number;
minItems?: number;
uniqueItems?: boolean;
enum?: string[] | number[];
multipleOf?: number;
}

View File

@ -0,0 +1,18 @@
import { OpenApiOperation } from './OpenApiOperation';
import { OpenApiParameter } from './OpenApiParameter';
import { OpenApiReference } from './OpenApiReference';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#pathItemObject
*/
export interface OpenApiPath {
$ref?: string;
get?: OpenApiOperation;
put?: OpenApiOperation;
post?: OpenApiOperation;
delete?: OpenApiOperation;
options?: OpenApiOperation;
head?: OpenApiOperation;
patch?: OpenApiOperation;
parameters?: OpenApiParameter[] | OpenApiReference[];
}

View File

@ -0,0 +1,6 @@
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#referenceObject
*/
export interface OpenApiReference {
$ref: string;
}

View File

@ -0,0 +1,14 @@
import { Dictionary } from '../../../utils/types';
import { OpenApiExample } from './OpenApiExample';
import { OpenApiHeader } from './OpenApiHeader';
import { OpenApiSchema } from './OpenApiSchema';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#responseObject
*/
export interface OpenApiResponse {
description: string;
schema?: OpenApiSchema;
headers?: Dictionary<OpenApiHeader>;
examples?: OpenApiExample;
}

View File

@ -0,0 +1,11 @@
import { OpenApiReference } from './OpenApiReference';
import { OpenApiResponse } from './OpenApiResponse';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#responsesObject
*/
export interface OpenApiResponses {
[httpcode: string]: OpenApiResponse | OpenApiReference;
default: OpenApiResponse | OpenApiReference;
}

View File

@ -0,0 +1,40 @@
import { Dictionary } from '../../../utils/types';
import { OpenApiExternalDocs } from './OpenApiExternalDocs';
import { OpenApiReference } from './OpenApiReference';
import { OpenApiXml } from './OpenApiXml';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#schemaObject
*/
export interface OpenApiSchema {
$ref?: string;
format?: 'int32' | 'int64' | 'float' | 'double' | 'string' | 'boolean' | 'byte' | 'binary' | 'date' | 'date-time' | 'password';
title?: string;
description?: string;
default?: any;
multipleOf?: number;
maximum?: number;
exclusiveMaximum?: boolean;
minimum?: number;
exclusiveMinimum?: boolean;
maxLength?: number;
minLength?: number;
pattern?: string;
maxItems?: number;
minItems?: number;
uniqueItems?: number;
maxProperties?: number;
minProperties?: number;
required?: string[];
enum?: string[] | number[];
type?: string;
items?: OpenApiReference | OpenApiSchema;
allOf?: OpenApiReference[] | OpenApiSchema[];
properties?: Dictionary<OpenApiSchema>;
additionalProperties?: boolean | OpenApiReference | OpenApiSchema;
discriminator?: string;
readOnly?: boolean;
xml?: OpenApiXml;
externalDocs?: OpenApiExternalDocs;
example?: any;
}

View File

@ -0,0 +1,6 @@
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#securityRequirementObject
*/
export interface OpenApiSecurityRequirement {
[key: string]: string;
}

View File

@ -0,0 +1,15 @@
import { Dictionary } from '../../../utils/types';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#securitySchemeObject
*/
export interface OpenApiSecurityScheme {
type: 'basic' | 'apiKey' | 'oauth2';
description?: string;
name?: string;
in?: 'query' | 'header';
flow?: 'implicit' | 'password' | 'application' | 'accessCode';
authorizationUrl?: string;
tokenUrl?: string;
scopes: Dictionary<string>;
}

View File

@ -0,0 +1,10 @@
import { OpenApiExternalDocs } from './OpenApiExternalDocs';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#tagObject
*/
export interface OpenApiTag {
name: string;
description?: string;
externalDocs?: OpenApiExternalDocs;
}

View File

@ -0,0 +1,10 @@
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#xmlObject
*/
export interface OpenApiXml {
name?: string;
namespace?: string;
prefix?: string;
attribute?: boolean;
wrapped?: boolean;
}

View File

@ -0,0 +1,23 @@
import { getMappedType } from './getMappedType';
describe('getMappedType', () => {
it('should map types to the basics', () => {
expect(getMappedType('File')).toEqual('File');
expect(getMappedType('Array')).toEqual('any[]');
expect(getMappedType('List')).toEqual('any[]');
expect(getMappedType('String')).toEqual('string');
expect(getMappedType('date')).toEqual('string');
expect(getMappedType('date-time')).toEqual('string');
expect(getMappedType('float')).toEqual('number');
expect(getMappedType('double')).toEqual('number');
expect(getMappedType('short')).toEqual('number');
expect(getMappedType('int')).toEqual('number');
expect(getMappedType('boolean')).toEqual('boolean');
expect(getMappedType('any')).toEqual('any');
expect(getMappedType('object')).toEqual('any');
expect(getMappedType('void')).toEqual('void');
expect(getMappedType('null')).toEqual('null');
expect(getMappedType('Unknown')).toEqual('Unknown');
expect(getMappedType('')).toEqual('');
});
});

View File

@ -0,0 +1,42 @@
const MAPPINGS = new Map<string, string>([
['file', 'File'],
['binary', 'File'],
['array', 'any[]'],
['list', 'any[]'],
['object', 'any'],
['any', 'any'],
['boolean', 'boolean'],
['byte', 'number'],
['int', 'number'],
['int32', 'number'],
['int64', 'number'],
['integer', 'number'],
['float', 'number'],
['double', 'number'],
['short', 'number'],
['long', 'number'],
['number', 'number'],
['char', 'string'],
['date', 'string'],
['date-time', 'string'],
['password', 'string'],
['string', 'string'],
['void', 'void'],
['null', 'null'],
]);
/**
* Get mapped type for given type to any basic Typescript/Javascript type.
* @param type
*/
export function getMappedType(type: string): string {
const mapped = MAPPINGS.get(type.toLowerCase());
if (mapped) {
return mapped;
}
return type;
}
export function hasMappedType(type: string): boolean {
return MAPPINGS.has(type.toLowerCase());
}

View File

@ -0,0 +1,9 @@
import { ModelProperties } from '../../../client/interfaces/ModelProperties';
export function parseModelProperties(): ModelProperties {
return {
imports: [],
properties: [],
enums: [],
};
}

View File

@ -0,0 +1,14 @@
import { ModelProperty } from '../../../client/interfaces/ModelProperty';
export function parseModelProperty(): ModelProperty {
return {
name: '',
type: '',
base: '',
template: '',
description: null,
required: false,
readOnly: false,
imports: [],
};
}

View File

@ -0,0 +1,23 @@
import { getModelTemplate } from './getModelTemplate';
describe('getModelTemplate', () => {
it('should return generic for template type', () => {
const template: string = getModelTemplate({
type: 'Link<Model>',
base: 'Link',
template: 'Model',
imports: ['Model'],
});
expect(template).toEqual('<T>');
});
it('should return empty for primary type', () => {
const template: string = getModelTemplate({
type: 'string',
base: 'string',
template: null,
imports: [],
});
expect(template).toEqual('');
});
});

View File

@ -0,0 +1,11 @@
import { Type } from '../../../client/interfaces/Type';
/**
* If our model has a template type, then we want to generalize that!
* In that case we should return "<T>" as our template type.
* @param modelClass The parsed model class type.
* @returns The model template type (<T> or empty).
*/
export function getModelTemplate(modelClass: Type): string {
return modelClass.template !== null ? '<T>' : '';
}

View File

@ -0,0 +1,36 @@
import { Model } from '../../../client/interfaces/Model';
import { OpenApi } from '../interfaces/OpenApi';
import { getType } from './getType';
import { getModelTemplate } from './getModelTemplate';
/**
* Parse and return the OpenAPI models.
* @param openApi
*/
export function getModels(openApi: OpenApi): Map<string, Model> {
const models = new Map<string, Model>();
const definitions = openApi.definitions;
for (const definitionName in definitions) {
if (definitions.hasOwnProperty(definitionName)) {
const definition = definitions[definitionName];
const required = definition.required || [];
const modelClass = getType(definitionName);
const modelTemplate: string = getModelTemplate(modelClass);
if (!models.has(modelClass.base)) {
const model: Model = {
name: modelClass.base,
base: modelClass.base,
type: modelClass.type,
template: modelTemplate,
description: null,
extends: null,
imports: [],
properties: [],
enums: [],
};
models.set(modelClass.base, model);
}
}
}
return models;
}

View File

@ -0,0 +1,11 @@
import { Schema } from '../../../client/interfaces/Schema';
import { OpenApi } from '../interfaces/OpenApi';
/**
* Parse and return the OpenAPI schemas.
* @param openApi
*/
export function getSchemas(openApi: OpenApi): Map<string, Schema> {
const schemas = new Map<string, Schema>();
return schemas;
}

View File

@ -0,0 +1,13 @@
import { getServer } from './getServer';
describe('getServer', () => {
it('should produce correct result', () => {
expect(
getServer({
host: 'localhost:8080',
basePath: '/api',
schemes: ['http', 'https'],
})
).toEqual('http://localhost:8080/api');
});
});

View File

@ -0,0 +1,10 @@
import { OpenApi } from '../interfaces/OpenApi';
type Props = Pick<OpenApi, 'schemes' | 'host' | 'basePath'>;
export function getServer(openApi: Props): string {
const scheme = (openApi.schemes && openApi.schemes[0]) || 'http';
const host = openApi.host;
const basePath = openApi.basePath || '';
return host ? `${scheme}://${host}${basePath}` : basePath;
}

View File

@ -0,0 +1,13 @@
import { getServiceClassName } from './getServiceClassName';
describe('getServiceClassName', () => {
it('should produce correct result', () => {
expect(getServiceClassName('')).toEqual('');
expect(getServiceClassName('FooBar')).toEqual('FooBarService');
expect(getServiceClassName('Foo Bar')).toEqual('FooBarService');
expect(getServiceClassName('foo bar')).toEqual('FooBarService');
expect(getServiceClassName('FooBarService')).toEqual('FooBarService');
expect(getServiceClassName('Foo Bar Service')).toEqual('FooBarService');
expect(getServiceClassName('foo bar service')).toEqual('FooBarService');
});
});

View File

@ -0,0 +1,14 @@
import * as camelcase from 'camelcase';
/**
* Convert the input value to a correct service classname. This converts
* the input string to PascalCase and appends the "Service" prefix if needed.
* @param value
*/
export function getServiceClassName(value: string): string {
const name = camelcase(value, { pascalCase: true });
if (name && !name.endsWith('Service')) {
return `${name}Service`;
}
return name;
}

View File

@ -0,0 +1,10 @@
import { getServiceOperationName } from './getServiceOperationName';
describe('getServiceOperationName', () => {
it('should produce correct result', () => {
expect(getServiceOperationName('')).toEqual('');
expect(getServiceOperationName('FooBar')).toEqual('fooBar');
expect(getServiceOperationName('Foo Bar')).toEqual('fooBar');
expect(getServiceOperationName('foo bar')).toEqual('fooBar');
});
});

View File

@ -0,0 +1,11 @@
import * as camelcase from 'camelcase';
/**
* Convert the input value to a correct operation (method) classname. This converts
* the input string to cascalCase, so the method name follows the most popular
* Javascript and Typescript writing style.
* @param value
*/
export function getServiceOperationName(value: string): string {
return camelcase(value);
}

View File

@ -0,0 +1,10 @@
import { getServicePath } from './getServicePath';
describe('getServicePath', () => {
it('should produce correct result', () => {
expect(getServicePath('/api/v{api-version}/list/{id}/{type}')).toEqual('/api/v${OpenAPI.VERSION}/list/${id}/${type}');
expect(getServicePath('/api/v{api-version}/list/{id}')).toEqual('/api/v${OpenAPI.VERSION}/list/${id}');
expect(getServicePath('/api/v1/list/{id}')).toEqual('/api/v1/list/${id}');
expect(getServicePath('/api/v1/list')).toEqual('/api/v1/list');
});
});

View File

@ -0,0 +1,9 @@
/**
* Get the final service path, this replaces the "{api-version}" placeholder
* with a new template string placeholder so we can dynamically inject the
* OpenAPI version without the need to hardcode this in the URL.
* @param path
*/
export function getServicePath(path: string): string {
return path.replace(/{api-version}/g, '{OpenAPI.VERSION}').replace(/\{(.*?)\}/g, '${$1}');
}

View File

@ -0,0 +1,9 @@
import { getServiceVersion } from './getServiceVersion';
describe('getServiceVersion', () => {
it('should produce correct result', () => {
expect(getServiceVersion('1.0')).toEqual('1.0');
expect(getServiceVersion('v1.0')).toEqual('1.0');
expect(getServiceVersion('V1.0')).toEqual('1.0');
});
});

View File

@ -0,0 +1,8 @@
/**
* Convert the service version to 'normal' version.
* This basically removes any "v" prefix from the version string.
* @param version
*/
export function getServiceVersion(version = '1.0'): string {
return version.replace(/^v/gi, '');
}

View File

@ -0,0 +1,35 @@
import { Service } from '../../../client/interfaces/Service';
import { OpenApi } from '../interfaces/OpenApi';
/**
* Parse and return the OpenAPI services.
* @param openApi
*/
export function getServices(openApi: OpenApi): Map<string, Service> {
const services = new Map<string, Service>();
const paths = openApi.paths;
for (const url in paths) {
if (paths.hasOwnProperty(url)) {
const path = paths[url];
for (const method in path) {
if (path.hasOwnProperty(method)) {
switch (method) {
case 'get':
case 'put':
case 'post':
case 'delete':
case 'options':
case 'head':
case 'patch':
const op = path[method];
if (op) {
//
}
break;
}
}
}
}
}
return services;
}

View File

@ -0,0 +1,51 @@
import { getType } from './getType';
describe('getType', () => {
it('should convert int', () => {
const type = getType('int', null);
expect(type.type).toEqual('number');
expect(type.base).toEqual('number');
expect(type.template).toEqual(null);
expect(type.imports).toEqual([]);
});
it('should convert string', () => {
const type = getType('String', null);
expect(type.type).toEqual('string');
expect(type.base).toEqual('string');
expect(type.template).toEqual(null);
expect(type.imports).toEqual([]);
});
it('should convert string array', () => {
const type = getType('Array[String]', null);
expect(type.type).toEqual('string[]');
expect(type.base).toEqual('string');
expect(type.template).toEqual(null);
expect(type.imports).toEqual([]);
});
it('should convert template with primary', () => {
const type = getType('#/definitions/Link[String]', null);
expect(type.type).toEqual('Link<string>');
expect(type.base).toEqual('Link');
expect(type.template).toEqual('string');
expect(type.imports).toEqual(['Link']);
});
it('should convert template with model', () => {
const type = getType('#/definitions/Link[Model]', null);
expect(type.type).toEqual('Link<Model>');
expect(type.base).toEqual('Link');
expect(type.template).toEqual('Model');
expect(type.imports).toEqual(['Link', 'Model']);
});
it('should convert generic', () => {
const type = getType('#/definitions/Link', 'Link');
expect(type.type).toEqual('T');
expect(type.base).toEqual('T');
expect(type.template).toEqual(null);
expect(type.imports).toEqual([]);
});
});

View File

@ -0,0 +1,74 @@
import { stripNamespace } from './stripNamespace';
import { Type } from '../../../client/interfaces/Type';
import { getMappedType, hasMappedType } from './getMappedType';
/**
* Parse any value into a type object.
* @param value String value like "integer" or "Link[Model]".
* @param template Optional template class from parent (needed to process generics)
*/
export function getType(value: string, template: string | null = null): Type {
let propertyType = 'any';
let propertyBase = 'any';
let propertyTemplate: string | null = null;
let propertyImports: string[] = [];
// Remove definitions prefix and cleanup string.
const valueTrimmed = stripNamespace(value || '');
// Check of we have an Array type or generic type, for instance: "Link[Model]".
if (/\[.*\]$/g.test(valueTrimmed)) {
// Find the first and second type
const match = valueTrimmed.match(/(.*?)\[(.*)\]$/);
if (match) {
// Both of the types can be complex types so parse each of them.
const match1 = getType(match[1]);
const match2 = getType(match[2]);
// If the first match is a generic array then construct a correct array type,
// for example the type "Array[Model]" becomes "Model[]".
if (match1.type === 'any[]') {
propertyType = `${match2.type}[]`;
propertyBase = `${match2.type}`;
match1.imports = [];
} else if (match2.type === '') {
// Primary types like number[] or string[]
propertyType = match1.type;
propertyBase = match1.type;
propertyTemplate = match1.type;
match2.imports = [];
} else {
propertyType = `${match1.type}<${match2.type}>`;
propertyBase = match1.type;
propertyTemplate = match2.type;
}
// Either way we need to add the found imports
propertyImports.push(...match1.imports);
propertyImports.push(...match2.imports);
}
} else if (hasMappedType(valueTrimmed)) {
const mapped = getMappedType(valueTrimmed);
propertyType = mapped;
propertyBase = mapped;
} else {
propertyType = valueTrimmed;
propertyBase = valueTrimmed;
propertyImports.push(valueTrimmed);
}
// If the property that we found matched the parent template class
// Then ignore this whole property and return it as a "T" template property.
if (propertyType === template) {
propertyType = 'T'; // Template;
propertyBase = 'T'; // Template;
propertyImports = [];
}
return {
type: propertyType,
base: propertyBase,
template: propertyTemplate,
imports: propertyImports,
};
}

View File

@ -0,0 +1,15 @@
import { isPrimaryType } from './isPrimaryType';
describe('isPrimaryType', () => {
it('should return true for primary types', () => {
expect(isPrimaryType('number')).toBeTruthy();
expect(isPrimaryType('boolean')).toBeTruthy();
expect(isPrimaryType('string')).toBeTruthy();
expect(isPrimaryType('any')).toBeTruthy();
expect(isPrimaryType('object')).toBeTruthy();
expect(isPrimaryType('void')).toBeTruthy();
expect(isPrimaryType('null')).toBeTruthy();
expect(isPrimaryType('Array')).toBeFalsy();
expect(isPrimaryType('MyModel')).toBeFalsy();
});
});

View File

@ -0,0 +1,17 @@
/**
* Check if given type is a primary type.
* @param type
*/
export function isPrimaryType(type: string): boolean {
switch (type.toLowerCase()) {
case 'number':
case 'boolean':
case 'string':
case 'object':
case 'any':
case 'void':
case 'null':
return true;
}
return false;
}

View File

@ -0,0 +1,14 @@
import { stripNamespace } from './stripNamespace';
describe('stripNamespace', () => {
it('should strip namespace', () => {
expect(stripNamespace('#/definitions/Item')).toEqual('Item');
expect(stripNamespace('#/parameters/Item')).toEqual('Item');
expect(stripNamespace('#/responses/Item')).toEqual('Item');
expect(stripNamespace('#/securityDefinitions/Item')).toEqual('Item');
expect(stripNamespace('Template[Model]')).toEqual('Template[Model]');
expect(stripNamespace('namespace.Template[Model]')).toEqual('Template[Model]');
expect(stripNamespace('namespace.Template[namespace.Model]')).toEqual('Template[Model]');
expect(stripNamespace('Item')).toEqual('Item');
});
});

View File

@ -0,0 +1,31 @@
/**
* Strip (OpenAPI) namespaces fom values.
* @param value
*/
export function stripNamespace(value: string): string {
return (
value
.trim()
.replace(/^#\/definitions\//, '')
.replace(/^#\/parameters\//, '')
.replace(/^#\/responses\//, '')
.replace(/^#\/securityDefinitions\//, '')
// First we remove the namespace from template notation:
// Example: namespace.Template[namespace.Model] -> namespace.Template[Model]
.replace(/(\[.*\]$)/, (s: string): string => {
const v = s
.replace('[', '')
.replace(']', '')
.split('.')
.pop()!;
return `[${v}]`;
})
// Then we remove the namespace from the complete result:
// Example: namespace.Template[Model] -> Template[Model]
.replace(/.*/, (s: string): string => {
return s.split('.').pop()!;
})
);
}

16
src/openApi/v3/index.ts Normal file
View File

@ -0,0 +1,16 @@
import { OpenApi } from './interfaces/OpenApi';
import { Client } from '../../client/interfaces/Client';
import { getServer } from './parser/getServer';
import { getModels } from './parser/getModels';
import { getServices } from './parser/getServices';
import { getSchemas } from './parser/getSchemas';
export function parse(openApi: OpenApi): Client {
return {
version: openApi.info.version,
server: getServer(openApi),
models: getModels(openApi),
schemas: getSchemas(openApi),
services: getServices(openApi),
};
}

21
src/openApi/v3/interfaces/OpenApi.d.ts vendored Normal file
View File

@ -0,0 +1,21 @@
import { OpenApiComponents } from './OpenApiComponents';
import { OpenApiExternalDocs } from './OpenApiExternalDocs';
import { OpenApiInfo } from './OpenApiInfo';
import { OpenApiPaths } from './OpenApiPaths';
import { OpenApiSecurityRequirement } from './OpenApiSecurityRequirement';
import { OpenApiServer } from './OpenApiServer';
import { OpenApiTag } from './OpenApiTag';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md
*/
export interface OpenApi {
openapi: string;
info: OpenApiInfo;
servers?: OpenApiServer[];
paths: OpenApiPaths;
components?: OpenApiComponents;
security?: OpenApiSecurityRequirement[];
tags?: OpenApiTag[];
externalDocs?: OpenApiExternalDocs;
}

View File

@ -0,0 +1,8 @@
import { OpenApiPath } from './OpenApiPath';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#callbackObject
*/
export interface OpenApiCallback {
[key: string]: OpenApiPath;
}

View File

@ -0,0 +1,26 @@
import { Dictionary } from '../../../utils/types';
import { OpenApiCallback } from './OpenApiCallback';
import { OpenApiExample } from './OpenApiExample';
import { OpenApiHeader } from './OpenApiHeader';
import { OpenApiLink } from './OpenApiLink';
import { OpenApiParameter } from './OpenApiParameter';
import { OpenApiReference } from './OpenApiReference';
import { OpenApiRequestBody } from './OpenApiRequestBody';
import { OpenApiResponses } from './OpenApiResponses';
import { OpenApiSchema } from './OpenApiSchema';
import { OpenApiSecurityScheme } from './OpenApiSecurityScheme';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#componentsObject
*/
export interface OpenApiComponents {
schemas?: Dictionary<OpenApiSchema | OpenApiReference>;
responses?: Dictionary<OpenApiResponses | OpenApiReference>;
parameters?: Dictionary<OpenApiParameter | OpenApiReference>;
examples?: Dictionary<OpenApiExample | OpenApiReference>;
requestBodies?: Dictionary<OpenApiRequestBody | OpenApiReference>;
headers?: Dictionary<OpenApiHeader | OpenApiReference>;
securitySchemes: Dictionary<OpenApiSecurityScheme | OpenApiReference>;
links?: Dictionary<OpenApiLink | OpenApiReference>;
callbacks?: Dictionary<OpenApiCallback | OpenApiReference>;
}

View File

@ -0,0 +1,8 @@
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#contactObject
*/
export interface OpenApiContact {
name?: string;
url?: string;
email?: string;
}

View File

@ -0,0 +1,9 @@
import { Dictionary } from '../../../utils/types';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#discriminatorObject
*/
export interface OpenApiDiscriminator {
propertyName: string;
mapping?: Dictionary<string>;
}

View File

@ -0,0 +1,14 @@
import { Dictionary } from '../../../utils/types';
import { OpenApiHeader } from './OpenApiHeader';
import { OpenApiReference } from './OpenApiReference';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#encodingObject
*/
export interface OpenApiEncoding {
contentType?: string;
headers?: Dictionary<OpenApiHeader | OpenApiReference>;
style?: string;
explode?: boolean;
allowReserved?: boolean;
}

View File

@ -0,0 +1,9 @@
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#exampleObject
*/
export interface OpenApiExample {
summary?: string;
description?: string;
value?: any;
externalValue?: string;
}

View File

@ -0,0 +1,7 @@
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#externalDocumentationObject
*/
export interface OpenApiExternalDocs {
description?: string;
url: string;
}

View File

@ -0,0 +1,20 @@
import { Dictionary } from '../../../utils/types';
import { OpenApiExample } from './OpenApiExample';
import { OpenApiReference } from './OpenApiReference';
import { OpenApiSchema } from './OpenApiSchema';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#headerObject
*/
export interface OpenApiHeader {
description?: string;
required?: boolean;
deprecated?: boolean;
allowEmptyValue?: boolean;
style?: string;
explode?: boolean;
allowReserved?: boolean;
schema?: OpenApiSchema | OpenApiReference;
example?: any;
examples?: Dictionary<OpenApiExample | OpenApiReference>;
}

View File

@ -0,0 +1,14 @@
import { OpenApiContact } from './OpenApiContact';
import { OpenApiLicense } from './OpenApiLicense';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#infoObject
*/
export interface OpenApiInfo {
title: string;
description?: string;
termsOfService?: string;
contact?: OpenApiContact;
license?: OpenApiLicense;
version: string;
}

View File

@ -0,0 +1,7 @@
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#licenseObject
*/
export interface OpenApiLicense {
name: string;
url?: string;
}

View File

@ -0,0 +1,14 @@
import { Dictionary } from '../../../utils/types';
import { OpenApiServer } from './OpenApiServer';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#linkObject
*/
export interface OpenApiLink {
operationRef?: string;
operationId?: string;
parameters?: Dictionary<any>;
requestBody?: any;
description?: string;
server?: OpenApiServer;
}

View File

@ -0,0 +1,15 @@
import { Dictionary } from '../../../utils/types';
import { OpenApiEncoding } from './OpenApiEncoding';
import { OpenApiExample } from './OpenApiExample';
import { OpenApiReference } from './OpenApiReference';
import { OpenApiSchema } from './OpenApiSchema';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#mediaTypeObject
*/
export interface OpenApiMediaType {
schema?: OpenApiSchema | OpenApiReference;
example?: any;
examples?: Dictionary<OpenApiExample | OpenApiReference>;
encoding?: Dictionary<OpenApiEncoding>;
}

View File

@ -0,0 +1,11 @@
import { Dictionary } from '../../../utils/types';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#oauthFlowObject
*/
export interface OpenApiOAuthFlow {
authorizationUrl: string;
tokenUrl: string;
refreshUrl?: string;
scopes: Dictionary<string>;
}

View File

@ -0,0 +1,11 @@
import { OpenApiOAuthFlow } from './OpenApiOAuthFlow';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#oauthFlowsObject
*/
export interface OpenApiOAuthFlows {
implicit?: OpenApiOAuthFlow;
password?: OpenApiOAuthFlow;
clientCredentials?: OpenApiOAuthFlow;
authorizationCode?: OpenApiOAuthFlow;
}

View File

@ -0,0 +1,27 @@
import { Dictionary } from '../../../utils/types';
import { OpenApiCallback } from './OpenApiCallback';
import { OpenApiExternalDocs } from './OpenApiExternalDocs';
import { OpenApiParameter } from './OpenApiParameter';
import { OpenApiReference } from './OpenApiReference';
import { OpenApiRequestBody } from './OpenApiRequestBody';
import { OpenApiResponses } from './OpenApiResponses';
import { OpenApiSecurityRequirement } from './OpenApiSecurityRequirement';
import { OpenApiServer } from './OpenApiServer';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operationObject
*/
export interface OpenApiOperation {
tags?: string[];
summary?: string;
description?: string;
externalDocs?: OpenApiExternalDocs;
operationId?: string;
parameters?: OpenApiParameter[] | OpenApiReference[];
requestBody?: OpenApiRequestBody | OpenApiReference;
responses: OpenApiResponses;
callbacks?: Dictionary<OpenApiCallback | OpenApiReference>;
deprecated?: boolean;
security?: OpenApiSecurityRequirement[];
servers?: OpenApiServer[];
}

View File

@ -0,0 +1,22 @@
import { Dictionary } from '../../../utils/types';
import { OpenApiExample } from './OpenApiExample';
import { OpenApiReference } from './OpenApiReference';
import { OpenApiSchema } from './OpenApiSchema';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#parameterObject
*/
export interface OpenApiParameter {
name: string;
in: 'path' | 'query' | 'header' | 'cookie';
description?: string;
required: boolean;
deprecated?: boolean;
allowEmptyValue?: boolean;
style?: string;
explode?: boolean;
allowReserved?: boolean;
schema?: OpenApiSchema | OpenApiReference;
example?: any;
examples?: Dictionary<OpenApiExample | OpenApiReference>;
}

View File

@ -0,0 +1,23 @@
import { OpenApiOperation } from './OpenApiOperation';
import { OpenApiParameter } from './OpenApiParameter';
import { OpenApiReference } from './OpenApiReference';
import { OpenApiServer } from './OpenApiServer';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#pathItemObject
*/
export interface OpenApiPath {
$ref?: string;
summary?: string;
description?: string;
get?: OpenApiOperation;
put?: OpenApiOperation;
post?: OpenApiOperation;
delete?: OpenApiOperation;
options?: OpenApiOperation;
head?: OpenApiOperation;
patch?: OpenApiOperation;
trace?: OpenApiOperation;
servers?: OpenApiServer[];
parameters?: OpenApiParameter[] | OpenApiReference[];
}

View File

@ -0,0 +1,8 @@
import { OpenApiPath } from './OpenApiPath';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#pathsObject
*/
export interface OpenApiPaths {
[path: string]: OpenApiPath;
}

View File

@ -0,0 +1,6 @@
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#referenceObject
*/
export interface OpenApiReference {
$ref: string;
}

View File

@ -0,0 +1,12 @@
import { Dictionary } from '../../../utils/types';
import { OpenApiMediaType } from './OpenApiMediaType';
import { OpenApiReference } from './OpenApiReference';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#requestBodyObject
*/
export interface OpenApiRequestBody {
description?: string;
content: Dictionary<OpenApiMediaType | OpenApiReference>;
required?: boolean;
}

View File

@ -0,0 +1,15 @@
import { Dictionary } from '../../../utils/types';
import { OpenApiHeader } from './OpenApiHeader';
import { OpenApiLink } from './OpenApiLink';
import { OpenApiMediaType } from './OpenApiMediaType';
import { OpenApiReference } from './OpenApiReference';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responseObject
*/
export interface OpenApiResponse {
description: string;
headers?: Dictionary<OpenApiHeader | OpenApiReference>;
content?: Dictionary<OpenApiMediaType>;
links?: Dictionary<OpenApiLink | OpenApiReference>;
}

View File

@ -0,0 +1,11 @@
import { OpenApiReference } from './OpenApiReference';
import { OpenApiResponse } from './OpenApiResponse';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responsesObject
*/
export interface OpenApiResponses {
[httpcode: string]: OpenApiResponse | OpenApiReference;
default: OpenApiResponse | OpenApiReference;
}

View File

@ -0,0 +1,46 @@
import { Dictionary } from '../../../utils/types';
import { OpenApiDiscriminator } from './OpenApiDiscriminator';
import { OpenApiExternalDocs } from './OpenApiExternalDocs';
import { OpenApiReference } from './OpenApiReference';
import { OpenApiXml } from './OpenApiXml';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#schemaObject
*/
export interface OpenApiSchema {
title?: string;
multipleOf?: number;
maximum?: number;
exclusiveMaximum?: boolean;
minimum?: number;
exclusiveMinimum?: boolean;
maxLength?: number;
minLength?: number;
pattern?: string;
maxItems?: number;
minItems?: number;
uniqueItems?: number;
maxProperties?: number;
minProperties?: number;
required?: string[];
enum?: string[] | number[];
type?: string;
allOf?: OpenApiReference[] | OpenApiSchema[];
oneOf?: OpenApiReference[] | OpenApiSchema[];
anyOf?: OpenApiReference[] | OpenApiSchema[];
not?: OpenApiReference[] | OpenApiSchema[];
items?: OpenApiReference | OpenApiSchema;
properties?: Dictionary<OpenApiSchema>;
additionalProperties?: boolean | OpenApiReference | OpenApiSchema;
description?: string;
format?: 'int32' | 'int64' | 'float' | 'double' | 'string' | 'boolean' | 'byte' | 'binary' | 'date' | 'date-time' | 'password';
default?: any;
nullable?: boolean;
discriminator?: OpenApiDiscriminator;
readOnly?: boolean;
writeOnly?: boolean;
xml?: OpenApiXml;
externalDocs?: OpenApiExternalDocs;
example?: any;
deprecated?: boolean;
}

View File

@ -0,0 +1,6 @@
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#securityRequirementObject
*/
export interface OpenApiSecurityRequirement {
[name: string]: string;
}

View File

@ -0,0 +1,15 @@
import { OpenApiOAuthFlows } from './OpenApiOAuthFlows';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#securitySchemeObject
*/
export interface OpenApiSecurityScheme {
type: 'apiKey' | 'http' | 'oauth2' | 'openIdConnect';
description?: string;
name?: string;
in?: 'query' | 'header' | 'cookie';
scheme?: string;
bearerFormat?: string;
flows?: OpenApiOAuthFlows;
openIdConnectUrl?: string;
}

View File

@ -0,0 +1,11 @@
import { Dictionary } from '../../../utils/types';
import { OpenApiServerVariable } from './OpenApiServerVariable';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#serverObject
*/
export interface OpenApiServer {
url: string;
description?: string;
variables?: Dictionary<OpenApiServerVariable>;
}

View File

@ -0,0 +1,8 @@
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#serverVariableObject
*/
export interface OpenApiServerVariable {
enum?: string[];
default: string;
description?: string;
}

View File

@ -0,0 +1,10 @@
import { OpenApiExternalDocs } from './OpenApiExternalDocs';
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#tagObject
*/
export interface OpenApiTag {
name: string;
description?: string;
externalDocs?: OpenApiExternalDocs;
}

View File

@ -0,0 +1,10 @@
/**
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#xmlObject
*/
export interface OpenApiXml {
name?: string;
namespace?: string;
prefix?: string;
attribute?: boolean;
wrapped?: boolean;
}

View File

@ -0,0 +1,23 @@
import { getMappedType } from './getMappedType';
describe('getMappedType', () => {
it('should map types to the basics', () => {
expect(getMappedType('File')).toEqual('File');
expect(getMappedType('Array')).toEqual('any[]');
expect(getMappedType('List')).toEqual('any[]');
expect(getMappedType('String')).toEqual('string');
expect(getMappedType('date')).toEqual('string');
expect(getMappedType('date-time')).toEqual('string');
expect(getMappedType('float')).toEqual('number');
expect(getMappedType('double')).toEqual('number');
expect(getMappedType('short')).toEqual('number');
expect(getMappedType('int')).toEqual('number');
expect(getMappedType('boolean')).toEqual('boolean');
expect(getMappedType('any')).toEqual('any');
expect(getMappedType('object')).toEqual('any');
expect(getMappedType('void')).toEqual('void');
expect(getMappedType('null')).toEqual('null');
expect(getMappedType('Unknown')).toEqual('Unknown');
expect(getMappedType('')).toEqual('');
});
});

View File

@ -0,0 +1,42 @@
const MAPPINGS = new Map<string, string>([
['file', 'File'],
['binary', 'File'],
['array', 'any[]'],
['list', 'any[]'],
['object', 'any'],
['any', 'any'],
['boolean', 'boolean'],
['byte', 'number'],
['int', 'number'],
['int32', 'number'],
['int64', 'number'],
['integer', 'number'],
['float', 'number'],
['double', 'number'],
['short', 'number'],
['long', 'number'],
['number', 'number'],
['char', 'string'],
['date', 'string'],
['date-time', 'string'],
['password', 'string'],
['string', 'string'],
['void', 'void'],
['null', 'null'],
]);
/**
* Get mapped type for given type to any basic Typescript/Javascript type.
* @param type
*/
export function getMappedType(type: string): string {
const mapped = MAPPINGS.get(type.toLowerCase());
if (mapped) {
return mapped;
}
return type;
}
export function hasMappedType(type: string): boolean {
return MAPPINGS.has(type.toLowerCase());
}

View File

@ -0,0 +1,11 @@
import { Model } from '../../../client/interfaces/Model';
import { OpenApi } from '../interfaces/OpenApi';
/**
* Parse and return the OpenAPI models.
* @param openApi
*/
export function getModels(openApi: OpenApi): Map<string, Model> {
const models = new Map<string, Model>();
return models;
}

View File

@ -0,0 +1,11 @@
import { Schema } from '../../../client/interfaces/Schema';
import { OpenApi } from '../interfaces/OpenApi';
/**
* Parse and return the OpenAPI schemas.
* @param openApi
*/
export function getSchemas(openApi: OpenApi): Map<string, Schema> {
const schemas = new Map<string, Schema>();
return schemas;
}

Some files were not shown because too many files have changed in this diff Show More