Skip to content

Kiota: Replace PowerShell script with inline MSBuild task for improved performance #1696

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 2 commits into from
Mar 2, 2025
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
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"rollForward": false
},
"microsoft.openapi.kiota": {
"version": "1.22.3",
"version": "1.23.0",
"commands": [
"kiota"
],
Expand Down
32 changes: 26 additions & 6 deletions docs/usage/openapi-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
```
Expand Down Expand Up @@ -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
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<KiotaReference Include="path/to/openapi.json">
<NamespaceName>$(MSBuildProjectName).GeneratedCode</NamespaceName>
<ClassName>ExampleApiClient</ClassName>
<OutputPath>./GeneratedCode</OutputPath>
<ExtraArguments>$(JsonApiExtraArguments)</ExtraArguments>
</KiotaReference>
</ItemGroup>
</Project>
```

> [!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 `<KiotaReference>`, 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).
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,49 @@
<!-- Loosely based on https://github.com/kimbell/Kiota.Testing, related to https://github.com/microsoft/kiota/issues/3005 -->
<Project>
<UsingTask TaskName="KiotaPatchGeneratedCodeFiles" TaskFactory="RoslynCodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<ParameterGroup>
<StartDirectory ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Code Type="Class" Language="cs">
<![CDATA[
using System;
using System.IO;
using System.Text.RegularExpressions;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

public sealed class KiotaPatchGeneratedCodeFiles : Task
{
private static readonly Regex HeaderRegex = new(@"// <auto-generated/>(?:\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)(?<ifBody>.*?)(?:\r\n|\n|\r)#nullable restore(?:\r\n|\n|\r)#else(?:\r\n|\n|\r)(?<elseBody>.*?)(?:\r\n|\n|\r)#endif", RegexOptions.Singleline | RegexOptions.Compiled);
private static readonly Regex LineBreaksRegex = new(@"}(?:\r\n|\n|\r)(?<lineIndent>[ ]+/// <summary>)", 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, $"// <auto-generated/>{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;
}
}
]]>
</Code>
</Task>
</UsingTask>

<!-- Restore local tools -->
<Target Name="_KiotaRestoreTools" Condition="'$(KiotaAutoRestoreTools)' == 'true'">
Expand Down Expand Up @@ -140,11 +184,6 @@
<Exec Command="$(_KiotaCommand) %(KiotaReference.Arguments)" EnvironmentVariables="KIOTA_TUTORIAL_ENABLED=false;KIOTA_OFFLINE_ENABLED=true" />

<!-- Post-process output files -->
<Message Importance="High" Condition="'$(KiotaPatchOutput)' == 'true'" Text="Patching kiota output files in %(KiotaReference.OutputPath)" />
<Exec Condition="'$(KiotaPatchOutput)' == 'true'" ConsoleToMSBuild="true"
Command="pwsh $(MSBuildThisFileDirectory)kiota-patch-generated-code.ps1 %(KiotaReference.OutputPath)">
<Output TaskParameter="ConsoleOutput" ItemName="OutputLinesOfScript" />
</Exec>
<Error Condition="'@(OutputLinesOfScript)' != ''" Text="@(OutputLinesOfScript, '%0a')" />
<KiotaPatchGeneratedCodeFiles StartDirectory="%(KiotaReference.OutputPath)" />
</Target>
</Project>

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
<None Include="..\..\PackageReadme.md" Visible="false" Pack="True" PackagePath="" />
<None Include="Build\*.props" Pack="True" PackagePath="build" />
<None Include="Build\*.targets" Pack="True" PackagePath="build" />
<None Include="Build\*.ps1" Pack="True" PackagePath="build" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading