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

use string templates for urls #69

Merged
merged 2 commits into from
Jan 11, 2024
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
19 changes: 15 additions & 4 deletions AspNetCoreExample.Api/Controllers/WeatherForecastController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@

using Microsoft.AspNetCore.Mvc;

using SkbKontur.TypeScript.ContractGenerator.TypeBuilders.ApiController;

namespace AspNetCoreExample.Api.Controllers;

[ApiController]
Expand Down Expand Up @@ -36,11 +34,11 @@ public void Update(string city, [FromBody] WeatherForecast forecast, Cancellatio
}

[HttpPost("~/[action]")]
public void Reset(int seed)
public IActionResult Reset(int seed)
{
return Ok();
}

[UrlOnly]
[HttpGet("{city}")]
public ActionResult Download(string city)
{
Expand All @@ -53,6 +51,19 @@ public ActionResult Download(string city)
return File(JsonSerializer.SerializeToUtf8Bytes(forecast), "application/json");
}

[HttpGet("{street}/view")]
public async Task<ActionResult> GetStreetView(string street, bool useGoogleImages)
{
await Task.Delay(100);
return File(Array.Empty<byte>(), "image/jpeg");
}

[HttpGet("none")]
public Task<ActionResult<Guid>> NewGuid()
{
return Task.FromResult((ActionResult<Guid>)Ok(Guid.NewGuid()));
}

private static readonly string[] summaries =
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
Expand Down
15 changes: 5 additions & 10 deletions AspNetCoreExample.Generator/output/api/NotesApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,15 @@
import { Guid } from './../DataTypes/Guid';
import { BlogEntry } from './../DataTypes/BlogEntry';
import { ApiBase } from './../ApiBase/ApiBase';
import { url } from './../ApiBase/ApiBase';

