Skip to content

Commit

Permalink
Fixes #48 and #45
Browse files Browse the repository at this point in the history
- Adds netstandard2.1 (because HttpMethod.Patch is widely used).
- Adds the Patch methods to IDownstreamRestApi
- and net462 (to avoid drawing the entiere framework when this
library is consumed from a library targeting 462)
- more test coverage.
  • Loading branch information
jmprieur committed Dec 29, 2022
1 parent 256af90 commit ac722d3
Show file tree
Hide file tree
Showing 10 changed files with 271 additions and 63 deletions.
102 changes: 53 additions & 49 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,57 +1,61 @@
<Project>
<PropertyGroup>
<!--This should be passed from the VSTS build-->
<ClientSemVer Condition="'$(ClientSemVer)' == ''">1.0.0-local</ClientSemVer>
<!--This will generate AssemblyVersion, AssemblyFileVersion and AssemblyInformationVersion-->
<Version>$(ClientSemVer)</Version>
<PropertyGroup>
<!--This should be passed from the VSTS build-->
<ClientSemVer Condition="'$(ClientSemVer)' == ''">1.0.0-local</ClientSemVer>
<!--This will generate AssemblyVersion, AssemblyFileVersion and AssemblyInformationVersion-->
<Version>$(ClientSemVer)</Version>

<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)\build\35MSSharedLib1024.snk</AssemblyOriginatorKeyFile>
<RepositoryType>git</RepositoryType>
<Authors>Microsoft</Authors>
<Company>Microsoft Corporation</Company>
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/AzureAD/microsoft-identity-abstractions-for-dotnet</PackageProjectUrl>
<RepositoryUrl>https://github.com/AzureAD/microsoft-identity-abstractions-for-dotnet</RepositoryUrl>
<PackageReleaseNotes>The release notes are available at https://github.com/AzureAD/microsoft-identity-abstractions-for-dotnet/releases and the roadmap at https://github.com/AzureAD/microsoft-identity-abstractions-for-dotnet/wiki#roadmap </PackageReleaseNotes>
<PackageTags>Microsoft Identity Abstractions for dotnet;Microsoft identity platform;Microsoft Identity Web;.NET;ASP.NET Core;Web App;Web API;B2C;Azure Active Directory;AAD;Identity;Authentication;Authorization</PackageTags>
<DefineConstants>$(DefineConstants);WEB</DefineConstants>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
</PropertyGroup>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)\build\35MSSharedLib1024.snk</AssemblyOriginatorKeyFile>
<RepositoryType>git</RepositoryType>
<Authors>Microsoft</Authors>
<Company>Microsoft Corporation</Company>
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/AzureAD/microsoft-identity-abstractions-for-dotnet</PackageProjectUrl>
<RepositoryUrl>https://github.com/AzureAD/microsoft-identity-abstractions-for-dotnet</RepositoryUrl>
<PackageReleaseNotes>The release notes are available at https://github.com/AzureAD/microsoft-identity-abstractions-for-dotnet/releases and the roadmap at https://github.com/AzureAD/microsoft-identity-abstractions-for-dotnet/wiki#roadmap </PackageReleaseNotes>
<PackageTags>Microsoft Identity Abstractions for dotnet;Microsoft identity platform;Microsoft Identity Web;.NET;ASP.NET Core;Web App;Web API;B2C;Azure Active Directory;AAD;Identity;Authentication;Authorization</PackageTags>
<DefineConstants>$(DefineConstants);WEB</DefineConstants>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
</PropertyGroup>

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net462</TargetFrameworks>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../../build/MSAL.snk</AssemblyOriginatorKeyFile>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Nullable>enable</Nullable>
<LangVersion>9.0</LangVersion>
</PropertyGroup>
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1;net462</TargetFrameworks>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../../build/MSAL.snk</AssemblyOriginatorKeyFile>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Nullable>enable</Nullable>
<LangVersion>9.0</LangVersion>
</PropertyGroup>

<PropertyGroup Label="Source Link">
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<!-- Build symbol package (.snupkg) to distribute the PDB containing Source Link -->
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<!-- The MSAL.snk has both private and public keys -->
<DelaySign>false</DelaySign>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>
<PropertyGroup Label="Source Link">
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<!-- Build symbol package (.snupkg) to distribute the PDB containing Source Link -->
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<!-- The MSAL.snk has both private and public keys -->
<DelaySign>false</DelaySign>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>

