From 9ba8da4c0fb407280b65e048fd889d22eed7e647 Mon Sep 17 00:00:00 2001 From: Pavel Vostretsov Date: Thu, 11 Jan 2024 13:52:19 +0500 Subject: [PATCH] simplify api generator --- .../output/api/NotesApi.ts | 8 ++-- .../output/api/UserApi.ts | 12 ++--- .../output/api/WeatherForecastApi.ts | 12 ++--- .../output/apiBase/ApiBase.ts | 48 ++++--------------- .../ApiController/ApiBaseLocation.cs | 9 ++++ .../ApiControllerTypeBuildingContext.cs | 17 +++---- .../ApiController/DefaultApiCustomization.cs | 29 +++++------ .../ApiController/IApiCustomization.cs | 3 +- .../ApiController/KnownTypeNames.cs | 2 + 9 files changed, 61 insertions(+), 79 deletions(-) create mode 100644 TypeScript.ContractGenerator/TypeBuilders/ApiController/ApiBaseLocation.cs diff --git a/AspNetCoreExample.Generator/output/api/NotesApi.ts b/AspNetCoreExample.Generator/output/api/NotesApi.ts index db65a53..8810a8f 100644 --- a/AspNetCoreExample.Generator/output/api/NotesApi.ts +++ b/AspNetCoreExample.Generator/output/api/NotesApi.ts @@ -2,16 +2,16 @@ // TypeScriptContractGenerator's generated content import { Guid } from './../DataTypes/Guid'; import { BlogEntry } from './../DataTypes/BlogEntry'; -import { ApiBase } from './../ApiBase/ApiBase'; +import { request } from './../ApiBase/ApiBase'; import { url } from './../ApiBase/ApiBase'; -export class NotesApi extends ApiBase implements INotesApi { +export class NotesApi implements INotesApi { addEntry(userId: Guid, entry: BlogEntry): Promise { - return this.makePostRequest(url`/v1/user/${userId}/blog`, entry); + return request('POST', url`/v1/user/${userId}/blog`, entry); } addEntries(userId: Guid, entries: BlogEntry[]): Promise { - return this.makePostRequest(url`/v1/user/${userId}/blog/batch`, entries); + return request('POST', url`/v1/user/${userId}/blog/batch`, entries); } }; diff --git a/AspNetCoreExample.Generator/output/api/UserApi.ts b/AspNetCoreExample.Generator/output/api/UserApi.ts index b62c2b2..60c2054 100644 --- a/AspNetCoreExample.Generator/output/api/UserApi.ts +++ b/AspNetCoreExample.Generator/output/api/UserApi.ts @@ -2,24 +2,24 @@ // TypeScriptContractGenerator's generated content import { User } from './../DataTypes/User'; import { Guid } from './../DataTypes/Guid'; -import { ApiBase } from './../ApiBase/ApiBase'; +import { request } from './../ApiBase/ApiBase'; import { url } from './../ApiBase/ApiBase'; -export class UserApi extends ApiBase implements IUserApi { +export class UserApi implements IUserApi { createUser(user: User): Promise { - return this.makePostRequest(url`/v1/users`, user); + return request('POST', url`/v1/users`, user); } deleteUser(userId: Guid): Promise { - return this.makeDeleteRequest(url`/v1/users/${userId}`); + return request('DELETE', url`/v1/users/${userId}`); } getUser(userId: Guid): Promise { - return this.makeGetRequest(url`/v1/users/${userId}`); + return request('GET', url`/v1/users/${userId}`); } searchUsers(name: string): Promise { - return this.makeGetRequest(url`/v1/users?name=${name}`); + return request('GET', url`/v1/users?name=${name}`); } }; diff --git a/AspNetCoreExample.Generator/output/api/WeatherForecastApi.ts b/AspNetCoreExample.Generator/output/api/WeatherForecastApi.ts index f7caa54..2f1a2ee 100644 --- a/AspNetCoreExample.Generator/output/api/WeatherForecastApi.ts +++ b/AspNetCoreExample.Generator/output/api/WeatherForecastApi.ts @@ -2,20 +2,20 @@ // TypeScriptContractGenerator's generated content import { WeatherForecast } from './../DataTypes/WeatherForecast'; import { Guid } from './../DataTypes/Guid'; -import { ApiBase } from './../ApiBase/ApiBase'; +import { request } from './../ApiBase/ApiBase'; import { url } from './../ApiBase/ApiBase'; -export class WeatherForecastApi extends ApiBase implements IWeatherForecastApi { +export class WeatherForecastApi implements IWeatherForecastApi { get(): Promise { - return this.makeGetRequest(url`/WeatherForecast`); + return request('GET', url`/WeatherForecast`); } update(city: string, forecast: WeatherForecast): Promise { - return this.makePostRequest(url`/WeatherForecast/Update/${city}`, forecast); + return request('POST', url`/WeatherForecast/Update/${city}`, forecast); } reset(seed: number): Promise { - return this.makePostRequest(url`/Reset?seed=${seed}`); + return request('POST', url`/Reset?seed=${seed}`); } urlForDownload(city: string): string { @@ -27,7 +27,7 @@ export class WeatherForecastApi extends ApiBase implements IWeatherForecastApi { } newGuid(): Promise { - return this.makeGetRequest(url`/WeatherForecast/none`); + return request('GET', url`/WeatherForecast/none`); } }; diff --git a/AspNetCoreExample.Generator/output/apiBase/ApiBase.ts b/AspNetCoreExample.Generator/output/apiBase/ApiBase.ts index 74a9f03..f858aa1 100644 --- a/AspNetCoreExample.Generator/output/apiBase/ApiBase.ts +++ b/AspNetCoreExample.Generator/output/apiBase/ApiBase.ts @@ -2,44 +2,14 @@ export const url = String.raw; -export class ApiBase { - public async makeGetRequest(url: string, body?: any): Promise { - const response = await fetch(url, { - method: "GET", - }); - return await response.json(); - } - - public async makePostRequest(url: string, body?: any): Promise { - const response = await fetch(url, { - method: "POST", - body: body && JSON.stringify(body), - }); - const textResult = await response.text(); - if (textResult !== "") { - return JSON.parse(textResult); - } - } - - public async makePutRequest(url: string, body?: any): Promise { - const response = await fetch(url, { - method: "PUT", - body: body && JSON.stringify(body), - }); - const textResult = await response.text(); - if (textResult !== "") { - return JSON.parse(textResult); - } - } +export const request = async (method: string, url: string, body?: any): Promise => { + const response = await fetch(url, { + method: method, + body: body && JSON.stringify(body), + }); - public async makeDeleteRequest(url: string, body?: any): Promise { - const response = await fetch(url, { - method: "DELETE", - body: body && JSON.stringify(body), - }); - const textResult = await response.text(); - if (textResult !== "") { - return JSON.parse(textResult); - } + const textResult = await response.text(); + if (textResult !== "") { + return JSON.parse(textResult); } -} +} \ No newline at end of file diff --git a/TypeScript.ContractGenerator/TypeBuilders/ApiController/ApiBaseLocation.cs b/TypeScript.ContractGenerator/TypeBuilders/ApiController/ApiBaseLocation.cs new file mode 100644 index 0000000..d917581 --- /dev/null +++ b/TypeScript.ContractGenerator/TypeBuilders/ApiController/ApiBaseLocation.cs @@ -0,0 +1,9 @@ +namespace SkbKontur.TypeScript.ContractGenerator.TypeBuilders.ApiController +{ + public class ApiBaseLocation + { + public string RequestMethodName { get; set; } + public string UrlTagName { get; set; } + public string Location { get; set; } + } +} \ No newline at end of file diff --git a/TypeScript.ContractGenerator/TypeBuilders/ApiController/ApiControllerTypeBuildingContext.cs b/TypeScript.ContractGenerator/TypeBuilders/ApiController/ApiControllerTypeBuildingContext.cs index ab1d326..4d6eec1 100644 --- a/TypeScript.ContractGenerator/TypeBuilders/ApiController/ApiControllerTypeBuildingContext.cs +++ b/TypeScript.ContractGenerator/TypeBuilders/ApiController/ApiControllerTypeBuildingContext.cs @@ -23,10 +23,8 @@ public override void Initialize(ITypeGenerator typeGenerator) BuildAndImportType = t => typeGenerator.BuildAndImportType(Unit, t); var baseApi = ApiCustomization.GetApiBase(Type); - var urlTag = ApiCustomization.GetUrlTag(Type); - - Unit.AddSymbolImport(baseApi.Name, baseApi.Location); - Unit.AddSymbolImport(urlTag.Name, urlTag.Location); + Unit.AddSymbolImport(baseApi.RequestMethodName, baseApi.Location); + Unit.AddSymbolImport(baseApi.UrlTagName, baseApi.Location); var apiName = ApiCustomization.GetApiClassName(Type); var interfaceName = ApiCustomization.GetApiInterfaceName(Type); @@ -37,7 +35,6 @@ public override void Initialize(ITypeGenerator typeGenerator) var apiClassDefinition = new TypeScriptClassDefinition { - BaseClass = new TypeScriptTypeReference(baseApi.Name), ImplementedInterfaces = new TypeScriptType[] {new TypeScriptTypeReference(interfaceName)}, }; @@ -102,13 +99,17 @@ protected virtual TypeScriptReturnStatement CreateCall(IMethodInfo methodInfo) return new TypeScriptReturnStatement(routeExpression); } + var requestMethodName = ApiCustomization.GetApiBase(methodInfo.DeclaringType!).RequestMethodName; + var verb = ApiCustomization.GetMethodVerb(methodInfo); + var requestExpression = new TypeScriptVariableReference(requestMethodName); + var methodExpression = (TypeScriptExpression)new TypeScriptStringLiteral(verb); var bodyExpression = ApiCustomization.GetMethodBodyExpression(methodInfo); var arguments = bodyExpression == null - ? new[] {routeExpression} - : new[] {routeExpression, bodyExpression}; + ? new[] {methodExpression, routeExpression} + : new[] {methodExpression, routeExpression, bodyExpression}; return new TypeScriptReturnStatement( - new TypeScriptMethodCallExpression(new TypeScriptThisReference(), ApiCustomization.GetMethodVerb(methodInfo), arguments) + new TypeScriptFunctionCallExpression(requestExpression, arguments) ); } diff --git a/TypeScript.ContractGenerator/TypeBuilders/ApiController/DefaultApiCustomization.cs b/TypeScript.ContractGenerator/TypeBuilders/ApiController/DefaultApiCustomization.cs index 2a00366..1f3f1df 100644 --- a/TypeScript.ContractGenerator/TypeBuilders/ApiController/DefaultApiCustomization.cs +++ b/TypeScript.ContractGenerator/TypeBuilders/ApiController/DefaultApiCustomization.cs @@ -16,15 +16,10 @@ namespace SkbKontur.TypeScript.ContractGenerator.TypeBuilders.ApiController { public class DefaultApiCustomization : IApiCustomization { - public virtual TypeLocation GetApiBase(ITypeInfo type) => new TypeLocation + public virtual ApiBaseLocation GetApiBase(ITypeInfo type) => new ApiBaseLocation { - Name = "ApiBase", - Location = "ApiBase/ApiBase", - }; - - public virtual TypeLocation GetUrlTag(ITypeInfo type) => new TypeLocation - { - Name = "url", + RequestMethodName = "request", + UrlTagName = "url", Location = "ApiBase/ApiBase", }; @@ -47,7 +42,7 @@ public virtual IParameterInfo[] GetMethodParameters(IMethodInfo methodInfo) => public virtual bool IsUrlMethod(IMethodInfo methodInfo) { - return GetMethodVerb(methodInfo) == "makeGetRequest" && ResolveReturnType(methodInfo.ReturnType).Equals(TypeInfo.From(typeof(void))); + return GetMethodVerb(methodInfo) == "GET" && ResolveReturnType(methodInfo.ReturnType).Equals(TypeInfo.From(typeof(void))); } public virtual bool IsAsyncMethod(IMethodInfo methodInfo) => false; @@ -57,19 +52,25 @@ public virtual string GetMethodVerb(IMethodInfo methodInfo) var attributes = methodInfo.GetAttributes(inherit : false); if (attributes.Any(x => x.HasName(KnownTypeNames.Attributes.HttpGet))) - return "makeGetRequest"; + return "GET"; if (attributes.Any(x => x.HasName(KnownTypeNames.Attributes.HttpPost))) - return "makePostRequest"; + return "POST"; if (attributes.Any(x => x.HasName(KnownTypeNames.Attributes.HttpPut))) - return "makePutRequest"; + return "PUT"; if (attributes.Any(x => x.HasName(KnownTypeNames.Attributes.HttpDelete))) - return "makeDeleteRequest"; + return "DELETE"; if (attributes.Any(x => x.HasName(KnownTypeNames.Attributes.HttpPatch))) - return "makePatchRequest"; + return "PATCH"; + + if (attributes.Any(x => x.HasName(KnownTypeNames.Attributes.HttpHead))) + return "HEAD"; + + if (attributes.Any(x => x.HasName(KnownTypeNames.Attributes.HttpOptions))) + return "OPTIONS"; throw new NotSupportedException($"Unresolved http verb for method {methodInfo.Name} at controller {methodInfo.DeclaringType?.Name}"); } diff --git a/TypeScript.ContractGenerator/TypeBuilders/ApiController/IApiCustomization.cs b/TypeScript.ContractGenerator/TypeBuilders/ApiController/IApiCustomization.cs index 541a3e0..fbead3d 100644 --- a/TypeScript.ContractGenerator/TypeBuilders/ApiController/IApiCustomization.cs +++ b/TypeScript.ContractGenerator/TypeBuilders/ApiController/IApiCustomization.cs @@ -7,8 +7,7 @@ namespace SkbKontur.TypeScript.ContractGenerator.TypeBuilders.ApiController { public interface IApiCustomization { - public TypeLocation GetApiBase(ITypeInfo type); - public TypeLocation GetUrlTag(ITypeInfo type); + public ApiBaseLocation GetApiBase(ITypeInfo type); string GetApiClassName(ITypeInfo type); string GetApiInterfaceName(ITypeInfo type); diff --git a/TypeScript.ContractGenerator/TypeBuilders/ApiController/KnownTypeNames.cs b/TypeScript.ContractGenerator/TypeBuilders/ApiController/KnownTypeNames.cs index fb2a994..3f70c59 100644 --- a/TypeScript.ContractGenerator/TypeBuilders/ApiController/KnownTypeNames.cs +++ b/TypeScript.ContractGenerator/TypeBuilders/ApiController/KnownTypeNames.cs @@ -26,6 +26,8 @@ public static class Attributes public const string HttpPut = "HttpPut"; public const string HttpPatch = "HttpPatch"; public const string HttpDelete = "HttpDelete"; + public const string HttpHead = "HttpHead"; + public const string HttpOptions = "HttpOptions"; public const string FromBody = "FromBody"; } }