Skip to content

Commit 19ab048

Browse files
committed
Add support for Kiota (lib, example and tests)
1 parent 047899e commit 19ab048

File tree

293 files changed

+24921
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

293 files changed

+24921
-0
lines changed

.config/dotnet-tools.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@
2525
"commands": [
2626
"docfx"
2727
]
28+
},
29+
"microsoft.openapi.kiota": {
30+
"version": "1.11.1",
31+
"commands": [
32+
"kiota"
33+
]
2834
}
2935
}
3036
}

JsonApiDotNetCore.sln

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenApiNSwagClientTests", "
7474
EndProject
7575
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenApiNSwagEndToEndTests", "test\OpenApiNSwagEndToEndTests\OpenApiNSwagEndToEndTests.csproj", "{3BA4F9B9-3D90-44B5-B09C-28D98E0B4225}"
7676
EndProject
77+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonApiDotNetCore.OpenApi.Client.Kiota", "src\JsonApiDotNetCore.OpenApi.Client.Kiota\JsonApiDotNetCore.OpenApi.Client.Kiota.csproj", "{617FCA5D-A2DE-4083-B373-ADCA9901059F}"
78+
EndProject
79+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenApiKiotaClientExample", "src\Examples\OpenApiKiotaClientExample\OpenApiKiotaClientExample.csproj", "{39DEAFE8-AE29-48E5-A67D-73776D70FC82}"
80+
EndProject
81+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenApiKiotaEndToEndTests", "test\OpenApiKiotaEndToEndTests\OpenApiKiotaEndToEndTests.csproj", "{FD86C676-3D80-4971-8D8C-B0729B2251F6}"
82+
EndProject
7783
Global
7884
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7985
Debug|Any CPU = Debug|Any CPU
@@ -396,6 +402,42 @@ Global
396402
{3BA4F9B9-3D90-44B5-B09C-28D98E0B4225}.Release|x64.Build.0 = Release|Any CPU
397403
{3BA4F9B9-3D90-44B5-B09C-28D98E0B4225}.Release|x86.ActiveCfg = Release|Any CPU
398404
{3BA4F9B9-3D90-44B5-B09C-28D98E0B4225}.Release|x86.Build.0 = Release|Any CPU
405+
{617FCA5D-A2DE-4083-B373-ADCA9901059F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
406+
{617FCA5D-A2DE-4083-B373-ADCA9901059F}.Debug|Any CPU.Build.0 = Debug|Any CPU
407+
{617FCA5D-A2DE-4083-B373-ADCA9901059F}.Debug|x64.ActiveCfg = Debug|Any CPU
408+
{617FCA5D-A2DE-4083-B373-ADCA9901059F}.Debug|x64.Build.0 = Debug|Any CPU
409+
{617FCA5D-A2DE-4083-B373-ADCA9901059F}.Debug|x86.ActiveCfg = Debug|Any CPU
410+
{617FCA5D-A2DE-4083-B373-ADCA9901059F}.Debug|x86.Build.0 = Debug|Any CPU
411+
{617FCA5D-A2DE-4083-B373-ADCA9901059F}.Release|Any CPU.ActiveCfg = Release|Any CPU
412+
{617FCA5D-A2DE-4083-B373-ADCA9901059F}.Release|Any CPU.Build.0 = Release|Any CPU
413+
{617FCA5D-A2DE-4083-B373-ADCA9901059F}.Release|x64.ActiveCfg = Release|Any CPU
414+
{617FCA5D-A2DE-4083-B373-ADCA9901059F}.Release|x64.Build.0 = Release|Any CPU
415+
{617FCA5D-A2DE-4083-B373-ADCA9901059F}.Release|x86.ActiveCfg = Release|Any CPU
416+
{617FCA5D-A2DE-4083-B373-ADCA9901059F}.Release|x86.Build.0 = Release|Any CPU
417+
{39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
418+
{39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Debug|Any CPU.Build.0 = Debug|Any CPU
419+
{39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Debug|x64.ActiveCfg = Debug|Any CPU
420+
{39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Debug|x64.Build.0 = Debug|Any CPU
421+
{39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Debug|x86.ActiveCfg = Debug|Any CPU
422+
{39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Debug|x86.Build.0 = Debug|Any CPU
423+
{39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Release|Any CPU.ActiveCfg = Release|Any CPU
424+
{39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Release|Any CPU.Build.0 = Release|Any CPU
425+
{39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Release|x64.ActiveCfg = Release|Any CPU
426+
{39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Release|x64.Build.0 = Release|Any CPU
427+
{39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Release|x86.ActiveCfg = Release|Any CPU
428+
{39DEAFE8-AE29-48E5-A67D-73776D70FC82}.Release|x86.Build.0 = Release|Any CPU
429+
{FD86C676-3D80-4971-8D8C-B0729B2251F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
430+
{FD86C676-3D80-4971-8D8C-B0729B2251F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
431+
{FD86C676-3D80-4971-8D8C-B0729B2251F6}.Debug|x64.ActiveCfg = Debug|Any CPU
432+
{FD86C676-3D80-4971-8D8C-B0729B2251F6}.Debug|x64.Build.0 = Debug|Any CPU
433+
{FD86C676-3D80-4971-8D8C-B0729B2251F6}.Debug|x86.ActiveCfg = Debug|Any CPU
434+
{FD86C676-3D80-4971-8D8C-B0729B2251F6}.Debug|x86.Build.0 = Debug|Any CPU
435+
{FD86C676-3D80-4971-8D8C-B0729B2251F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
436+
{FD86C676-3D80-4971-8D8C-B0729B2251F6}.Release|Any CPU.Build.0 = Release|Any CPU
437+
{FD86C676-3D80-4971-8D8C-B0729B2251F6}.Release|x64.ActiveCfg = Release|Any CPU
438+
{FD86C676-3D80-4971-8D8C-B0729B2251F6}.Release|x64.Build.0 = Release|Any CPU
439+
{FD86C676-3D80-4971-8D8C-B0729B2251F6}.Release|x86.ActiveCfg = Release|Any CPU
440+
{FD86C676-3D80-4971-8D8C-B0729B2251F6}.Release|x86.Build.0 = Release|Any CPU
399441
EndGlobalSection
400442
GlobalSection(SolutionProperties) = preSolution
401443
HideSolutionNode = FALSE
@@ -427,6 +469,9 @@ Global
427469
{7FC5DFA3-6F66-4FD8-820D-81E93856F252} = {026FBC6C-AF76-4568-9B87-EC73457899FD}
428470
{77F98215-3085-422E-B99D-4C404C2114CF} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
429471
{3BA4F9B9-3D90-44B5-B09C-28D98E0B4225} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
472+
{617FCA5D-A2DE-4083-B373-ADCA9901059F} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}
473+
{39DEAFE8-AE29-48E5-A67D-73776D70FC82} = {026FBC6C-AF76-4568-9B87-EC73457899FD}
474+
{FD86C676-3D80-4971-8D8C-B0729B2251F6} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
430475
EndGlobalSection
431476
GlobalSection(ExtensibilityGlobals) = postSolution
432477
SolutionGuid = {A2421882-8F0A-4905-928F-B550B192F9A4}

JsonApiDotNetCore.sln.DotSettings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,7 @@ $left$ = $right$;</s:String>
670670
<s:Boolean x:Key="/Default/UserDictionary/Words/=Contoso/@EntryIndexedValue">True</s:Boolean>
671671
<s:Boolean x:Key="/Default/UserDictionary/Words/=Injectables/@EntryIndexedValue">True</s:Boolean>
672672
<s:Boolean x:Key="/Default/UserDictionary/Words/=jsonapi/@EntryIndexedValue">True</s:Boolean>
673+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Kiota/@EntryIndexedValue">True</s:Boolean>
673674
<s:Boolean x:Key="/Default/UserDictionary/Words/=linebreaks/@EntryIndexedValue">True</s:Boolean>
674675
<s:Boolean x:Key="/Default/UserDictionary/Words/=Microservices/@EntryIndexedValue">True</s:Boolean>
675676
<s:Boolean x:Key="/Default/UserDictionary/Words/=navigations/@EntryIndexedValue">True</s:Boolean>

package-versions.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<FluentAssertionsVersion>6.12.*</FluentAssertionsVersion>
1919
<GitHubActionsTestLoggerVersion>2.3.*</GitHubActionsTestLoggerVersion>
2020
<InheritDocVersion>2.0.*</InheritDocVersion>
21+
<KiotaVersion>1.*</KiotaVersion>
2122
<MicrosoftApiClientVersion>8.0.*</MicrosoftApiClientVersion>
2223
<NSwagApiClientVersion>13.20.*</NSwagApiClientVersion>
2324
<NewtonsoftJsonVersion>13.0.*</NewtonsoftJsonVersion>
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using JetBrains.Annotations;
2+
3+
namespace OpenApiKiotaClientExample;
4+
5+
/// <summary>
6+
/// Writes incoming and outgoing HTTP messages to the console.
7+
/// </summary>
8+
internal sealed class ColoredConsoleLogHttpMessageHandler : DelegatingHandler
9+
{
10+
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
11+
{
12+
#if DEBUG
13+
await LogRequestAsync(request, cancellationToken);
14+
#endif
15+
16+
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
17+
18+
#if DEBUG
19+
await LogResponseAsync(response, cancellationToken);
20+
#endif
21+
22+
return response;
23+
}
24+
25+
[UsedImplicitly]
26+
private static async Task LogRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
27+
{
28+
using var _ = new ConsoleColorScope(ConsoleColor.Green);
29+
30+
Console.WriteLine($"--> {request}");
31+
string? requestBody = request.Content != null ? await request.Content.ReadAsStringAsync(cancellationToken) : null;
32+
33+
if (!string.IsNullOrEmpty(requestBody))
34+
{
35+
Console.WriteLine();
36+
Console.WriteLine(requestBody);
37+
}
38+
}
39+
40+
[UsedImplicitly]
41+
private static async Task LogResponseAsync(HttpResponseMessage response, CancellationToken cancellationToken)
42+
{
43+
using var _ = new ConsoleColorScope(ConsoleColor.Cyan);
44+
45+
Console.WriteLine($"<-- {response}");
46+
string responseBody = await response.Content.ReadAsStringAsync(cancellationToken);
47+
48+
if (!string.IsNullOrEmpty(responseBody))
49+
{
50+
Console.WriteLine();
51+
Console.WriteLine(responseBody);
52+
}
53+
}
54+
55+
private sealed class ConsoleColorScope : IDisposable
56+
{
57+
public ConsoleColorScope(ConsoleColor foregroundColor)
58+
{
59+
Console.ForegroundColor = foregroundColor;
60+
}
61+
62+
public void Dispose()
63+
{
64+
Console.ResetColor();
65+
}
66+
}
67+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// <auto-generated/>
2+
using Microsoft.Kiota.Abstractions;
3+
using OpenApiKiotaClientExample.GeneratedCode.Api.People;
4+
using OpenApiKiotaClientExample.GeneratedCode.Api.Tags;
5+
using OpenApiKiotaClientExample.GeneratedCode.Api.TodoItems;
6+
using System.Collections.Generic;
7+
using System.IO;
8+
using System.Linq;
9+
using System.Threading.Tasks;
10+
using System;
11+
namespace OpenApiKiotaClientExample.GeneratedCode.Api {
12+
/// <summary>
13+
/// Builds and executes requests for operations under \api
14+
/// </summary>
15+
public class ApiRequestBuilder : BaseRequestBuilder {
16+
/// <summary>The people property</summary>
17+
public PeopleRequestBuilder People { get =>
18+
new PeopleRequestBuilder(PathParameters, RequestAdapter);
19+
}
20+
/// <summary>The tags property</summary>
21+
public TagsRequestBuilder Tags { get =>
22+
new TagsRequestBuilder(PathParameters, RequestAdapter);
23+
}
24+
/// <summary>The todoItems property</summary>
25+
public TodoItemsRequestBuilder TodoItems { get =>
26+
new TodoItemsRequestBuilder(PathParameters, RequestAdapter);
27+
}
28+
/// <summary>
29+
/// Instantiates a new ApiRequestBuilder and sets the default values.
30+
/// </summary>
31+
/// <param name="pathParameters">Path parameters for the request</param>
32+
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
33+
public ApiRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api", pathParameters) {
34+
}
35+
/// <summary>
36+
/// Instantiates a new ApiRequestBuilder and sets the default values.
37+
/// </summary>
38+
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
39+
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
40+
public ApiRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api", rawUrl) {
41+
}
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// <auto-generated/>
2+
using Microsoft.Kiota.Abstractions.Serialization;
3+
using Microsoft.Kiota.Abstractions;
4+
using OpenApiKiotaClientExample.GeneratedCode.Models;
5+
using System.Collections.Generic;
6+
using System.IO;
7+
using System.Linq;
8+
using System.Threading.Tasks;
9+
using System.Threading;
10+
using System;
11+
namespace OpenApiKiotaClientExample.GeneratedCode.Api.People.Item.AssignedTodoItems {
12+
/// <summary>
13+
/// Builds and executes requests for operations under \api\people\{id}\assignedTodoItems
14+
/// </summary>
15+
public class AssignedTodoItemsRequestBuilder : BaseRequestBuilder {
16+
/// <summary>
17+
/// Instantiates a new AssignedTodoItemsRequestBuilder and sets the default values.
18+
/// </summary>
19+
/// <param name="pathParameters">Path parameters for the request</param>
20+
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
21+
public AssignedTodoItemsRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/people/{id}/assignedTodoItems{?query*}", pathParameters) {
22+
}
23+
/// <summary>
24+
/// Instantiates a new AssignedTodoItemsRequestBuilder and sets the default values.
25+
/// </summary>
26+
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
27+
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
28+
public AssignedTodoItemsRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/people/{id}/assignedTodoItems{?query*}", rawUrl) {
29+
}
30+
/// <summary>
31+
/// Retrieves the related todoItems of an individual person&apos;s assignedTodoItems relationship.
32+
/// </summary>
33+
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
34+
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
35+
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
36+
#nullable enable
37+
public async Task<TodoItemCollectionResponseDocument?> GetAsync(Action<RequestConfiguration<AssignedTodoItemsRequestBuilderGetQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default) {
38+
#nullable restore
39+
#else
40+
public async Task<TodoItemCollectionResponseDocument> GetAsync(Action<RequestConfiguration<AssignedTodoItemsRequestBuilderGetQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default) {
41+
#endif
42+
var requestInfo = ToGetRequestInformation(requestConfiguration);
43+
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>> {
44+
{"400", ErrorResponseDocument.CreateFromDiscriminatorValue},
45+
{"404", ErrorResponseDocument.CreateFromDiscriminatorValue},
46+
};
47+
return await RequestAdapter.SendAsync<TodoItemCollectionResponseDocument>(requestInfo, TodoItemCollectionResponseDocument.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
48+
}
49+
/// <summary>
50+
/// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
51+
/// </summary>
52+
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
53+
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
54+
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
55+
#nullable enable
56+
public async Task<Stream?> HeadAsync(Action<RequestConfiguration<AssignedTodoItemsRequestBuilderHeadQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default) {
57+
#nullable restore
58+
#else
59+
public async Task<Stream> HeadAsync(Action<RequestConfiguration<AssignedTodoItemsRequestBuilderHeadQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default) {
60+
#endif
61+
var requestInfo = ToHeadRequestInformation(requestConfiguration);
62+
return await RequestAdapter.SendPrimitiveAsync<Stream>(requestInfo, default, cancellationToken).ConfigureAwait(false);
63+
}
64+
/// <summary>
65+
/// Retrieves the related todoItems of an individual person&apos;s assignedTodoItems relationship.
66+
/// </summary>
67+
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
68+
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
69+
#nullable enable
70+
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<AssignedTodoItemsRequestBuilderGetQueryParameters>>? requestConfiguration = default) {
71+
#nullable restore
72+
#else
73+
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<AssignedTodoItemsRequestBuilderGetQueryParameters>> requestConfiguration = default) {
74+
#endif
75+
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
76+
requestInfo.Configure(requestConfiguration);
77+
requestInfo.Headers.TryAdd("Accept", "application/vnd.api+json");
78+
return requestInfo;
79+
}
80+
/// <summary>
81+
/// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
82+
/// </summary>
83+
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
84+
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
85+
#nullable enable
86+
public RequestInformation ToHeadRequestInformation(Action<RequestConfiguration<AssignedTodoItemsRequestBuilderHeadQueryParameters>>? requestConfiguration = default) {
87+
#nullable restore
88+
#else
89+
public RequestInformation ToHeadRequestInformation(Action<RequestConfiguration<AssignedTodoItemsRequestBuilderHeadQueryParameters>> requestConfiguration = default) {
90+
#endif
91+
var requestInfo = new RequestInformation(Method.HEAD, UrlTemplate, PathParameters);
92+
requestInfo.Configure(requestConfiguration);
93+
return requestInfo;
94+
}
95+
/// <summary>
96+
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
97+
/// </summary>
98+
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
99+
public AssignedTodoItemsRequestBuilder WithUrl(string rawUrl) {
100+
return new AssignedTodoItemsRequestBuilder(rawUrl, RequestAdapter);
101+
}
102+
/// <summary>
103+
/// Retrieves the related todoItems of an individual person&apos;s assignedTodoItems relationship.
104+
/// </summary>
105+
public class AssignedTodoItemsRequestBuilderGetQueryParameters {
106+
/// <summary>For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.</summary>
107+
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
108+
#nullable enable
109+
[QueryParameter("query")]
110+
public string? Query { get; set; }
111+
#nullable restore
112+
#else
113+
[QueryParameter("query")]
114+
public string Query { get; set; }
115+
#endif
116+
}
117+
/// <summary>
118+
/// Compare the returned ETag HTTP header with an earlier one to determine if the response has changed since it was fetched.
119+
/// </summary>
120+
public class AssignedTodoItemsRequestBuilderHeadQueryParameters {
121+
/// <summary>For syntax, see the documentation for the [`include`](https://www.jsonapi.net/usage/reading/including-relationships.html)/[`filter`](https://www.jsonapi.net/usage/reading/filtering.html)/[`sort`](https://www.jsonapi.net/usage/reading/sorting.html)/[`page`](https://www.jsonapi.net/usage/reading/pagination.html)/[`fields`](https://www.jsonapi.net/usage/reading/sparse-fieldset-selection.html) query string parameters.</summary>
122+
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
123+
#nullable enable
124+
[QueryParameter("query")]
125+
public string? Query { get; set; }
126+
#nullable restore
127+
#else
128+
[QueryParameter("query")]
129+
public string Query { get; set; }
130+
#endif
131+
}
132+
}
133+
}

0 commit comments

Comments
 (0)