<PropertyGroup Condition="'$(TF_BUILD)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
<PropertyGroup Condition="'$(TF_BUILD)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>

<ItemGroup>
<None Include="..\..\LICENSE">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<None Include="..\..\LICENSE">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>

<ItemGroup Label="Build Tools" Condition="$([MSBuild]::IsOsPlatform('Windows'))">
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net462'">
<PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>

<ItemGroup Label="Build Tools" Condition="$([MSBuild]::IsOsPlatform('Windows'))">
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ public class DownstreamRestApiOptionsReadOnlyHttpMethod : DownstreamRestApiOptio
/// <param name="other">other instance to copy from.</param>
private DownstreamRestApiOptionsReadOnlyHttpMethod(DownstreamRestApiOptionsReadOnlyHttpMethod other) : base(other)
{

}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,148 @@ public Task PutForAppAsync<TInput>(
Action<DownstreamRestApiOptionsReadOnlyHttpMethod>? downstreamRestApiOptionsOverride = null,
CancellationToken cancellationToken = default)where TOutput : class;

#if NETSTANDARD2_1_OR_GREATER

/// <summary>
/// Calls, using <see cref="HttpMethod.Patch"/>, a downstream REST API with some input data .
/// By default the input data is serialized in JSON but you can provide your own serializer in the action
/// you pass-in through the <paramref name="downstreamRestApiOptionsOverride"/> parameter.
/// </summary>
/// <typeparam name="TInput">Generic input type.</typeparam>
/// <param name="serviceName">Name of the service describing the downstream REST API. There can
/// be several configuration named sections mapped to a <see cref="DownstreamRestApiOptions"/>,
/// each for one downstream REST API. You can pass-in null, but in that case <paramref name="downstreamRestApiOptionsOverride"/>
/// needs to be set.</param>
/// <param name="input">Data sent to the downstream web API, through the body or the HTTP request.</param>
/// <param name="downstreamRestApiOptionsOverride">[Optional] Overrides the options proposed in the configuration described
/// by <paramref name="serviceName"/>.</param>
/// <param name="user">[Optional] Claims representing a user. This is useful in platforms like Blazor
/// or Azure Signal R, where the HttpContext is not available. In other platforms, the library
/// will find the user from the HttpContext.</param>
/// <param name="cancellationToken"></param>
/// <returns> The value returned by the downstream web API.</returns>
/// <example>
/// <code>
/// await _downstreamWebApi.PatchForUserAsync&lt;MyItem&gt;(
/// "MyService",
/// myItem,
/// options =>
/// {
/// options.RelativePath = $"api/todolist/{myItem.Id}";
/// });
/// </code>
/// </example>
public Task PatchForUserAsync<TInput>(
string? serviceName,
TInput input,
Action<DownstreamRestApiOptionsReadOnlyHttpMethod>? downstreamRestApiOptionsOverride = null,
ClaimsPrincipal? user = null,
CancellationToken cancellationToken = default);

/// <summary>
/// Calls, using <see cref="HttpMethod.Patch"/>, a downstream REST API with some input data and returning data.
/// By default the input data is serialized in JSON and the returned data is deserialized from JSON but you can provide your own serializer and your own deserializer in the action
/// you pass-in through the <paramref name="downstreamRestApiOptionsOverride"/> parameter.
/// </summary>
/// <typeparam name="TInput">Generic input type.</typeparam>
/// <typeparam name="TOutput">Generic output type.</typeparam>
/// <param name="serviceName">Name of the service describing the downstream REST API. There can
/// be several configuration named sections mapped to a <see cref="DownstreamRestApiOptions"/>,
/// each for one downstream REST API. You can pass-in null, but in that case <paramref name="downstreamRestApiOptionsOverride"/>
/// needs to be set.</param>
/// <param name="input">Data sent to the downstream web API, through the body or the HTTP request.</param>
/// <param name="downstreamRestApiOptionsOverride">[Optional] Overrides the options proposed in the configuration described
/// by <paramref name="serviceName"/>.</param>
/// <param name="user">[Optional] Claims representing a user. This is useful in platforms like Blazor
/// or Azure Signal R, where the HttpContext is not available. In other platforms, the library
/// will find the user from the HttpContext.</param>
/// <param name="cancellationToken"></param>
/// <returns> The value returned by the downstream web API.</returns>
/// <example>
/// <code>
/// var result = await _downstreamWebApi.PatchForUserAsync&lt;MyItem, IEnumerable&lt;MyItem&gt;&gt;(
/// "MyService",
/// myItem,
/// options =>
/// {
/// options.RelativePath = $"api/todolist/{myItem.Id}";
/// });
/// </code>
/// </example>
public Task<TOutput?> PatchForUserAsync<TInput, TOutput>(
string? serviceName,
TInput input,
Action<DownstreamRestApiOptionsReadOnlyHttpMethod>? downstreamRestApiOptionsOverride = null,
ClaimsPrincipal? user = null,
CancellationToken cancellationToken = default)where TOutput : class;

