diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index ef337ed8d7..3956f0ea31 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -31,7 +31,7 @@ "rollForward": false }, "microsoft.openapi.kiota": { - "version": "1.22.3", + "version": "1.23.0", "commands": [ "kiota" ], diff --git a/docs/usage/openapi-client.md b/docs/usage/openapi-client.md index 14fbe8e822..7958b1b10f 100644 --- a/docs/usage/openapi-client.md +++ b/docs/usage/openapi-client.md @@ -12,7 +12,7 @@ The following code generators are supported, though you may try others as well: # [NSwag](#tab/nswag) -For C# clients, we provide an additional package that provides workarounds for bugs in NSwag and enables using partial PATCH/POST requests. +For C# clients, we provide an additional package that provides workarounds for bugs in NSwag and enables using partial POST/PATCH requests. To add it to your project, run the following command: ``` @@ -146,16 +146,36 @@ From here, continue from step 3 in the list of steps for Visual Studio. # [Kiota](#tab/kiota) -To generate your C# client, install the Kiota tool by following the steps at https://learn.microsoft.com/en-us/openapi/kiota/install#install-as-net-tool. - -Next, generate client code by running the [command line tool](https://learn.microsoft.com/en-us/openapi/kiota/using#client-generation). For example: +To generate your C# client, first add the Kiota tool to your solution: ``` -dotnet kiota generate --language CSharp --class-name ExampleApiClient --output ./GeneratedCode --backing-store --exclude-backward-compatible --clean-output --clear-cache --openapi http://localhost:14140/swagger/v1/swagger.json +dotnet tool install microsoft.openapi.kiota +``` + +After adding the `JsonApiDotNetCore.OpenApi.Client.Kiota` package to your project, add a `KiotaReference` element +to your project file to import your OpenAPI file. For example: + +```xml + + + + $(MSBuildProjectName).GeneratedCode + ExampleApiClient + ./GeneratedCode + $(JsonApiExtraArguments) + + + ``` +> [!NOTE] +> The `ExtraArguments` parameter is required for compatibility with JSON:API. + +Next, build your project. It runs the kiota command-line tool, which generates files in the `GeneratedCode` subdirectory. + > [!CAUTION] -> The `--backing-store` switch is needed for JSON:API partial PATCH/POST requests to work correctly. +> If you're not using ``, at least make sure you're passing the `--backing-store` switch to the command-line tool, +> which is needed for JSON:API partial POST/PATCH requests to work correctly. Kiota is pretty young and therefore still rough around the edges. At the time of writing, there are various bugs, for which we have workarounds in place. For a full example, see the [example project](https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/openapi/src/Examples/OpenApiKiotaClientExample). diff --git a/src/JsonApiDotNetCore.OpenApi.Client.Kiota/Build/JsonApiDotNetCore.OpenApi.Client.Kiota.targets b/src/JsonApiDotNetCore.OpenApi.Client.Kiota/Build/JsonApiDotNetCore.OpenApi.Client.Kiota.targets index 75566822e9..72b32aa7a5 100644 --- a/src/JsonApiDotNetCore.OpenApi.Client.Kiota/Build/JsonApiDotNetCore.OpenApi.Client.Kiota.targets +++ b/src/JsonApiDotNetCore.OpenApi.Client.Kiota/Build/JsonApiDotNetCore.OpenApi.Client.Kiota.targets @@ -1,5 +1,49 @@ + + + + + + + (?:\r\n|\n|\r)(#pragma|using)", RegexOptions.Singleline | RegexOptions.Compiled); + private static readonly Regex NullableRegex = new(@"(?s)#if NETSTANDARD2_1_OR_GREATER .*?(?:\r\n|\n|\r)#nullable enable(?:\r\n|\n|\r)(?.*?)(?:\r\n|\n|\r)#nullable restore(?:\r\n|\n|\r)#else(?:\r\n|\n|\r)(?.*?)(?:\r\n|\n|\r)#endif", RegexOptions.Singleline | RegexOptions.Compiled); + private static readonly Regex LineBreaksRegex = new(@"}(?:\r\n|\n|\r)(?[ ]+/// )", RegexOptions.Singleline | RegexOptions.Compiled); + + public string StartDirectory { get; set; } + + public override bool Execute() + { + string absoluteStartDirectory = Path.GetFullPath(StartDirectory); + Log.LogMessage(MessageImportance.High, $"Patching kiota output files in {absoluteStartDirectory}"); + + foreach (string path in Directory.GetFiles(absoluteStartDirectory, "*.cs", SearchOption.AllDirectories)) + { + string content = File.ReadAllText(path); + content = HeaderRegex.Replace(content, $"// {Environment.NewLine}#nullable enable{Environment.NewLine}#pragma warning disable CS8625{Environment.NewLine}$1"); + content = NullableRegex.Replace(content, "$1"); + content = LineBreaksRegex.Replace(content, $"}}{Environment.NewLine}{Environment.NewLine}$1"); + + File.WriteAllText(path, content); + Log.LogMessage(MessageImportance.Normal, $"Patched file: {path}"); + } + + return true; + } + } + ]]> + + + @@ -140,11 +184,6 @@ - - - - - + diff --git a/src/JsonApiDotNetCore.OpenApi.Client.Kiota/Build/kiota-patch-generated-code.ps1 b/src/JsonApiDotNetCore.OpenApi.Client.Kiota/Build/kiota-patch-generated-code.ps1 deleted file mode 100644 index 73093007ec..0000000000 --- a/src/JsonApiDotNetCore.OpenApi.Client.Kiota/Build/kiota-patch-generated-code.ps1 +++ /dev/null @@ -1,43 +0,0 @@ -#Requires -Version 7.0 - -<# - Patches C# code generated by kiota. - Workaround for bug at https://github.com/microsoft/kiota/issues/3944#issuecomment-2597201229. -#> -param ( - # Path to directory where kiota generated C# code. - [Parameter(Mandatory=$true)] - [string]$kiotaOutputDir -) - -$PSStyle.OutputRendering = 'PlainText' -$osLineBreak = [System.Environment]::NewLine -$files = Get-ChildItem -Path $kiotaOutputDir -Recurse -Include *.cs - -foreach ($file in $files) { - # remove blank lines at EOF, in case this script runs more than once - $content = (Get-Content $file -Raw).TrimEnd() - - # add #nullable enable, suppress extra warning CS8625: Cannot convert null literal to non-nullable reference type. - $content = ($content -replace "// ${osLineBreak}(?#pragma|using)", "// ${osLineBreak}#nullable enable${osLineBreak}#pragma warning disable CS8625${osLineBreak}`$1") - - # remove conditionals - $content = ($content -replace "(?s)#if NETSTANDARD2_1_OR_GREATER .*?${osLineBreak}#nullable enable${osLineBreak}(?.*?)${osLineBreak}#nullable restore${osLineBreak}#else${osLineBreak}(?.*?)${osLineBreak}#endif", "`$1") - - # insert new line between member and next summary - $content = ($content -replace "}${osLineBreak}(?[ ]+/// )", "}${osLineBreak}${osLineBreak}`$1") - - try { - Set-Content -Path $file -Value $content -ErrorAction Stop - } - catch { - # files are occasionally locked when running locally, retry once - Start-Sleep -Milliseconds 100 - Set-Content -Path $file -Value $content -ErrorAction Stop - - if ($error.Count -ne 0) { - $thisScriptName=Split-Path -leaf $PSCommandpath - echo "Failed to run $thisScriptName at file $file" - } - } -} diff --git a/src/JsonApiDotNetCore.OpenApi.Client.Kiota/JsonApiDotNetCore.OpenApi.Client.Kiota.csproj b/src/JsonApiDotNetCore.OpenApi.Client.Kiota/JsonApiDotNetCore.OpenApi.Client.Kiota.csproj index d0f08306cc..66c4d3e6bd 100644 --- a/src/JsonApiDotNetCore.OpenApi.Client.Kiota/JsonApiDotNetCore.OpenApi.Client.Kiota.csproj +++ b/src/JsonApiDotNetCore.OpenApi.Client.Kiota/JsonApiDotNetCore.OpenApi.Client.Kiota.csproj @@ -29,7 +29,6 @@ -