export class NotesApi extends ApiBase implements INotesApi {
async addEntry(userId: Guid, entry: BlogEntry): Promise<void> {
return this.makePostRequest(`/v1/user/${userId}/blog`, {

}, {
...entry,
});
addEntry(userId: Guid, entry: BlogEntry): Promise<void> {
return this.makePostRequest(url`/v1/user/${userId}/blog`, entry);
}

async addEntries(userId: Guid, entries: BlogEntry[]): Promise<void> {
return this.makePostRequest(`/v1/user/${userId}/blog/batch`, {

}, entries);
addEntries(userId: Guid, entries: BlogEntry[]): Promise<void> {
return this.makePostRequest(url`/v1/user/${userId}/blog/batch`, entries);
}

};
Expand Down
33 changes: 9 additions & 24 deletions AspNetCoreExample.Generator/output/api/UserApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,23 @@
import { User } from './../DataTypes/User';
import { Guid } from './../DataTypes/Guid';
import { ApiBase } from './../ApiBase/ApiBase';
import { url } from './../ApiBase/ApiBase';

export class UserApi extends ApiBase implements IUserApi {
async createUser(user: User): Promise<void> {
return this.makePostRequest(`/v1/users`, {

}, {
...user,
});
createUser(user: User): Promise<void> {
return this.makePostRequest(url`/v1/users`, user);
}

async deleteUser(userId: Guid): Promise<void> {
return this.makeDeleteRequest(`/v1/users/${userId}`, {

}, {

});
deleteUser(userId: Guid): Promise<void> {
return this.makeDeleteRequest(url`/v1/users/${userId}`);
}

async getUser(userId: Guid): Promise<User> {
return this.makeGetRequest(`/v1/users/${userId}`, {

}, {

});
getUser(userId: Guid): Promise<User> {
return this.makeGetRequest(url`/v1/users/${userId}`);
}

async searchUsers(name: string): Promise<User[]> {
return this.makeGetRequest(`/v1/users`, {
['name']: name,
}, {

});
searchUsers(name: string): Promise<User[]> {
return this.makeGetRequest(url`/v1/users?name=${name}`);
}

};
Expand Down
42 changes: 21 additions & 21 deletions AspNetCoreExample.Generator/output/api/WeatherForecastApi.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
// tslint:disable
// TypeScriptContractGenerator's generated content
import { WeatherForecast } from './../DataTypes/WeatherForecast';
import { Guid } from './../DataTypes/Guid';
import { ApiBase } from './../ApiBase/ApiBase';
import { url } from './../ApiBase/ApiBase';

export class WeatherForecastApi extends ApiBase implements IWeatherForecastApi {
async get(): Promise<WeatherForecast[]> {
return this.makeGetRequest(`/WeatherForecast`, {

}, {

});
get(): Promise<WeatherForecast[]> {
return this.makeGetRequest(url`/WeatherForecast`);
}

async update(city: string, forecast: WeatherForecast): Promise<void> {
return this.makePostRequest(`/WeatherForecast/Update/${city}`, {

}, {
...forecast,
});
update(city: string, forecast: WeatherForecast): Promise<void> {
return this.makePostRequest(url`/WeatherForecast/Update/${city}`, forecast);
}

async reset(seed: number): Promise<void> {
return this.makePostRequest(`/Reset`, {
['seed']: seed,
}, {

});
reset(seed: number): Promise<void> {
return this.makePostRequest(url`/Reset?seed=${seed}`);
}

getDownloadUrl(city: string): string {
return `/WeatherForecast/${city}`;
urlForDownload(city: string): string {
return url`/WeatherForecast/${city}`;
}

urlForGetStreetView(street: string, useGoogleImages: boolean): string {
return url`/WeatherForecast/${street}/view?useGoogleImages=${useGoogleImages}`;
}

newGuid(): Promise<Guid> {
return this.makeGetRequest(url`/WeatherForecast/none`);
}

};
export interface IWeatherForecastApi {
get(): Promise<WeatherForecast[]>;
update(city: string, forecast: WeatherForecast): Promise<void>;
reset(seed: number): Promise<void>;
getDownloadUrl(city: string): string;
urlForDownload(city: string): string;
urlForGetStreetView(street: string, useGoogleImages: boolean): string;
newGuid(): Promise<Guid>;
}
37 changes: 12 additions & 25 deletions AspNetCoreExample.Generator/output/apiBase/ApiBase.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,45 @@
/* eslint-disable */

interface IParamsMap {
[key: string]: null | undefined | number | string | any[] | boolean;
}
export const url = String.raw;

export class ApiBase {
public async makeGetRequest(url: string, queryParams: IParamsMap, body: any): Promise<any> {
const response = await fetch(this.getUrl(url, queryParams), {
public async makeGetRequest(url: string, body?: any): Promise<any> {
const response = await fetch(url, {
method: "GET",
});
return await response.json();
}

public async makePostRequest(url: string, queryParams: IParamsMap, body: any): Promise<any> {
const response = await fetch(this.getUrl(url, queryParams), {
public async makePostRequest(url: string, body?: any): Promise<any> {
const response = await fetch(url, {
method: "POST",
body: JSON.stringify(body),
body: body && JSON.stringify(body),
});
const textResult = await response.text();
if (textResult !== "") {
return JSON.parse(textResult);
}
}

public async makePutRequest(url: string, queryParams: IParamsMap, body: any): Promise<any> {
const response = await fetch(this.getUrl(url, queryParams), {
public async makePutRequest(url: string, body?: any): Promise<any> {
const response = await fetch(url, {
method: "PUT",
body: JSON.stringify(body),
body: body && JSON.stringify(body),
});
const textResult = await response.text();
if (textResult !== "") {
return JSON.parse(textResult);
}
}

public async makeDeleteRequest(url: string, queryParams: IParamsMap, body: any): Promise<any> {
const response = await fetch(this.getUrl(url, queryParams), {
public async makeDeleteRequest(url: string, body?: any): Promise<any> {
const response = await fetch(url, {
method: "DELETE",
body: JSON.stringify(body),
body: body && JSON.stringify(body),
});
const textResult = await response.text();
if (textResult !== "") {
return JSON.parse(textResult);
}
}

public getUrl(url: string, params?: IParamsMap): string {
return url + this.createQueryString(params);
}

public createQueryString(params: any): string {
if (params == null) {
return "";
}
return `${new URLSearchParams(params)}`;
}
}
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
# Changelog
## v2.1 - 2024.01.11
- refactor ApiControllerTypeBuildingContext
- use string templates with custom tag for urls in generated apis

## v2.0.150 - 2024.01.09
- Add net8.0 support to cli tool; run tests against net8.0 tfm
- Fix tests to run on Linux

## v2.0.142 - 2023.11.09
- support type imports

## v2.0.135 - 2023.09.19
- Update `Microsoft.CodeAnalysis.CSharp.Workspaces`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ using SkbKontur.TypeScript.ContractGenerator;
using SkbKontur.TypeScript.ContractGenerator.Abstractions;
using SkbKontur.TypeScript.ContractGenerator.Extensions;
using SkbKontur.TypeScript.ContractGenerator.Internals;
using SkbKontur.TypeScript.ContractGenerator.TypeBuilders.ApiController;
using SkbKontur.TypeScript.ContractGenerator.TypeBuilders;

namespace AspNetCoreExample.Generator
{
public class ApiControllerTypeBuildingContext : ApiControllerTypeBuildingContextBase
public class ApiControllerTypeBuildingContext : TypeBuildingContext
{
public ApiControllerTypeBuildingContext(TypeScriptUnit unit, ITypeInfo type)
: base(unit, type)
Expand All @@ -29,7 +29,7 @@ namespace AspNetCoreExample.Generator
return SkbKontur.TypeScript.ContractGenerator.Roslyn.TypeInfoRewriter.Types[6].IsAssignableFrom(type);
}

protected override TypeLocation GetApiBase(ITypeInfo controllerType)
protected TypeLocation GetApiBase(ITypeInfo controllerType)
{
var apiBaseName = GetApiBaseName(controllerType);
return new TypeLocation
Expand All @@ -46,7 +46,7 @@ namespace AspNetCoreExample.Generator
return "ApiBase";
}

protected override ITypeInfo ResolveReturnType(ITypeInfo typeInfo)
protected ITypeInfo ResolveReturnType(ITypeInfo typeInfo)
{
if (typeInfo.IsGenericType)
{
Expand All @@ -62,49 +62,49 @@ namespace AspNetCoreExample.Generator
return typeInfo;
}

protected override BaseApiMethod ResolveBaseApiMethod(IMethodInfo methodInfo)
protected string ResolveBaseApiMethod(IMethodInfo methodInfo)
{
if (methodInfo.GetAttributes(SkbKontur.TypeScript.ContractGenerator.Roslyn.TypeInfoRewriter.Types[13]).Any())
return BaseApiMethod.Get;
return "Get";

if (methodInfo.GetAttributes(SkbKontur.TypeScript.ContractGenerator.Roslyn.TypeInfoRewriter.Types[14]).Any())
return BaseApiMethod.Post;
return "Post";

if (methodInfo.GetAttributes(SkbKontur.TypeScript.ContractGenerator.Roslyn.TypeInfoRewriter.Types[15]).Any())
return BaseApiMethod.Put;
return "Put";

if (methodInfo.GetAttributes(SkbKontur.TypeScript.ContractGenerator.Roslyn.TypeInfoRewriter.Types[16]).Any())
return BaseApiMethod.Delete;
return "Delete";

throw new NotSupportedException($"Unresolved http verb for method {methodInfo.Name} at controller {methodInfo.DeclaringType?.Name}");
}

protected override string BuildRoute(ITypeInfo controllerType, IMethodInfo methodInfo)
protected string BuildRoute(ITypeInfo controllerType, IMethodInfo methodInfo)
{
var routeTemplate = methodInfo.GetAttributes(false)
.Select(x => x.AttributeData.TryGetValue("Template", out var value) ? (string)value : null)
.SingleOrDefault(x => !string.IsNullOrEmpty(x));
return AppendRoutePrefix(routeTemplate, controllerType);
}

protected override bool PassParameterToCall(IParameterInfo parameterInfo, ITypeInfo controllerType)
protected bool PassParameterToCall(IParameterInfo parameterInfo, ITypeInfo controllerType)
{
if (IsUserScopedApi(controllerType) && parameterInfo.Name == "userId")
return false;
return true;
}

protected override IParameterInfo[] GetQueryParameters(IParameterInfo[] parameters, ITypeInfo controllerType)
protected IParameterInfo[] GetQueryParameters(IParameterInfo[] parameters, ITypeInfo controllerType)
{
return parameters.Where(x => PassParameterToCall(x, controllerType) && !x.GetAttributes(SkbKontur.TypeScript.ContractGenerator.Roslyn.TypeInfoRewriter.Types[17]).Any()).ToArray();
}

protected override IParameterInfo GetBody(IParameterInfo[] parameters, ITypeInfo controllerType)
protected IParameterInfo GetBody(IParameterInfo[] parameters, ITypeInfo controllerType)
{
return parameters.SingleOrDefault(x => PassParameterToCall(x, controllerType) && x.GetAttributes(SkbKontur.TypeScript.ContractGenerator.Roslyn.TypeInfoRewriter.Types[18]).Any());
}

protected override IMethodInfo[] GetMethodsToImplement(ITypeInfo controllerType)
protected IMethodInfo[] GetMethodsToImplement(ITypeInfo controllerType)
{
return controllerType.GetMethods(BindingFlags.Instance | BindingFlags.Public)
.Where(m => !((MethodWrapper)m).Method.IsSpecialName)
Expand Down
Loading
Loading