Skip to content

Commit

Permalink
Merge pull request #63 from microsoft/andrueastman/NRT
Browse files Browse the repository at this point in the history
Adds support for nullable ref types
  • Loading branch information
andrueastman authored Jan 17, 2023
2 parents b0cd595 + 8a58f9a commit ac62342
Show file tree
Hide file tree
Showing 23 changed files with 133 additions and 118 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

## [1.0.0-rc.4] - 2023-01-17

### Changed

- Adds support for nullable reference types

## [1.0.0-rc.3] - 2023-01-09

### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<TargetFrameworks>net462;net6.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<OutputType>Library</OutputType>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
Expand Down
9 changes: 5 additions & 4 deletions Microsoft.Kiota.Abstractions.Tests/RequestHeadersTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,15 @@ public void RemovesValue() {
instance.Remove("name", "value");
Assert.Equal(new [] { "value2" }, instance["name"]);
instance.Remove("name", "value2");
Assert.Null(instance["name"]);
Assert.Throws<KeyNotFoundException>(() => instance["name"]);
}
[Fact]
public void Removes() {
var instance = new RequestHeaders();
instance.Add("name", "value");
instance.Add("name", "value2");
Assert.True(instance.Remove("name"));
Assert.Null(instance["name"]);
Assert.Throws<KeyNotFoundException>(() => instance["name"]);
Assert.False(instance.Remove("name"));
}
[Fact]
Expand All @@ -59,7 +59,7 @@ public void RemovesKVP() {
instance.Add("name", "value");
instance.Add("name", "value2");
Assert.True(instance.Remove(new KeyValuePair<string, IEnumerable<string>>("name", new [] { "value", "value2" })));
Assert.Null(instance["name"]);
Assert.Throws<KeyNotFoundException>(() => instance["name"]);
Assert.False(instance.Remove("name"));
}
[Fact]
Expand All @@ -68,7 +68,8 @@ public void Clears() {
instance.Add("name", "value");
instance.Add("name", "value2");
instance.Clear();
Assert.Null(instance["name"]);
Assert.Throws<KeyNotFoundException>(() => instance["name"]);
Assert.Empty(instance.Keys);
}
[Fact]
public void GetsEnumerator() {
Expand Down
16 changes: 8 additions & 8 deletions src/IRequestAdapter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information.
// ------------------------------------------------------------------------------

Expand Down Expand Up @@ -32,7 +32,7 @@ public interface IRequestAdapter
/// <param name="errorMapping">The error factories mapping to use in case of a failed request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use for cancelling the requests.</param>
/// <returns>The deserialized response model.</returns>
Task<ModelType> SendAsync<ModelType>(RequestInformation requestInfo, ParsableFactory<ModelType> factory, Dictionary<string, ParsableFactory<IParsable>> errorMapping = default, CancellationToken cancellationToken = default) where ModelType : IParsable;
Task<ModelType?> SendAsync<ModelType>(RequestInformation requestInfo, ParsableFactory<ModelType> factory, Dictionary<string, ParsableFactory<IParsable>>? errorMapping = default, CancellationToken cancellationToken = default) where ModelType : IParsable;
/// <summary>
/// Executes the HTTP request specified by the given RequestInformation and returns the deserialized response model collection.
/// </summary>
Expand All @@ -41,42 +41,42 @@ public interface IRequestAdapter
/// <param name="errorMapping">The error factories mapping to use in case of a failed request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use for cancelling the requests.</param>
/// <returns>The deserialized response model collection.</returns>
Task<IEnumerable<ModelType>> SendCollectionAsync<ModelType>(RequestInformation requestInfo, ParsableFactory<ModelType> factory, Dictionary<string, ParsableFactory<IParsable>> errorMapping = default, CancellationToken cancellationToken = default) where ModelType : IParsable;
Task<IEnumerable<ModelType>?> SendCollectionAsync<ModelType>(RequestInformation requestInfo, ParsableFactory<ModelType> factory, Dictionary<string, ParsableFactory<IParsable>>? errorMapping = default, CancellationToken cancellationToken = default) where ModelType : IParsable;
/// <summary>
/// Executes the HTTP request specified by the given RequestInformation and returns the deserialized primitive response model.
/// </summary>
/// <param name="requestInfo">The RequestInformation object to use for the HTTP request.</param>
/// <param name="errorMapping">The error factories mapping to use in case of a failed request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use for cancelling the requests.</param>
/// <returns>The deserialized primitive response model.</returns>
Task<ModelType> SendPrimitiveAsync<ModelType>(RequestInformation requestInfo, Dictionary<string, ParsableFactory<IParsable>> errorMapping = default, CancellationToken cancellationToken = default);
Task<ModelType?> SendPrimitiveAsync<ModelType>(RequestInformation requestInfo, Dictionary<string, ParsableFactory<IParsable>>? errorMapping = default, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the HTTP request specified by the given RequestInformation and returns the deserialized primitive response model collection.
/// </summary>
/// <param name="requestInfo">The RequestInformation object to use for the HTTP request.</param>
/// <param name="errorMapping">The error factories mapping to use in case of a failed request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use for cancelling the requests.</param>
/// <returns>The deserialized primitive response model collection.</returns>
Task<IEnumerable<ModelType>> SendPrimitiveCollectionAsync<ModelType>(RequestInformation requestInfo, Dictionary<string, ParsableFactory<IParsable>> errorMapping = default, CancellationToken cancellationToken = default);
Task<IEnumerable<ModelType>?> SendPrimitiveCollectionAsync<ModelType>(RequestInformation requestInfo, Dictionary<string, ParsableFactory<IParsable>>? errorMapping = default, CancellationToken cancellationToken = default);
/// <summary>
/// Executes the HTTP request specified by the given RequestInformation with no return content.
/// </summary>
/// <param name="requestInfo">The RequestInformation object to use for the HTTP request.</param>
/// <param name="errorMapping">The error factories mapping to use in case of a failed request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use for cancelling the requests.</param>
/// <returns>A Task to await completion.</returns>
Task SendNoContentAsync(RequestInformation requestInfo, Dictionary<string, ParsableFactory<IParsable>> errorMapping = default, CancellationToken cancellationToken = default);
Task SendNoContentAsync(RequestInformation requestInfo, Dictionary<string, ParsableFactory<IParsable>>? errorMapping = default, CancellationToken cancellationToken = default);
/// <summary>
/// The base url for every request.
/// </summary>
string BaseUrl { get; set; }
string? BaseUrl { get; set; }
/// <summary>
/// Converts the given RequestInformation into a native HTTP request used by the implementing adapter.
/// </summary>
/// <typeparam name="T">The type of the native request.</typeparam>
/// <param name="requestInfo">The RequestInformation object to use for the HTTP request.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use for cancelling the requests.</param>
/// <returns>The native HTTP request.</returns>
Task<T> ConvertToNativeRequestAsync<T>(RequestInformation requestInfo, CancellationToken cancellationToken = default);
Task<T?> ConvertToNativeRequestAsync<T>(RequestInformation requestInfo, CancellationToken cancellationToken = default);
}
}
2 changes: 1 addition & 1 deletion src/IResponseHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ public interface IResponseHandler
/// <typeparam name="NativeResponseType">The type of the native response object.</typeparam>
/// <typeparam name="ModelType">The type of the response model object.</typeparam>
/// <returns>A task that represents the asynchronous operation and contains the deserialized response.</returns>
Task<ModelType> HandleResponseAsync<NativeResponseType, ModelType>(NativeResponseType response, Dictionary<string, ParsableFactory<IParsable>> errorMappings);
Task<ModelType?> HandleResponseAsync<NativeResponseType, ModelType>(NativeResponseType response, Dictionary<string, ParsableFactory<IParsable>>? errorMappings);
}
}
7 changes: 5 additions & 2 deletions src/Microsoft.Kiota.Abstractions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,23 @@
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
<AssemblyTitle>Kiota Abstractions Library for dotnet</AssemblyTitle>
<Authors>Microsoft</Authors>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFrameworks>netstandard2.0;netstandard2.1;</TargetFrameworks>
<LangVersion>latest</LangVersion>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<RepositoryUrl>https://github.com/microsoft/kiota-abstractions-dotnet</RepositoryUrl>
<PackageProjectUrl>https://microsoft.github.io/kiota/</PackageProjectUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<Deterministic>true</Deterministic>
<VersionPrefix>1.0.0</VersionPrefix>
<VersionSuffix>rc.3</VersionSuffix>
<VersionSuffix>rc.4</VersionSuffix>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<SignAssembly>false</SignAssembly>
<DelaySign>false</DelaySign>
<AssemblyOriginatorKeyFile>35MSSharedLib1024.snk</AssemblyOriginatorKeyFile>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<!-- Enable this line once we go live to prevent breaking changes -->
<!-- <PackageValidationBaselineVersion>1.0.0</PackageValidationBaselineVersion> -->
<PackageReleaseNotes>
Expand Down
8 changes: 4 additions & 4 deletions src/NativeResponseHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ public class NativeResponseHandler : IResponseHandler
/// <summary>
/// The value of the response
/// </summary>
public object Value;
public object? Value;

