Skip to content

Commit

Permalink
3.2 to be published, for .NET 8
Browse files Browse the repository at this point in the history
  • Loading branch information
zijianhuang committed Jun 28, 2024
1 parent 9e2d974 commit 468c125
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 227 deletions.
2 changes: 1 addition & 1 deletion CodeGen.ps1
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Demostrate how to use Fonlow.OpenApiClientGen.exe
cd $PSScriptRoot
./Fonlow.OpenApiClientGen/bin/Debug/net7.0/Fonlow.OpenApiClientGen.exe ./Tests/DemoClientApi/petStore.yaml DemoCodeGen.json
./Fonlow.OpenApiClientGen/bin/Debug/net8.0/Fonlow.OpenApiClientGen.exe ./Tests/DemoClientApi/petStore.yaml DemoCodeGen.json
2 changes: 1 addition & 1 deletion CodeGenNgFormGroup.ps1
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Demostrate how to use Fonlow.OpenApiClientGen.exe
cd $PSScriptRoot
./Fonlow.OpenApiClientGen/bin/Debug/net7.0/Fonlow.OpenApiClientGen.exe ./Tests/DemoClientApi/petStore.yaml DemoCodeGenNgFormGroup.json
./Fonlow.OpenApiClientGen/bin/Debug/net8.0/Fonlow.OpenApiClientGen.exe ./Tests/DemoClientApi/petStore.yaml DemoCodeGenNgFormGroup.json
2 changes: 1 addition & 1 deletion DemoCodeGenNgFormGroup.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
{
"AssemblyName": "Fonlow.OpenApiClientGen.NG2FormGroup",
"TargetDir": "./ng2/src/clientapi",
"TSFile": "ClientApiAuto.ts",
"TSFile": "ClientApiFormGroupAuto.ts",
"AsModule": true,
"ContentType": "application/json;charset=UTF-8"
}
Expand Down
3 changes: 2 additions & 1 deletion OpenApiClientGen.sln
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.editorconfig = .editorconfig
BuildRelease.ps1 = BuildRelease.ps1
CHANGELOG.md = CHANGELOG.md
CodeGen.bat = CodeGen.bat
CodeGen.ps1 = CodeGen.ps1
CodeGenNgFormGroup.ps1 = CodeGenNgFormGroup.ps1
DemoCodeGen.json = DemoCodeGen.json
README.md = README.md
StartPetsApi.ps1 = StartPetsApi.ps1
Expand Down
129 changes: 76 additions & 53 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Strongly Typed OpenAPI Client Generators
Reading an OpenAPI / Swagger YAML/JSON definition file, OpenApiClientGen generates strongly typed client API codes in C# and in TypeScript for Angular, Axios, Fetch API, Aurelia and jQuery, as well as Angular strictly typed forms.
Reading an OpenAPI / Swagger YAML/JSON definition file, OpenApiClientGen generates strongly typed client API codes in C# and in TypeScript for Angular, Axios, Fetch API, Aurelia and jQuery, as well as Angular strictly typed reactive forms.