/// <summary>
/// Calls, using <see cref="HttpMethod.Patch"/>, a downstream REST API with some input data .
/// By default the input data is serialized in JSON but you can provide your own serializer in the action
/// you pass-in through the <paramref name="downstreamRestApiOptionsOverride"/> parameter.
/// </summary>
/// <typeparam name="TInput">Generic input type.</typeparam>
/// <param name="serviceName">Name of the service describing the downstream REST API. There can
/// be several configuration named sections mapped to a <see cref="DownstreamRestApiOptions"/>,
/// each for one downstream REST API. You can pass-in null, but in that case <paramref name="downstreamRestApiOptionsOverride"/>
/// needs to be set.</param>
/// <param name="input">Data sent to the downstream web API, through the body or the HTTP request.</param>
/// <param name="downstreamRestApiOptionsOverride">[Optional] Overrides the options proposed in the configuration described
/// by <paramref name="serviceName"/>.</param>
/// <param name="cancellationToken"></param>
/// <returns> The value returned by the downstream web API.</returns>
/// <example>
/// <code>
/// await _downstreamWebApi.PatchForAppAsync&lt;MyItem&gt;(
/// "MyService",
/// myItem,
/// options =>
/// {
/// options.RelativePath = $"api/todolist/{myItem.Id}";
/// });
/// </code>
/// </example>
public Task PatchForAppAsync<TInput>(
string? serviceName,
TInput input,
Action<DownstreamRestApiOptionsReadOnlyHttpMethod>? downstreamRestApiOptionsOverride = null,
CancellationToken cancellationToken = default);

/// <summary>
/// Calls, using <see cref="HttpMethod.Patch"/>, a downstream REST API with some input data and returning data.
/// By default the input data is serialized in JSON and the returned data is deserialized from JSON but you can provide your own serializer and your own deserializer in the action
/// you pass-in through the <paramref name="downstreamRestApiOptionsOverride"/> parameter.
/// </summary>
/// <typeparam name="TInput">Generic input type.</typeparam>
/// <typeparam name="TOutput">Generic output type.</typeparam>
/// <param name="serviceName">Name of the service describing the downstream REST API. There can
/// be several configuration named sections mapped to a <see cref="DownstreamRestApiOptions"/>,
/// each for one downstream REST API. You can pass-in null, but in that case <paramref name="downstreamRestApiOptionsOverride"/>
/// needs to be set.</param>
/// <param name="input">Data sent to the downstream web API, through the body or the HTTP request.</param>
/// <param name="downstreamRestApiOptionsOverride">[Optional] Overrides the options proposed in the configuration described
/// by <paramref name="serviceName"/>.</param>
/// <param name="cancellationToken"></param>
/// <returns> The value returned by the downstream web API.</returns>
/// <example>
/// <code>
/// var result = await _downstreamWebApi.PatchForAppAsync&lt;MyItem, IEnumerable&lt;MyItem&gt;&gt;(
/// "MyService",
/// myItem,
/// options =>
/// {
/// options.RelativePath = $"api/todolist/{myItem.Id}";
/// });
/// </code>
/// </example>
public Task<TOutput?> PatchForAppAsync<TInput, TOutput>(
string? serviceName,
TInput input,
Action<DownstreamRestApiOptionsReadOnlyHttpMethod>? downstreamRestApiOptionsOverride = null,
CancellationToken cancellationToken = default)where TOutput : class;

#endif // NETSTANDARD2_1_OR_GREATER