/// <summary>
/// The error mappings for the response to use when deserializing failed responses bodies. Where an error code like 401 applies specifically to that status code, a class code like 4XX applies to all status codes within the range if an the specific error code is not present.
/// </summary>
public Dictionary<string, ParsableFactory<IParsable>> ErrorMappings { get; set; }
public Dictionary<string, ParsableFactory<IParsable>>? ErrorMappings { get; set; }

/// <inheritdoc />
public Task<ModelType> HandleResponseAsync<NativeResponseType, ModelType>(NativeResponseType response, Dictionary<string, ParsableFactory<IParsable>> errorMappings)
public Task<ModelType?> HandleResponseAsync<NativeResponseType, ModelType>(NativeResponseType response, Dictionary<string, ParsableFactory<IParsable>>? errorMappings)
{
Value = response;
Value = response ?? throw new ArgumentNullException(nameof(response));
ErrorMappings = errorMappings;
return Task.FromResult(default(ModelType));
}
Expand Down
20 changes: 10 additions & 10 deletions src/NativeResponseWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ public class NativeResponseWrapper
/// <param name="h">The request headers of the request</param>
/// <param name="o">Request options</param>
/// <returns></returns>
public static async Task<NativeResponseType> CallAndGetNativeType<ModelType, NativeResponseType, QueryParametersType>(
Func<Action<QueryParametersType>, Action<IDictionary<string, string>>, IEnumerable<IRequestOption>, IResponseHandler, Task<ModelType>> originalCall,
Action<QueryParametersType> q = default,
Action<IDictionary<string, string>> h = default,
IEnumerable<IRequestOption> o = default) where NativeResponseType : class
public static async Task<NativeResponseType?> CallAndGetNativeType<ModelType, NativeResponseType, QueryParametersType>(
Func<Action<QueryParametersType>?, Action<IDictionary<string, string>>?, IEnumerable<IRequestOption>?, IResponseHandler, Task<ModelType>> originalCall,
Action<QueryParametersType>? q = default,
Action<IDictionary<string, string>>? h = default,
IEnumerable<IRequestOption>? o = default) where NativeResponseType : class
{
var responseHandler = new NativeResponseHandler();
await originalCall.Invoke(q, h, o, responseHandler);
Expand All @@ -42,12 +42,12 @@ public static async Task<NativeResponseType> CallAndGetNativeType<ModelType, Nat
/// <param name="q">The query parameters of the request</param>
/// <param name="h">The request headers of the request</param>
/// <param name="o">Request options</param>
public static async Task<NativeResponseType> CallAndGetNativeType<ModelType, NativeResponseType, QueryParametersType, RequestBodyType>(
Func<RequestBodyType, Action<QueryParametersType>, Action<IDictionary<string, string>>, IEnumerable<IRequestOption>, IResponseHandler, Task<ModelType>> originalCall,
public static async Task<NativeResponseType?> CallAndGetNativeType<ModelType, NativeResponseType, QueryParametersType, RequestBodyType>(
Func<RequestBodyType, Action<QueryParametersType>?, Action<IDictionary<string, string>>?, IEnumerable<IRequestOption>?, IResponseHandler, Task<ModelType>> originalCall,
RequestBodyType requestBody,
Action<QueryParametersType> q = default,
Action<IDictionary<string, string>> h = default,
IEnumerable<IRequestOption> o = default) where NativeResponseType : class
Action<QueryParametersType>? q = default,
Action<IDictionary<string, string>>? h = default,
IEnumerable<IRequestOption>? o = default) where NativeResponseType : class
{
var responseHandler = new NativeResponseHandler();
await originalCall.Invoke(requestBody, q, h, o, responseHandler);
Expand Down
4 changes: 3 additions & 1 deletion src/RequestHeaders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public void Add(string headerName, params string[] headerValues) {
/// <inheritdoc/>
public bool IsReadOnly => false;
/// <inheritdoc/>
public IEnumerable<string> this[string key] { get => TryGetValue(key, out var result) ? result : null; set => Add(key, value); }
public IEnumerable<string> this[string key] { get => TryGetValue(key, out var result) ? result : throw new KeyNotFoundException($"Key not found : {key}"); set => Add(key, value); }

/// <summary>
/// Removes the specified value from the header with the specified name.
Expand Down Expand Up @@ -75,7 +75,9 @@ public void Clear() {
/// <inheritdoc/>
public bool ContainsKey(string key) => !string.IsNullOrEmpty(key) && _headers.ContainsKey(key);
/// <inheritdoc/>
#pragma warning disable CS8604 // Possible null reference argument. //Can't change signature of overriden method implementation
public void Add(string key, IEnumerable<string> value) => Add(key, value?.ToArray());
#pragma warning restore CS8604 // Possible null reference argument.
/// <inheritdoc/>
public bool Remove(string key) {
if(string.IsNullOrEmpty(key))
Expand Down
Loading

0 comments on commit ac62342

Please sign in to comment.