This program is based on Fonlow.TypeScriptCodeDomCore and Fonlow.Poco2TsCore which are core components of [WebApiClientGen](https://github.com/zijianhuang/webapiclientgen), thus the codes generated share similar characteristics.

Expand All @@ -21,29 +21,101 @@ OpenApiClientGen had been tested upon over 2,000 Open API definitions in v3 form
* [Generated TypeScript codes for Fetch API](https://github.com/zijianhuang/openapiclientgen/tree/master/Tests/SwagTests/FetchResults) and [integration tests for pet.yaml](https://github.com/zijianhuang/openapiclientgen/tree/master/fetchapi/src)
* [Generated TypeScript codes for jQuery](https://github.com/zijianhuang/openapiclientgen/tree/master/Tests/SwagTests/JqResults) and [integration tests for pet.yaml](https://github.com/zijianhuang/openapiclientgen/tree/master/jq/src)

## Data Mappings

This section describes only mappings for simple and primitive types in OpenAPI definition, C# and TypeScript.

### To C# Codes

```c#
static readonly Dictionary<string, Type> basicClrTypeDic = new()
{
{"integer_int32", typeof(int) },
{"integer_int64", typeof(long) },
{"integer", typeof(int) },
{"integer_uint32", typeof(uint) },
{"number_float", typeof(float) },
{"number_double", typeof(double) },
{"number_decimal", typeof(decimal) },
{"number", typeof(double) }, //C# by default use double for number literal
{"string", typeof(string) },
{"boolean", typeof(bool) },
{"string_date", typeof(DateOnly) },
{"string_date-time", typeof(DateTimeOffset) },
};
```

### To TypeScript Codes

```c#
static readonly Dictionary<string, string> basicTsTypeDic = new()
{
{"integer_int32", "number" },
{"integer_int64", "number" },
{"integer", "number" },
{"integer_uint32", "number" },
{"number_float", "number" },
{"number_double", "number" },
{"number_decimal", "number" },
{"number", "number" },
{"string", "string" },
{"boolean", "boolean" },
{"string_date", "Date" },
{"string_date-time", "Date" },
};
```

And "integer_int64" is actually mapped to "string" because JS supports integral precision up to 53 bits.

**References:**
* [Dealing with Large Integral Numbers in JavaScript for Integral Types of ASP.NET Core Web API](https://www.codeproject.com/Articles/5377807/Dealing-with-Large-Integral-Numbers-in-JavaScript)

## Validation Mappings

### OpenAPI validation to .NET validation

| Schema | .NET Attribute |
| ------ | ---- |
| MinLength | MinLength |
| MaxLength | MaxLength |
| MinLength + MaxLength | Length |
| Minimum, Maximum | Range(min, max) or Range(TypeFullName, min, max) |
| MinItems | MinLength |
| MaxItems | MaxLength |
| MinItems + MaxItems | Length |
| Pattern | RegularExpression |

### OpenAPI validation to Angular Reactive Forms Validation

| Schema | Angular Reactive Forms Validation |
| ----- | ---- |
| Required | required |
| MinLength, MaxLength, MinItems, MaxItems | minLength, maxLength |
| Minimum, Maximum | min, max |
| Pattern | pattern |

## Installation
OpenApiClientGen is a .NET Core console app.

**Prerequisites**

* .NET 7.
* .NET 8 (since v3.2)

**Remarks**

* The generated C# codes could be built with .NET Frameworks in addition to .NET.


### Source Installation
Check out this repository or one of its tags, then do a release build or a Visual Stuido's Publish.
Check out this repository or one of its tags, then do a release build or a Visual Studio's Publish.

**Hints**

The plugin assemblies should be copied accordingly after a release build.

### Binary Download

Download the [zip files](https://github.com/zijianhuang/openapiclientgen/releases) and extract to a local folder. The console app includes the following plugins for JavaScript/TypeScript libs or frameworks:
Download one the [zip files](https://github.com/zijianhuang/openapiclientgen/releases) of the releases and extract to a local folder. The console app includes the following plugins for JavaScript/TypeScript libs or frameworks:

* Fonlow.OpenApiClientGen.Aurelia
* Fonlow.OpenApiClientGen.Axios
Expand Down Expand Up @@ -416,55 +488,6 @@ The JSON file is mapped to the following [C# codes](https://github.com/zijianhua

For more details, especially the contexts of using these settings, please check [Settings Explained](https://github.com/zijianhuang/openapiclientgen/wiki/Settings-Explained).

### Data Mappings

This section describes only mappings for simple and primitive types in OpenAPI definition, C# and TypeScript.

### To C# Codes

```c#
static readonly Dictionary<string, Type> basicClrTypeDic = new()
{
{"integer_int32", typeof(int) },
{"integer_int64", typeof(long) },
{"integer", typeof(int) },
{"integer_uint32", typeof(uint) },
{"number_float", typeof(float) },
{"number_double", typeof(double) },
{"number_decimal", typeof(decimal) },
{"number", typeof(double) }, //C# by default use double for number literal
{"string", typeof(string) },
{"boolean", typeof(bool) },
{"string_date", typeof(DateOnly) },
{"string_date-time", typeof(DateTimeOffset) },
};
```

### To TypeScript Codes

```c#
static readonly Dictionary<string, string> basicTsTypeDic = new()
{
{"integer_int32", "number" },
{"integer_int64", "number" },
{"integer", "number" },
{"integer_uint32", "number" },
{"number_float", "number" },
{"number_double", "number" },
{"number_decimal", "number" },
{"number", "number" },
{"string", "string" },
{"boolean", "boolean" },
{"string_date", "Date" },
{"string_date-time", "Date" },
};
```

And "integer_int64" is actually mapped to "string" because JS supports integral precision up to 53 bits.

**References:**
* [Dealing with Large Integral Numbers in JavaScript for Integral Types of ASP.NET Core Web API](https://www.codeproject.com/Articles/5377807/Dealing-with-Large-Integral-Numbers-in-JavaScript)

## Comparison with NSwag

* [Quick Comparison](https://github.com/zijianhuang/openapiclientgen/wiki/Comparison-with-NSwag)
Expand Down
48 changes: 24 additions & 24 deletions Tests/DemoClientApiTextJson/PetStoreAuto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace My.Pet.Client
using System.Text.Json;
using System.Text.Json.Serialization;
using Fonlow.Net.Http;


[System.Runtime.Serialization.DataContract(Namespace="http://pet.domain/2020/03")]
public class Address
Expand Down Expand Up @@ -246,8 +246,8 @@ public async Task<Pet> AddPetAsync(Pet requestBody, Action<System.Net.Http.Heade
try
{
responseMessage.EnsureSuccessStatusCodeEx();
var stream = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<Pet>(stream, jsonSerializerSettings);
var streamContent = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<Pet>(streamContent, jsonSerializerSettings);
}
finally
{
Expand Down Expand Up @@ -304,8 +304,8 @@ public async Task<User> CreateUsersWithListInputAsync(User[] requestBody, Action
try
{
responseMessage.EnsureSuccessStatusCodeEx();
var stream = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<User>(stream, jsonSerializerSettings);
var streamContent = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<User>(streamContent, jsonSerializerSettings);
}
finally
{
Expand Down Expand Up @@ -411,8 +411,8 @@ public async Task<Pet[]> FindPetsByStatusAsync(PetStatus status, Action<System.N
try
{
responseMessage.EnsureSuccessStatusCodeEx();
var stream = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<Pet[]>(stream, jsonSerializerSettings);
var streamContent = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<Pet[]>(streamContent, jsonSerializerSettings);
}
finally
{
Expand Down Expand Up @@ -440,8 +440,8 @@ public async Task<Pet[]> FindPetsByTagsAsync(string[] tags, Action<System.Net.Ht
try
{
responseMessage.EnsureSuccessStatusCodeEx();
var stream = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<Pet[]>(stream, jsonSerializerSettings);
var streamContent = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<Pet[]>(streamContent, jsonSerializerSettings);
}
finally
{
Expand All @@ -468,8 +468,8 @@ public async Task<Pet[]> FindPetsByTagsAsync(string[] tags, Action<System.Net.Ht
try
{
responseMessage.EnsureSuccessStatusCodeEx();
var stream = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<System.Collections.Generic.Dictionary<string, int>>(stream, jsonSerializerSettings);
var streamContent = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<System.Collections.Generic.Dictionary<string, int>>(streamContent, jsonSerializerSettings);
}
finally
{
Expand Down Expand Up @@ -497,8 +497,8 @@ public async Task<Order> GetOrderByIdAsync(long orderId, Action<System.Net.Http.
try
{
responseMessage.EnsureSuccessStatusCodeEx();
var stream = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<Order>(stream, jsonSerializerSettings);
var streamContent = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<Order>(streamContent, jsonSerializerSettings);
}
finally
{
Expand Down Expand Up @@ -526,8 +526,8 @@ public async Task<Pet> GetPetByIdAsync(long petId, Action<System.Net.Http.Header
try
{
responseMessage.EnsureSuccessStatusCodeEx();
var stream = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<Pet>(stream, jsonSerializerSettings);
var streamContent = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<Pet>(streamContent, jsonSerializerSettings);
}
finally
{
Expand All @@ -554,8 +554,8 @@ public async Task<User> GetUserByNameAsync(string username, Action<System.Net.Ht
try
{
responseMessage.EnsureSuccessStatusCodeEx();
var stream = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<User>(stream, jsonSerializerSettings);
var streamContent = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<User>(streamContent, jsonSerializerSettings);
}
finally
{
Expand Down Expand Up @@ -583,8 +583,8 @@ public async Task<string> LoginUserAsync(string username, string password, Actio
try
{
responseMessage.EnsureSuccessStatusCodeEx();
var stream = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<string>(stream, jsonSerializerSettings);
var streamContent = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<string>(streamContent, jsonSerializerSettings);
}
finally
{
Expand Down Expand Up @@ -626,7 +626,7 @@ public async Task<Order> PlaceOrderAsync(Order requestBody, Action<System.Net.Ht
{
var requestUri = "store/order";
using var httpRequestMessage = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Post, requestUri);
using var content = System.Net.Http.Json.JsonContent.Create(requestBody, mediaType: null, jsonSerializerSettings);
var content = System.Net.Http.Json.JsonContent.Create(requestBody, mediaType: null, jsonSerializerSettings);
httpRequestMessage.Content = content;
if (handleHeaders != null)
{
Expand All @@ -637,8 +637,8 @@ public async Task<Order> PlaceOrderAsync(Order requestBody, Action<System.Net.Ht
try
{
responseMessage.EnsureSuccessStatusCodeEx();
var stream = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<Order>(stream, jsonSerializerSettings);
var streamContent = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<Order>(streamContent, jsonSerializerSettings);
}
finally
{
Expand Down Expand Up @@ -668,8 +668,8 @@ public async Task<Pet> UpdatePetAsync(Pet requestBody, Action<System.Net.Http.He
try
{
responseMessage.EnsureSuccessStatusCodeEx();
var stream = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<Pet>(stream, jsonSerializerSettings);
var streamContent = await responseMessage.Content.ReadAsStreamAsync();
return JsonSerializer.Deserialize<Pet>(streamContent, jsonSerializerSettings);
}
finally
{
Expand Down
Loading

0 comments on commit 468c125

Please sign in to comment.