/// <summary>
/// Calls, using <see cref="HttpMethod.Delete"/>, a downstream REST API with some input data .
/// By default the input data is serialized in JSON but you can provide your own serializer in the action
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,15 @@ namespace Microsoft.Identity.Abstractions
public partial interface IDownstreamRestApi
{
<#
foreach(string httpMethod in new string[]{ "Get", "Post", "Put", "Delete"} )
foreach(string httpMethod in new string[]{ "Get", "Post", "Put", "Patch", "Delete"} )
{
if (httpMethod == "Patch")
{
#>

#if NETSTANDARD2_1_OR_GREATER
<#
}
foreach(string token in new string[] {"User", "App"})
{
foreach(string template in new string[] {"<TInput>", "<TOutput>", "<TInput, TOutput>"} )
Expand All @@ -35,7 +42,7 @@ namespace Microsoft.Identity.Abstractions
if (httpMethod == "Get" && !hasOutput)
continue;

if ((httpMethod == "Post" || httpMethod == "Put" || httpMethod == "Delete") && !hasInput)
if ((httpMethod == "Post" || httpMethod == "Put" || httpMethod == "Delete" || httpMethod == "Patch") && !hasInput)
continue;

bool hasApp = (token == "App");
Expand Down Expand Up @@ -96,8 +103,17 @@ namespace Microsoft.Identity.Abstractions
<# } #>
CancellationToken cancellationToken = default)<#= hasOutput?"where TOutput : class;" :";" #>
<#
}
}
}
#>
<#
if (httpMethod == "Patch")
{
#>

#endif // NETSTANDARD2_1_OR_GREATER
<#
}
}
#>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net462'">
<PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>

<ItemGroup>
<None Update="DownstreamRestApi\IDownstreamRestApi.HttpMethods.tt">
<LastGenOutput>IDownstreamRestApi.HttpMethods.cs</LastGenOutput>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,36 @@ public Task DeleteForUserAsync<TInput>(string? serviceName, TInput input, Action
return Task.FromResult<TOutput?>(default);
}

public Task PatchForAppAsync<TInput>(string? serviceName, TInput input, Action<DownstreamRestApiOptionsReadOnlyHttpMethod>? downstreamRestApiOptionsOverride = null, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}

public Task<TOutput?> PatchForAppAsync<TOutput>(string? serviceName, Action<DownstreamRestApiOptionsReadOnlyHttpMethod>? downstreamRestApiOptionsOverride = null, CancellationToken cancellationToken = default) where TOutput : class
{
throw new NotImplementedException();
}

public Task<TOutput?> PatchForAppAsync<TInput, TOutput>(string? serviceName, TInput input, Action<DownstreamRestApiOptionsReadOnlyHttpMethod>? downstreamRestApiOptionsOverride = null, CancellationToken cancellationToken = default) where TOutput : class
{
return Task.FromResult<TOutput?>(null);
}

public Task PatchForUserAsync<TInput>(string? serviceName, TInput input, Action<DownstreamRestApiOptionsReadOnlyHttpMethod>? downstreamRestApiOptionsOverride = null, ClaimsPrincipal? user = null, CancellationToken cancellationToken = default)
{
return Task.FromResult<HttpResponseMessage>(null!);
}

public Task<TOutput?> PatchForUserAsync<TOutput>(string? serviceName, Action<DownstreamRestApiOptionsReadOnlyHttpMethod>? downstreamRestApiOptionsOverride = null, ClaimsPrincipal? user = null, CancellationToken cancellationToken = default) where TOutput : class
{
return Task.FromResult<TOutput?>(null);
}

public Task<TOutput?> PatchForUserAsync<TInput, TOutput>(string? serviceName, TInput input, Action<DownstreamRestApiOptionsReadOnlyHttpMethod>? downstreamRestApiOptionsOverride = null, ClaimsPrincipal? user = null, CancellationToken cancellationToken = default) where TOutput : class
{
return Task.FromResult<TOutput?>(null);
}

public Task PostForAppAsync<TInput>(string? serviceName, TInput input, Action<DownstreamRestApiOptionsReadOnlyHttpMethod>? downstreamRestApiOptionsOverride = null, CancellationToken cancellationToken = default)
{
return Task.CompletedTask;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ public async Task<ActionResult> GetInOrderToEdit(int id)
return View(todo);
}

// HttpMethod.Patch is only available on netcore2.0 and above, not .NET FW
#if NETCOREAPP2_0_OR_GREATER
// POST: TodoList/Edit/5
public async Task<ActionResult> Edit(int id, /*[Bind("Id,Title,Owner")]*/ Todo todo)
{
Expand All @@ -95,6 +97,7 @@ await _downstreamWebApi.CallRestApiForUserAsync<Todo, Todo>(

return RedirectToAction("Index");
}
#endif

// GET: TodoList/Delete/5
public async Task<ActionResult> Delete(int id)
Expand Down
Loading

0 comments on commit ac722d3

Please sign in to comment.