webapi-ng2
Version:
ASP.NET Core Web API client generator for Angular 2
343 lines • 27 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AngularGenerator = void 0;
var specification_1 = require("../../specification");
var AngularGenerator = /** @class */ (function () {
function AngularGenerator() {
}
AngularGenerator.prototype.generate = function (specification, config) {
// if (apiDescription.definitions != undefined) {
// this.mapDefinitions(apiDescription.definitions);
// }
var script = this.getHeader() + '\n\r' +
this.getImports() + '\n\r' +
this.getOptions(config) + '\n\r' +
this.getBaseClass(config) + '\n\r';
for (var _i = 0, _a = specification.controllers; _i < _a.length; _i++) {
var controller = _a[_i];
script += '\n\r' + this.getController(controller, config);
}
if (specification.schema != undefined) {
var enumTypes = [];
for (var schemaName in specification.schema) {
var schema = specification.schema[schemaName];
script += '\n\r' + this.getSchema(schema);
if (schema.type == specification_1.SchemaType.Enumeration) {
enumTypes.push(schema);
}
}
if (enumTypes.length > 0) {
script += '\n\r' + this.getEnumService(enumTypes);
}
}
return script;
};
AngularGenerator.prototype.getHeader = function () {
var result = "\n //\n // This file is autogenerated.\n // See http://github.com/bmitchenko/webapi-ng2 for details.\n //\n // tslint:disable:max-line-length\n ";
return result;
};
AngularGenerator.prototype.getImports = function () {
var result = "\n import { Injectable } from '@angular/core';\n import { HttpClient, HttpParams, HttpEvent, HttpResponse, HttpErrorResponse, HttpRequest, HttpHeaders } from '@angular/common/http';\n import { map, filter, catchError } from 'rxjs/operators';\n import { Observable, throwError } from 'rxjs';\n ";
return result;
};
AngularGenerator.prototype.getOptions = function (config) {
var result = "\n export class HttpError extends Error {\n detail?: string;\n error?: any;\n status: number;\n title?: string;\n traceId?: string;\n type?: string;\n url: string;\n \n constructor(message?: string) {\n super(message);\n }\n }\n\n @Injectable()\n export class " + config.outputClass + "Options {\n public basePath = '';\n public loginUrl: string;\n public dateSerialization: 'local' | 'utc' = 'utc';\n }";
return result;
};
AngularGenerator.prototype.getEnumService = function (enumTypes) {
var enumMetadata = [];
for (var _i = 0, enumTypes_1 = enumTypes; _i < enumTypes_1.length; _i++) {
var enumType = enumTypes_1[_i];
var displayName = void 0;
if (enumType.attributes != undefined) {
displayName = this.getDisplayName(enumType.attributes);
}
if (displayName == undefined) {
displayName = enumType.name;
}
var values = [];
if (enumType.values != undefined) {
for (var _a = 0, _b = enumType.values; _a < _b.length; _a++) {
var enumValue = _b[_a];
var valueDisplayName = void 0;
if (enumValue.attributes != undefined) {
valueDisplayName = this.getDisplayName(enumValue.attributes);
}
if (valueDisplayName == undefined) {
valueDisplayName = enumValue.name;
}
values.push("{ value: " + enumType.name + "." + enumValue.name + ", displayName: '" + valueDisplayName + "' }");
}
}
enumMetadata.push("\n {\n type: " + enumType.name + ",\n displayName: '" + displayName + "',\n values: [\n " + values.join(',\r') + "\n ]\n }");
}
var result = "\n export interface EnumValue<T> {\n displayName: string;\n value: T;\n }\n \n interface EnumMetadata<T> {\n displayName: string;\n type: any;\n values: EnumValue<T>[];\n }\n\n export const ENUM_METADATA: EnumMetadata<any>[] = [\n " + enumMetadata.join(',') + "\n ];\n\n @Injectable()\n export class EnumService {\n public getDisplayName(enumType: any): string {\n const metadata = this.findEnum<any>(enumType);\n \n return metadata.displayName;\n }\n \n public getValueDisplayName(enumType: any, enumValue: number): string {\n const metadata = this.findEnum<any>(enumType);\n\n const exactValue = metadata.values.find(x => x.value === enumValue);\n \n if (exactValue != undefined) {\n return exactValue.displayName;\n }\n \n const result = metadata.values\n // tslint:disable-next-line:no-bitwise\n .filter(x => x.value !== 0 && (x.value & enumValue) === x.value)\n .map(x => x.displayName);\n \n return result.join(', ');\n }\n \n public getValues<T>(enumType: any): EnumValue<T>[] {\n const metadata = this.findEnum<any>(enumType);\n \n return metadata.values;\n }\n \n private findEnum<T>(enumType: any): EnumMetadata<T> {\n const metadata = ENUM_METADATA.find(x => x.type === enumType);\n \n if (metadata == undefined) {\n throw Error('Metadata for enum type not found.');\n }\n \n return metadata;\n } \n }";
return result;
};
AngularGenerator.prototype.getDisplayName = function (attributes) {
var displayAttribute = attributes.find(function (x) { return x.name == 'DisplayName'; });
if (displayAttribute != undefined) {
if (displayAttribute.parameters != undefined) {
var nameParameter = displayAttribute.parameters.find(function (x) { return x.name == 'displayName'; });
if (nameParameter != undefined) {
return nameParameter.value;
}
}
}
var descriptionAttribute = attributes.find(function (x) { return x.name == 'Description'; });
if (descriptionAttribute != undefined) {
if (descriptionAttribute.parameters != undefined) {
var nameParameter = descriptionAttribute.parameters.find(function (x) { return x.name == 'description'; });
if (nameParameter != undefined) {
return nameParameter.value;
}
}
}
var displayNameAttribute = attributes.find(function (x) { return x.name == 'Display'; });
if (displayNameAttribute != undefined) {
if (displayNameAttribute.parameters != undefined) {
var nameParameter = displayNameAttribute.parameters.find(function (x) { return x.name == 'Name'; });
if (nameParameter != undefined) {
return nameParameter.value;
}
}
}
return undefined;
};
AngularGenerator.prototype.getBaseClass = function (config) {
var result = "\n @Injectable()\n export abstract class " + config.outputClass + "Base {\n private dateFormat = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}.*/;\n\n constructor(public http: HttpClient, public options: " + config.outputClass + ("Options) {\n this.reviver = this.reviver.bind(this);\n }\n\n " + this.getRequestMethod(config) + "\n\n private extractBody(response: HttpResponse<any>): any {\n let body = response.body;\n \n if (typeof body === 'string') {\n if (this.isJsonResponse(response)) {\n try {\n body = this.parseJson(body);\n } catch (e) {\n }\n }\n }\n \n return body;\n }\n \n private extractError(response: HttpErrorResponse): Error {\n const error = new HttpError(response.message);\n \n error.status = response.status;\n error.url = response.url;\n \n const contentType = response.headers.get('content-type');\n \n if (contentType?.includes('application/problem+json')) {\n const problemDetail = JSON.parse(response.error);\n \n if (problemDetail.detail != null) {\n error.detail = problemDetail.detail;\n }\n \n if (problemDetail.error != null || problemDetail.errors != null) {\n error.error = problemDetail.error ?? problemDetail.errors;\n }\n \n if (problemDetail.title != null) {\n error.title = problemDetail.title;\n }\n \n if (problemDetail.traceId != null) {\n error.traceId = problemDetail.traceId;\n }\n \n if (problemDetail.type != null) {\n error.type = problemDetail.type;\n }\n } else {\n if (typeof response.error === 'string') {\n error.title = response.error;\n } else {\n if (this.isJsonResponse(response)) {\n error.error = JSON.parse(response.error);\n } else {\n error.error = response.error;\n }\n }\n }\n \n return error;\n }\n\n private serializeBody(body?: any) {\n if (body == undefined) {\n return body;\n }\n \n if (typeof body !== 'object') {\n return body;\n }\n \n return JSON.stringify(body, (key, value) => {\n if (typeof value === 'string' && this.dateFormat.test(value)) {\n return this.serializeDate(new Date(value));\n }\n \n return value;\n });\n }\n \n private serializeDate(d: Date): string {\n let result: string;\n \n const n = x => x < 10 ? '0' + x.toString() : x.toString();\n \n if (this.options.dateSerialization === 'local') {\n result =\n d.getFullYear() + '-' +\n n(d.getMonth() + 1) + '-' +\n n(d.getDate()) + 'T' +\n n(d.getHours()) + ':' +\n n(d.getMinutes()) + ':' +\n n(d.getSeconds());\n } else {\n result = d.toJSON();\n }\n \n return result;\n } \n\n private isJsonResponse(response: HttpResponse<any> | HttpErrorResponse): boolean {\n const contentType = response.headers.get('content-type');\n \n if (contentType && contentType.indexOf('application/json') !== -1) {\n return true;\n }\n \n return false;\n }\n \n private parseJson(text: string): any {\n return JSON.parse(text, this.reviver);\n }\n \n private reviver(key: any, value: any) {\n if (typeof value === 'string' && this.dateFormat.test(value)) {\n return this.parseDate(value);\n }\n \n return value;\n }\n \n private parseDate(s: string): Date {\n const a = s.split(/[^0-9]/) as any;\n const d = new Date(a[0], a[1] - 1, a[2], a[3], a[4], a[5]);\n return d;\n }\n \n private addSearchParam(params: HttpParams, name: string, value: any): HttpParams {\n if (value instanceof Array) {\n value.forEach((v, i) => {\n params = this.addSearchParam(params, ") + "`${name}[${i}]`" + ", v);\n });\n } else {\n if (value instanceof Date) {\n params = params.append(name, this.serializeDate(value));\n } else {\n if (value instanceof Object) {\n Object.getOwnPropertyNames(value).forEach((propertyName) => {\n params = this.addSearchParam(params, " + "`${name}.${propertyName}`" + ", value[propertyName]);\n });\n } else {\n if (value != undefined) {\n params = params.append(name, value);\n }\n }\n }\n }\n \n return params;\n }\n }";
return result;
};
AngularGenerator.prototype.getRequestMethod = function (config) {
var returnType = config.usePromises ? 'Promise<T>' : 'Observable<T>';
var request = "\n const result = this.http.request(request)\n .pipe(\n filter((event: HttpEvent<any>) => event instanceof HttpResponse),\n map((event: HttpResponse<any>) => {\n return this.extractBody(event);\n }),\n catchError((error: HttpErrorResponse) => {\n return throwError(this.extractError(error));\n })\n );";
var result = "\n protected request<T>(path: string, method: string, urlParams?: any, body?: any): " + returnType + " {\n let url = path;\n let params = new HttpParams();\n \n if (urlParams !== undefined) {\n Object.getOwnPropertyNames(urlParams).forEach((paramName) => {\n if (url.indexOf(" + "`{${paramName}}`" + ") !== -1) {\n url = url.replace(" + "`{${paramName}}`" + (", urlParams[paramName]);\n } else {\n params = this.addSearchParam(params, paramName, urlParams[paramName]);\n }\n });\n }\n\n body = this.serializeBody(body);\n \n const request = new HttpRequest<any>(method, this.options.basePath + url, body, {\n headers: new HttpHeaders({ 'Content-Type': 'application/json' }),\n params: params,\n responseType: 'text'\n });\n \n " + request + "\n \n return " + (config.usePromises ? 'result.toPromise()' : 'result') + ";\n }");
return result;
};
AngularGenerator.prototype.getController = function (controller, config) {
var operations = [];
if (controller.operations != undefined) {
for (var _i = 0, _a = controller.operations; _i < _a.length; _i++) {
var operation = _a[_i];
operations.push(this.getOperation(operation, config));
}
}
var className = controller.name;
var suffix = config.suffix == undefined ? 'Service' : config.suffix;
className += suffix;
var result = "\n @Injectable()\n export class " + className + " extends " + config.outputClass + ("Base {\n " + operations.join('') + "\n }");
return result;
};
AngularGenerator.prototype.getOperation = function (operation, config) {
var path = operation.path;
var method = operation.method;
var urlParams = '';
var bodyParam = undefined;
var name = this.camelCase(operation.name);
var returnTypeArgument = this.mapType(operation.responseType);
var returnType = config.usePromises ? "Promise<" + returnTypeArgument + ">" : "Observable<" + returnTypeArgument + ">";
var parameters = [];
if (operation.parameters != undefined) {
for (var _i = 0, _a = operation.parameters; _i < _a.length; _i++) {
var parameter = _a[_i];
var source = parameter.in.toLowerCase();
if (source == 'body') {
bodyParam = parameter.name;
}
if (source == 'query' || source == 'path') {
if (urlParams.length > 0) {
urlParams += ', ';
}
urlParams += parameter.name + ": " + parameter.name;
}
parameters.push(this.getOperationParameter(parameter));
}
}
var requestParameters = "'" + path + "', '" + method + "'";
if (urlParams.length > 0 || bodyParam != undefined) {
requestParameters += ", { " + urlParams + " }";
}
if (bodyParam != undefined) {
requestParameters += ", " + bodyParam;
}
var operationMethod = "\n public " + name + "(" + parameters.join() + "): " + returnType + " {\n return this.request<" + returnTypeArgument + ">(" + requestParameters + ");\n }\n ";
if (operation.summary) {
operationMethod = "/** " + operation.summary + " */ " + operationMethod;
}
return operationMethod;
};
AngularGenerator.prototype.getOperationParameter = function (operationParameter) {
var parameter = operationParameter.name;
if (!operationParameter.required && operationParameter.default == undefined) {
parameter += '?';
}
var parameterType = this.mapType(operationParameter.type);
parameter += ": " + parameterType;
if (!operationParameter.required) {
var defaultValue = operationParameter.default;
if (defaultValue != undefined) {
if (parameterType == 'string') {
defaultValue = "'" + defaultValue + "'";
}
parameter += " = " + defaultValue;
}
}
return parameter;
};
AngularGenerator.prototype.getSchema = function (schema) {
if (schema.type === specification_1.SchemaType.Enumeration) {
return this.getEnum(schema);
}
return this.getInterface(schema);
};
AngularGenerator.prototype.getEnum = function (schema) {
var values = [];
if (schema.values != undefined) {
for (var _i = 0, _a = schema.values; _i < _a.length; _i++) {
var enumValue = _a[_i];
if (enumValue.value != undefined) {
values.push(enumValue.name + " = " + enumValue.value);
}
else {
values.push(enumValue.name);
}
}
}
var result = "\n export enum " + schema.name + " {\n " + values.join(', \n') + "\n }";
return result;
};
AngularGenerator.prototype.getInterface = function (schema) {
var _this = this;
var base = '';
var properties = [];
if (schema.extends != undefined) {
base = "extends " + schema.extends.map(function (x) { return _this.mapType(x); }).join(', ');
}
if (schema.properties != undefined) {
for (var _i = 0, _a = schema.properties; _i < _a.length; _i++) {
var property = _a[_i];
var propertyName = this.camelCase(property.name);
var propertyType = this.mapType(property.type);
if (property.nullable) {
propertyName += '?';
}
properties.push(propertyName + ": " + propertyType + ";");
}
}
var result = "\n export interface " + schema.name + " " + base + " {\n " + properties.join('\n') + "\n }";
return result;
};
AngularGenerator.prototype.mapType = function (coreType) {
// collection;
var _this = this;
if (coreType.endsWith('[]')) {
var collectionType = this.mapType(coreType.substr(0, coreType.length - 2));
return collectionType + "[]";
}
// generic type;
if (coreType.endsWith('>')) {
var genericType = this.parseGenericType(coreType);
var genericArguments = genericType.arguments
.map(function (x) { return _this.mapType(x); })
.join(', ');
return genericType.name + "<" + genericArguments + ">";
}
// primitive;
switch (coreType.toLowerCase()) {
case 'guid':
case 'string':
return 'string';
case 'datetime':
return 'Date';
case 'bool':
case 'boolean':
return 'boolean';
case 'byte':
case 'short':
case 'int':
case 'integer':
case 'int16':
case 'int32':
case 'int64':
case 'float':
case 'decimal':
case 'double':
case 'single':
return 'number';
case 'void':
return 'void';
case 'object':
return 'any';
default:
break;
}
// interface;
return coreType;
};
AngularGenerator.prototype.parseGenericType = function (typeName) {
var open = typeName.indexOf('<');
var genericName = typeName.substr(0, open);
var genericArguments = [];
var splitters;
var level = 0;
var buf = '';
for (var i = open + 1; i < typeName.length - 1; i++) {
var c = typeName[i];
if (c == '<') {
level++;
}
else if (c == '>') {
level--;
}
else if (c == ',' && level == 0) {
genericArguments.push(buf.trim());
buf = '';
}
if (c != ',' || level > 0) {
buf += c;
}
}
genericArguments.push(buf.trim());
return { arguments: genericArguments, name: genericName };
};
AngularGenerator.prototype.camelCase = function (text) {
return text.substr(0, 1).toLowerCase() + text.substr(1);
};
return AngularGenerator;
}());
exports.AngularGenerator = AngularGenerator;
// private getApi(apiDescription: ApiDescription, config: GeneratorConfig): string {
// let fields: string[] = [];
// let properties: string[] = [];
// if (apiDescription.controllers != undefined) {
// apiDescription.controllers.forEach((controller) => {
// var className = controller.name + 'Controller';
// var fieldName = '_' + this.camelCase(controller.name);
// fields.push(`private ${fieldName}: ${className};`);
// var property = `public get ${this.camelCase(controller.name)}(): ${className} {
// if (this.${fieldName} == undefined) {
// this.${fieldName} = new ${className}(this._connection);
// }
// return this.${fieldName};
// }`;
// properties.push(property);
// });
// }
// return `
// @Injectable()
// export class ${config.outputClass} {
// private _connection: ApiConnection;
// ${fields.join('\n')}
// constructor (http: Http) {
// this._connection = new ApiConnection(http, '');
// }
// public get basePath(): string {
// return this._connection.basePath;
// }
// public set basePath(basePath: string) {
// this._connection.basePath = basePath;
// }
// ${properties.join('\n')}
// }`;
// }
//# sourceMappingURL=angular-generator.js.map