Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Add class-transformer to transform results and support dates #42

Merged
merged 6 commits into from
Sep 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/baseInterfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@ export interface ISwaggerOptions {
strictNullChecks?: boolean | undefined
/** definition Class mode */
modelMode?: 'class' | 'interface'
/** use class-transformer to transform the results */
useClassTransformer?: boolean
}

export interface IPropDef {
name: string
type: string
format?: string
desc: string
isType: boolean
isEnum: boolean
}

export interface IInclude {
Expand Down
2 changes: 1 addition & 1 deletion src/definitionCodegen/createDefinitionClass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export function createDefinitionClass(
model.imports.push(ref)
}
// propsStr += classPropsTemplate(k, propType, v.description)
model.props.push({ name: k, type: propType, desc: v.description })
model.props.push({ name: k, type: propType, format: v.format, desc: v.description, isType, isEnum })
}
// : classTemplate(className, propsStr, constructorStr)
return { enums, model }
Expand Down
13 changes: 7 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ const defaultOptions: ISwaggerOptions = {
useCustomerRequestInstance: false,
modelMode: 'interface',
include: [],
strictNullChecks: true
strictNullChecks: true,
useClassTransformer: false,
}


Expand Down Expand Up @@ -48,8 +49,8 @@ export async function codegen(params: ISwaggerOptions) {
...params
}
let apiSource = options.useCustomerRequestInstance
? customerServiceHeader
: serviceHeader
? customerServiceHeader(options)
: serviceHeader(options)
// TODO: next next next time
// if (options.multipleFileMode) {
if (false) {
Expand Down Expand Up @@ -83,7 +84,7 @@ export async function codegen(params: ISwaggerOptions) {
Object.values(models).forEach(item => {
const text = params.modelMode === 'interface'
? interfaceTemplate(item.value.name, item.value.props, [], params.strictNullChecks)
: classTemplate(item.value.name, item.value.props, [], params.strictNullChecks)
: classTemplate(item.value.name, item.value.props, [], params.strictNullChecks, options.useClassTransformer)
// const fileDir = path.join(options.outputDir || '', 'definitions')
// writeFile(fileDir, item.name + '.ts', format(text, options))
defsString += text
Expand Down Expand Up @@ -143,7 +144,7 @@ export async function codegen(params: ISwaggerOptions) {
if (allImport.includes(item.name)) {
const text = params.modelMode === 'interface'
? interfaceTemplate(item.value.name, item.value.props, [], params.strictNullChecks)
: classTemplate(item.value.name, item.value.props, [], params.strictNullChecks)
: classTemplate(item.value.name, item.value.props, [], params.strictNullChecks, options.useClassTransformer)
defSource += text
}
})
Expand Down Expand Up @@ -181,7 +182,7 @@ export async function codegen(params: ISwaggerOptions) {
Object.values(models).forEach(item => {
const text = params.modelMode === 'interface'
? interfaceTemplate(item.value.name, item.value.props, [], params.strictNullChecks)
: classTemplate(item.value.name, item.value.props, [], params.strictNullChecks)
: classTemplate(item.value.name, item.value.props, [], params.strictNullChecks, options.useClassTransformer)
apiSource += text
})

Expand Down
5 changes: 3 additions & 2 deletions src/requestCodegen/getResponseType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ export function getResponseType(reqProps: IRequestMethod): { responseType: strin
}

let checkType = resSchema.type
let format = resSchema.format;
// 如果是数组
if (checkType === 'array' || resSchema.items) {
if (resSchema.items.$ref) {
const refType = refClassName(resSchema.items.$ref)
isRef = true
result = refType + '[]'
} else {
const refType = toBaseType(resSchema.type)
const refType = toBaseType(resSchema.items.type, resSchema.items.format)
result = refType + '[]'
}
} else if (resSchema.$ref) {
Expand All @@ -38,7 +39,7 @@ export function getResponseType(reqProps: IRequestMethod): { responseType: strin
isRef = true
} else {
result = checkType
result = toBaseType(result)
result = toBaseType(result, format)
}

if (result == 'object') {
Expand Down
2 changes: 2 additions & 0 deletions src/swaggerInterfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface IRequestMethod {
'$ref': string,
'type'?: string,
'items'?: IParameterItems,
'format'?: string,
}
}
}
Expand Down Expand Up @@ -60,6 +61,7 @@ export interface IParameterSchema {

export interface IParameterItems {
type?: string
format?: string
$ref: string
items?: IParameterItems
}
Expand Down
197 changes: 116 additions & 81 deletions src/template.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import camelcase from 'camelcase'
import { IPropDef } from "./baseInterfaces";
import { IPropDef, ISwaggerOptions } from "./baseInterfaces";
import { toBaseType } from './utils';

const baseTypes = ['string', 'number', 'object', 'boolean', 'any'];

/** 类模板 */
export function interfaceTemplate(name: string, props: IPropDef[], imports: string[], strictNullChecks: boolean = true) {
Expand All @@ -13,24 +16,29 @@ export function interfaceTemplate(name: string, props: IPropDef[], imports: stri

export interface ${name} {

${props.map(p => classPropsTemplate(p.name, p.type, p.desc, !strictNullChecks)).join('')}
${props.map(p => classPropsTemplate(p.name, p.type, null, p.desc, !strictNullChecks, false, false)).join('')}
}
`
}

/** 类模板 */
export function classTemplate(name: string, props: IPropDef[], imports: string[], strictNullChecks: boolean = true) {
export function classTemplate(name: string, props: IPropDef[], imports: string[], strictNullChecks: boolean = true, useClassTransformer: boolean) {
// 所有的引用
const importString = imports.map(imp => {
const mappedImports = imports.map(imp => {
return `import { ${imp} } from '../definitions/${imp}'\n`
}).join('')
})

if (useClassTransformer && imports.length > 0) {
mappedImports.push(`import { Type, Transform, Expose } from 'class-transformer'\n`)
}
const importString = mappedImports.join('');

return `
${importString}

export class ${name} {

${props.map(p => classPropsTemplate(p.name, p.type, p.desc, !strictNullChecks)).join('')}
${props.map(p => classPropsTemplate(p.name, p.type, p.format, p.desc, !strictNullChecks, useClassTransformer, p.isEnum || p.isType)).join('')}

constructor(data: (undefined | any) = {}){
${props.map(p => classConstructorTemplate(p.name)).join('')}
Expand All @@ -40,19 +48,32 @@ export function classTemplate(name: string, props: IPropDef[], imports: string[]
}

/** 类属性模板 */
export function classPropsTemplate(filedName: string, type: string, description: string, canNull: boolean) {
export function classPropsTemplate(filedName: string, type: string, format: string, description: string, canNull: boolean, useClassTransformer: boolean, isType: boolean) {
/**
* eg:
* //description
* fieldName: type
*/
type = toBaseType(type, format);
const decorators = useClassTransformer ? classTransformTemplate(type, format, isType) : '';

return `
/** ${description || ''} */
${decorators}
${filedName}${canNull ? '?' : ''}:${type};
`
}

export function classTransformTemplate(type: string, format: string, isType: boolean) {
const decorators: string[] = [`@Expose()`];
const nonArrayType = type.replace('[', '').replace(']', '');
/* ignore interfaces */
if (baseTypes.indexOf(nonArrayType) < 0 && !isType) {
decorators.push(`@Type(() => ${nonArrayType})`);
}
return decorators.join('\n');
}

/** 类属性模板 */
export function classConstructorTemplate(name: string) {
return `this['${name}'] = data['${name}'];\n`
Expand Down Expand Up @@ -92,9 +113,12 @@ export function requestTemplate(name: string, requestSchema: IRequestSchema, opt
parsedParameters = <any>{},
formData = ''
} = requestSchema

const { useClassTransformer } = options;
const { queryParameters = [], bodyParameters = [] } = parsedParameters

const nonArrayType = responseType.replace('[', '').replace(']', '');
const isArrayType = responseType.indexOf('[') > 0;
const transform = useClassTransformer && baseTypes.indexOf(nonArrayType) < 0;
const resolveString = transform ? `(response: any${isArrayType ? '[]' : ''}) => resolve(plainToClass(${nonArrayType}, response, {strategy: 'excludeAll'}))` : 'resolve';
return `
/**
* ${summary || ''}
Expand All @@ -114,7 +138,7 @@ ${options.useStaticMethod ? 'static' : ''} ${camelcase(name)}(${parameters}optio
}
${contentType === 'multipart/form-data' ? formData : ''}
configs.data = data;
axios(configs, resolve, reject)
axios(configs, ${resolveString}, reject);
});
}`;
}
Expand All @@ -128,91 +152,102 @@ export function serviceTemplate(name: string, body: string) {
`
}

export const serviceHeader = `/** Generate by swagger-axios-codegen */
export const serviceHeader = (options: ISwaggerOptions) => {
const classTransformerImport = options.useClassTransformer
? `import { Expose, Transform, Type, plainToClass } from 'class-transformer';
` : '';

import axiosStatic, { AxiosPromise, AxiosInstance } from 'axios';
export interface IRequestOptions {
headers?: any;
baseURL?: string;
responseType?: string;
}
return `/** Generate by swagger-axios-codegen */
import axiosStatic, { AxiosPromise, AxiosInstance } from 'axios';

interface IRequestConfig {
method?: any;
headers?: any;
url?: any;
data?: any;
params?: any;
}
${classTransformerImport}

// Add options interface
export interface ServiceOptions {
axios?: AxiosInstance,
}

// Add default options
export const serviceOptions: ServiceOptions = {
};
export interface IRequestOptions {
headers?: any;
baseURL?: string;
responseType?: string;
}

// Instance selector
function axios(configs: IRequestConfig, resolve: (p: any) => void, reject: (p: any) => void) {
const req = serviceOptions.axios ? serviceOptions.axios.request(configs) : axiosStatic(configs);
interface IRequestConfig {
method?: any;
headers?: any;
url?: any;
data?: any;
params?: any;
}

return req.then((res) => { resolve(res.data); }).catch(err => { reject(err); });
}
// Add options interface
export interface ServiceOptions {
axios?: AxiosInstance,
}

function getConfigs(method: string, contentType: string, url: string,options: any):IRequestConfig {
const configs: IRequestConfig = { ...options, method, url };
configs.headers = {
...options.headers,
'Content-Type': contentType,
// Add default options
export const serviceOptions: ServiceOptions = {
};
return configs
}
`

export const customerServiceHeader = `/** Generate by swagger-axios-codegen */
// Instance selector
function axios(configs: IRequestConfig, resolve: (p: any) => void, reject: (p: any) => void) {
const req = serviceOptions.axios ? serviceOptions.axios.request(configs) : axiosStatic(configs);

export interface IRequestOptions {
headers?: any;
return req.then((res) => { resolve(res.data); }).catch(err => { reject(err); });
}

function getConfigs(method: string, contentType: string, url: string,options: any):IRequestConfig {
const configs: IRequestConfig = { ...options, method, url };
configs.headers = {
...options.headers,
'Content-Type': contentType,
};
return configs
}
`
}

interface IRequestPromise<T=any> extends Promise<IRequestResponse<T>> {}
export const customerServiceHeader = (options: ISwaggerOptions) => {

interface IRequestResponse<T=any> {
data: T;
status: number;
statusText: string;
headers: any;
config: any;
request?: any;
}
return `/** Generate by swagger-axios-codegen */

interface IRequestInstance {
(config: any): IRequestPromise;
(url: string, config?: any): IRequestPromise;
request<T = any>(config: any): IRequestPromise<T>;
}
export interface IRequestOptions {
headers?: any;
}

interface IRequestConfig {
method?: any;
headers?: any;
url?: any;
data?: any;
params?: any;
}
interface IRequestPromise<T=any> extends Promise<IRequestResponse<T>> {}

// Add options interface
export interface ServiceOptions {
axios?: IRequestInstance,
}
interface IRequestResponse<T=any> {
data: T;
status: number;
statusText: string;
headers: any;
config: any;
request?: any;
}

// Add default options
export const serviceOptions: ServiceOptions = {
};
interface IRequestInstance {
(config: any): IRequestPromise;
(url: string, config?: any): IRequestPromise;
request<T = any>(config: any): IRequestPromise<T>;
}

// Instance selector
function axios(configs: IRequestConfig): IRequestPromise {
return serviceOptions.axios && serviceOptions.axios.request(configs);
}
`
interface IRequestConfig {
method?: any;
headers?: any;
url?: any;
data?: any;
params?: any;
}

// Add options interface
export interface ServiceOptions {
axios?: IRequestInstance,
}

// Add default options
export const serviceOptions: ServiceOptions = {
};

// Instance selector
function axios(configs: IRequestConfig): IRequestPromise {
return serviceOptions.axios && serviceOptions.axios.request(configs);
}
`
}
Loading