Merge pull request #225 from microsoft/release/update/220107073544
Sync up from Mainline branch for CommitId 6636c16f03f6b9276362d2c2b29…
This commit is contained in:
Коммит
57e6ba83df
|
@ -5,7 +5,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(ProjectSpecificFx)' == ''">
|
||||
<TargetFrameworks>net462;net472;net48;netcoreapp3.0;netcoreapp3.1</TargetFrameworks>
|
||||
<TargetFrameworks>net462;net472;net48;netcoreapp3.0;netcoreapp3.1;netstandard2.0</TargetFrameworks>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Identity.Client;
|
||||
using Microsoft.PowerPlatform.Dataverse.Client.Auth.TokenCache;
|
||||
using Microsoft.PowerPlatform.Dataverse.Client.Utils;
|
||||
|
@ -90,11 +90,11 @@ namespace Microsoft.PowerPlatform.Dataverse.Client.Auth
|
|||
}
|
||||
else
|
||||
{
|
||||
var rslt = GetAuthorityFromTargetServiceAsync(ClientServiceProviders.Instance.GetService<IHttpClientFactory>(), processResult.TargetServiceUrl, logSink).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
if (!string.IsNullOrEmpty(rslt.Authority))
|
||||
var details = GetAuthorityFromTargetServiceAsync(ClientServiceProviders.Instance.GetService<IHttpClientFactory>(), processResult.TargetServiceUrl, logSink).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
if (details.Success)
|
||||
{
|
||||
Authority = rslt.Authority;
|
||||
Resource = rslt.Resource;
|
||||
Authority = details.Authority.AbsoluteUri;
|
||||
Resource = details.Resource.AbsoluteUri;
|
||||
}
|
||||
else
|
||||
throw new ArgumentNullException("Authority", "Need a non-empty authority");
|
||||
|
@ -446,18 +446,6 @@ namespace Microsoft.PowerPlatform.Dataverse.Client.Auth
|
|||
return versionTaggedUriBuilder;
|
||||
}
|
||||
|
||||
|
||||
private const string AuthenticateHeader = "WWW-Authenticate";
|
||||
private const string Bearer = "bearer";
|
||||
private const string AuthorityKey = "authorization_uri";
|
||||
private const string ResourceKey = "resource_id";
|
||||
|
||||
internal class AuthRoutingProperties
|
||||
{
|
||||
public string Authority { get; set; }
|
||||
public string Resource { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get authority and resource for this instance.
|
||||
/// </summary>
|
||||
|
@ -465,66 +453,11 @@ namespace Microsoft.PowerPlatform.Dataverse.Client.Auth
|
|||
/// <param name="logger">Logger to write info too</param>
|
||||
/// <param name="clientFactory">HTTP Client factory to use for this request.</param>
|
||||
/// <returns></returns>
|
||||
private static async Task<AuthRoutingProperties> GetAuthorityFromTargetServiceAsync(IHttpClientFactory clientFactory, Uri targetServiceUrl, DataverseTraceLogger logger)
|
||||
private static async Task<AuthenticationDetails> GetAuthorityFromTargetServiceAsync(IHttpClientFactory clientFactory, Uri targetServiceUrl, DataverseTraceLogger logger)
|
||||
{
|
||||
AuthRoutingProperties authRoutingProperties = new AuthRoutingProperties();
|
||||
var client = clientFactory.CreateClient("DataverseHttpClientFactory");
|
||||
var rslt = await client.GetAsync(targetServiceUrl).ConfigureAwait(false);
|
||||
|
||||
if (rslt.StatusCode == System.Net.HttpStatusCode.NotFound || rslt.StatusCode == System.Net.HttpStatusCode.BadRequest)
|
||||
{
|
||||
// didn't find endpoint.
|
||||
logger.Log($"Failed to get Authority and Resource error. Attempt to Access Endpoint {targetServiceUrl.ToString()} resulted in {rslt.StatusCode}.", TraceEventType.Error);
|
||||
return authRoutingProperties;
|
||||
}
|
||||
|
||||
if (rslt.Headers.Contains("WWW-Authenticate"))
|
||||
{
|
||||
var authenticateHeader = rslt.Headers.GetValues("WWW-Authenticate").FirstOrDefault();
|
||||
authenticateHeader = authenticateHeader.Trim();
|
||||
|
||||
// This also checks for cases like "BearerXXXX authorization_uri=...." and "Bearer" and "Bearer "
|
||||
if (!authenticateHeader.StartsWith(Bearer, StringComparison.OrdinalIgnoreCase)
|
||||
|| authenticateHeader.Length < Bearer.Length + 2
|
||||
|| !char.IsWhiteSpace(authenticateHeader[Bearer.Length]))
|
||||
{
|
||||
//var ex = new ArgumentException(AdalErrorMessage.InvalidAuthenticateHeaderFormat,
|
||||
// nameof(authenticateHeader));
|
||||
//CoreLoggerBase.Default.Error(AdalErrorMessage.InvalidAuthenticateHeaderFormat);
|
||||
//CoreLoggerBase.Default.ErrorPii(ex);
|
||||
//throw ex;
|
||||
}
|
||||
|
||||
authenticateHeader = authenticateHeader.Substring(Bearer.Length).Trim();
|
||||
|
||||
IDictionary<string, string> authenticateHeaderItems = null;
|
||||
try
|
||||
{
|
||||
authenticateHeaderItems =
|
||||
EncodingHelper.ParseKeyValueListStrict(authenticateHeader, ',', false, true);
|
||||
}
|
||||
catch //(ArgumentException ex)
|
||||
{
|
||||
//var newEx = new ArgumentException(AdalErrorMessage.InvalidAuthenticateHeaderFormat,
|
||||
// nameof(authenticateHeader), ex);
|
||||
//CoreLoggerBase.Default.Error(AdalErrorMessage.InvalidAuthenticateHeaderFormat);
|
||||
//CoreLoggerBase.Default.ErrorPii(newEx);
|
||||
//throw newEx;
|
||||
}
|
||||
|
||||
if (authenticateHeaderItems != null)
|
||||
{
|
||||
string param;
|
||||
authenticateHeaderItems.TryGetValue(AuthorityKey, out param);
|
||||
authRoutingProperties.Authority =
|
||||
param.Replace("oauth2/authorize", "") // swap out the old oAuth pattern.
|
||||
.Replace("common", "organizations"); // swap common for organizations because MSAL reasons.
|
||||
authenticateHeaderItems.TryGetValue(ResourceKey, out param);
|
||||
authRoutingProperties.Resource = param;
|
||||
}
|
||||
}
|
||||
|
||||
return authRoutingProperties;
|
||||
var resolver = new AuthorityResolver(client, (t, msg) => logger.Log(msg, t));
|
||||
return await resolver.ProbeForExpectedAuthentication(targetServiceUrl);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.PowerPlatform.Dataverse.Client.Auth
|
||||
{
|
||||
/// <summary>
|
||||
/// Details of expected authentication.
|
||||
/// </summary>
|
||||
public sealed class AuthenticationDetails
|
||||
{
|
||||
/// <summary>
|
||||
/// True if probing returned a WWW-Authenticate header.
|
||||
/// </summary>
|
||||
public bool Success { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Authority to initiate OAuth flow with.
|
||||
/// </summary>
|
||||
// TODO: the 2 Uris here should be nullable: Uri? but that requires to update C# used for this solution from current 7.x to C# 9 or 10
|
||||
public Uri Authority { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// OAuth resource to request authentication for.
|
||||
/// </summary>
|
||||
public Uri Resource { get; internal set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Probes API endpoint to elicit a 401 response with the WWW-Authenticate header and processes the found information
|
||||
/// </summary>
|
||||
public sealed class AuthorityResolver
|
||||
{
|
||||
private const string AuthenticateHeader = "WWW-Authenticate";
|
||||
private const string Bearer = "bearer";
|
||||
private const string AuthorityKey = "authorization_uri";
|
||||
private const string ResourceKey = "resource_id";
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly Action<TraceEventType, string> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// instantiate resolver, using specified HttpClient to be used.
|
||||
/// </summary>
|
||||
/// <param name="httpClient"></param>
|
||||
/// <param name="logger"></param>
|
||||
public AuthorityResolver(HttpClient httpClient, Action<TraceEventType, string> logger = null)
|
||||
{
|
||||
_ = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
|
||||
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attemtps to solicit a WWW-Authenticate reply using an unauthenticated GET call to the given endpoint.
|
||||
/// Parses returned header for details
|
||||
/// </summary>
|
||||
/// <param name="endpoint"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public async Task<AuthenticationDetails> ProbeForExpectedAuthentication(Uri endpoint)
|
||||
{
|
||||
_ = endpoint ?? throw new ArgumentNullException(nameof(endpoint));
|
||||
var details = new AuthenticationDetails();
|
||||
|
||||
HttpResponseMessage response;
|
||||
try
|
||||
{
|
||||
response = await _httpClient.GetAsync(endpoint).ConfigureAwait(false);
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
var errDetails = string.Empty;
|
||||
if (ex.InnerException is WebException wex)
|
||||
{
|
||||
errDetails = $"; details: {wex.Message} ({wex.Status})";
|
||||
}
|
||||
LogError($"Failed to get response from: {endpoint}; error: {ex.Message}{errDetails}");
|
||||
return details;
|
||||
}
|
||||
|
||||
|
||||
if (response.StatusCode == HttpStatusCode.NotFound || response.StatusCode == HttpStatusCode.BadRequest)
|
||||
{
|
||||
// didn't find endpoint.
|
||||
LogError($"Failed to get Authority and Resource error. Attempt to Access Endpoint {endpoint} resulted in {response.StatusCode}.");
|
||||
return details;
|
||||
}
|
||||
|
||||
if (response.Headers.Contains(AuthenticateHeader))
|
||||
{
|
||||
var authenticateHeader = response.Headers.GetValues(AuthenticateHeader).FirstOrDefault();
|
||||
authenticateHeader = authenticateHeader.Trim();
|
||||
|
||||
// This also checks for cases like "BearerXXXX authorization_uri=...." and "Bearer" and "Bearer "
|
||||
if (!authenticateHeader.StartsWith(Bearer, StringComparison.OrdinalIgnoreCase)
|
||||
|| authenticateHeader.Length < Bearer.Length + 2
|
||||
|| !char.IsWhiteSpace(authenticateHeader[Bearer.Length]))
|
||||
{
|
||||
LogError($"Malformed 'Bearer' format: {authenticateHeader}");
|
||||
return details;
|
||||
}
|
||||
|
||||
authenticateHeader = authenticateHeader.Substring(Bearer.Length).Trim();
|
||||
|
||||
IDictionary<string, string> authenticateHeaderItems = null;
|
||||
try
|
||||
{
|
||||
authenticateHeaderItems =
|
||||
EncodingHelper.ParseKeyValueListStrict(authenticateHeader, ',', false, true);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
LogError($"Malformed arguments in '{AuthenticateHeader}: {authenticateHeader}");
|
||||
return details;
|
||||
}
|
||||
|
||||
if (authenticateHeaderItems != null)
|
||||
{
|
||||
if (!authenticateHeaderItems.TryGetValue(AuthorityKey, out var auth))
|
||||
{
|
||||
LogError($"Response header from {endpoint} is missing expected key/value for {AuthorityKey}");
|
||||
return details;
|
||||
}
|
||||
details.Authority = new Uri(
|
||||
auth.Replace("oauth2/authorize", "") // swap out the old oAuth pattern.
|
||||
.Replace("common", "organizations")); // swap common for organizations because MSAL reasons.
|
||||
|
||||
if (!authenticateHeaderItems.TryGetValue(ResourceKey, out var res))
|
||||
{
|
||||
LogError($"Response header from {endpoint} is missing expected key/value for {ResourceKey}");
|
||||
return details;
|
||||
}
|
||||
details.Resource = new Uri(res);
|
||||
details.Success = true;
|
||||
}
|
||||
}
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
private void LogError(string message)
|
||||
{
|
||||
if (_logger != null)
|
||||
{
|
||||
_logger(TraceEventType.Error, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -591,7 +591,7 @@ namespace Microsoft.PowerPlatform.Dataverse.Client
|
|||
/// Cookies that are being passed though clients, when cookies are used
|
||||
/// </summary>
|
||||
internal Dictionary<string, string> CurrentCookieCollection { get; set; } = null;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Server Hint for the number of concurrent threads that would provbide optimal processing.
|
||||
/// </summary>
|
||||
|
@ -1581,7 +1581,9 @@ namespace Microsoft.PowerPlatform.Dataverse.Client
|
|||
}
|
||||
else if (req.Parameters.ContainsKey("Target") && req.Parameters["Target"] is EntityReference entRef) // this should cover things that have targets.
|
||||
{
|
||||
cReq = new Entity(entRef.LogicalName, entRef.Id);
|
||||
cReq = entRef.KeyAttributes.Any()
|
||||
? new Entity(entRef.LogicalName, entRef.KeyAttributes)
|
||||
: new Entity(entRef.LogicalName, entRef.Id);
|
||||
}
|
||||
|
||||
EntityMetadata entityMetadata = null;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ComponentAreaName>DataverseConnectControl</ComponentAreaName>
|
||||
<ComponentAreaName>DataverseClient</ComponentAreaName>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Microsoft.PowerPlatform.Dataverse.ConnectControl</RootNamespace>
|
||||
|
@ -11,7 +11,7 @@
|
|||
<Import Project="..\..\..\Build.Common.StandardAndLegacy.props" />
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(DotNetClassicTargetFrameworks)</TargetFrameworks>
|
||||
<DocumentationFile>$(OutDir)\Microsoft.PowerPlatform.Dataverse.CrmConnectControl.xml</DocumentationFile>
|
||||
<DocumentationFile>$(OutDir)\Microsoft.PowerPlatform.Dataverse.ConnectControl.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="$(PackageVersion_Adal)" />
|
||||
|
|
|
@ -5,6 +5,8 @@ VisualStudioVersion = 16.0.31729.503
|
|||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LivePackageTestsConsole", "UnitTests\LivePackageTestsConsole\LivePackageTestsConsole.csproj", "{AD21CFD4-EDA7-4F31-9A07-CC90C03B4C27}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LivePackageRunUnitTests", "UnitTests\LivePackageRunUnitTests\LivePackageRunUnitTests.csproj", "{F90838B9-F2D2-499B-8C37-4F8389380743}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -15,6 +17,10 @@ Global
|
|||
{AD21CFD4-EDA7-4F31-9A07-CC90C03B4C27}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AD21CFD4-EDA7-4F31-9A07-CC90C03B4C27}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AD21CFD4-EDA7-4F31-9A07-CC90C03B4C27}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F90838B9-F2D2-499B-8C37-4F8389380743}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F90838B9-F2D2-499B-8C37-4F8389380743}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F90838B9-F2D2-499B-8C37-4F8389380743}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F90838B9-F2D2-499B-8C37-4F8389380743}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ComponentAreaName>DataverseConnectControl</ComponentAreaName>
|
||||
<ComponentAreaName>DataverseClient</ComponentAreaName>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>LoginControlTester</RootNamespace>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ComponentAreaName>DataverseConnectControl</ComponentAreaName>
|
||||
<ComponentAreaName>DataverseClient</ComponentAreaName>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Microsoft.PowerPlatform.Dataverse.Ui.Styles</RootNamespace>
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Microsoft.PowerPlatform.Dataverse.Client.Auth;
|
||||
using Xunit;
|
||||
|
||||
namespace Client_Core_Tests.Auth
|
||||
{
|
||||
public class AuthResolverTests
|
||||
{
|
||||
private const string AuthenticateHeader = "www-authenticate";
|
||||
private readonly MockHttpMessageHandler _msgHandler;
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public AuthResolverTests()
|
||||
{
|
||||
_msgHandler = new MockHttpMessageHandler();
|
||||
_httpClient = new HttpClient(_msgHandler);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("https://login.somewhere.tst/abc/authorize", "https://hello.com", HttpStatusCode.Unauthorized, true, "Bearer authorization_uri=https://login.somewhere.tst/abc/authorize, resource_id=https://hello.com")]
|
||||
[InlineData("https://login.somewhere.tst/abc/authorize", "https://hello.com", HttpStatusCode.Unauthorized, true, "BeArEr AUTHORIZATION_URI=https://login.somewhere.tst/abc/authorize, reSoUrCe_ID=https://hello.com")]
|
||||
[InlineData("n/a", "n/a", HttpStatusCode.Unauthorized, false, "Bearer")]
|
||||
[InlineData("https://login.somewhere.tst/abc/authorize", "n/a", HttpStatusCode.Unauthorized, false, "Bearer authorization_uri=https://login.somewhere.tst/abc/authorize")]
|
||||
[InlineData("n/a", "https://hello.com", HttpStatusCode.Unauthorized, false, "Bearer resource_id=https://hello.com")]
|
||||
public async Task ProbeSuccessful(string expectedAuthority, string expectedResource, HttpStatusCode expectedStatus, bool success, string responseHeader)
|
||||
{
|
||||
var endpoint = new Uri("https://ppdevtools.crm.dynamics.com/api/data/v9");
|
||||
var log = new List<string>();
|
||||
var resolver = new AuthorityResolver(_httpClient, (et, msg) =>
|
||||
{
|
||||
et.Should().Be(TraceEventType.Error);
|
||||
log.Add(msg);
|
||||
});
|
||||
|
||||
_msgHandler.ResponseHeader = responseHeader;
|
||||
_msgHandler.ResponseStatus = expectedStatus;
|
||||
var details = await resolver.ProbeForExpectedAuthentication(endpoint);
|
||||
|
||||
details.Success.Should().Be(success);
|
||||
if (success)
|
||||
{
|
||||
details.Authority.Should().Be(new Uri(expectedAuthority));
|
||||
details.Resource.Should().Be(new Uri(expectedResource));
|
||||
}
|
||||
else
|
||||
{
|
||||
log.Count.Should().BeGreaterOrEqualTo(1);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void DnsErrorsHandled()
|
||||
{
|
||||
var endpoint = new Uri("https://doesnotexist-bad.crm.dynamics.com/api/data/v9");
|
||||
var log = new List<string>();
|
||||
var resolver = new AuthorityResolver(_httpClient, (et, msg) =>
|
||||
{
|
||||
et.Should().Be(TraceEventType.Error);
|
||||
log.Add(msg);
|
||||
});
|
||||
|
||||
_msgHandler.ErrorOnSend = true;
|
||||
var details = await resolver.ProbeForExpectedAuthentication(endpoint);
|
||||
details.Success.Should().BeFalse();
|
||||
log.Count.Should().BeGreaterOrEqualTo(1);
|
||||
}
|
||||
|
||||
private class MockHttpMessageHandler : HttpMessageHandler
|
||||
{
|
||||
public string ResponseHeader { get; set; }
|
||||
public HttpStatusCode ResponseStatus { get; set; } = HttpStatusCode.Unauthorized;
|
||||
public bool ErrorOnSend { get; set; }
|
||||
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (ErrorOnSend)
|
||||
{
|
||||
throw new HttpRequestException("Failed to get response", new WebException("The remote name could not be resolved", WebExceptionStatus.NameResolutionFailure));
|
||||
}
|
||||
|
||||
var response = new HttpResponseMessage(ResponseStatus);
|
||||
response.Headers.Remove(AuthenticateHeader);
|
||||
response.Headers.TryAddWithoutValidation(AuthenticateHeader, ResponseHeader);
|
||||
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ namespace DataverseClient_Core_UnitTests.Properties {
|
|||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<ProjectSpecificFx>true</ProjectSpecificFx>
|
||||
<!-- only test with one of the Classic and Core frameworks each -->
|
||||
<TargetFrameworks>net462;net472;net48;netcoreapp3.1;net5.0</TargetFrameworks>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<ComponentAreaName>DataverseClient-Tests-Package</ComponentAreaName>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\..\..\Build.Common.core.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
|
||||
<PackageReference Include="xunit" Version="$(PackageVersion_XUnit)" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="$(PackageVersion_XUnit)" />
|
||||
<PackageReference Include="coverlet.collector" Version="1.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\LivePackageTestsConsole\LivePackageTestsConsole.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,69 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace LivePackageRunUnitTests
|
||||
{
|
||||
public class RunTests
|
||||
{
|
||||
public RunTests(ITestOutputHelper output)
|
||||
{
|
||||
var converter = new Converter(output);
|
||||
Console.SetOut(converter);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvokeBasicTest()
|
||||
{
|
||||
LivePackageTestsConsole.Program.SkipStop = true;
|
||||
LivePackageTestsConsole.Program.Main(new string[] { "BasicFlow" });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvokeReadSolutionsTest()
|
||||
{
|
||||
LivePackageTestsConsole.Program.SkipStop = true;
|
||||
LivePackageTestsConsole.Program.Main(new string[] { "listsolutions" });
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void InvokeCUDTestTest()
|
||||
{
|
||||
LivePackageTestsConsole.Program.SkipStop = true;
|
||||
LivePackageTestsConsole.Program.Main(new string[] { "CUDTest" });
|
||||
}
|
||||
|
||||
#region Utility
|
||||
private class Converter : TextWriter
|
||||
{
|
||||
ITestOutputHelper _output;
|
||||
public Converter(ITestOutputHelper output)
|
||||
{
|
||||
_output = output;
|
||||
}
|
||||
public override Encoding Encoding
|
||||
{
|
||||
get { return Encoding.UTF8; }
|
||||
}
|
||||
public override void WriteLine(string message)
|
||||
{
|
||||
_output.WriteLine(message);
|
||||
}
|
||||
public override void WriteLine(string format, params object[] args)
|
||||
{
|
||||
_output.WriteLine(format, args);
|
||||
}
|
||||
|
||||
public override void Write(char value)
|
||||
{
|
||||
throw new NotSupportedException("This text writer only supports WriteLine(string) and WriteLine(string, params object[]).");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
using FluentAssertions;
|
||||
using System;
|
||||
using Microsoft.Xrm.Sdk;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using CrmSdk;
|
||||
|
||||
namespace LivePackageTestsConsole
|
||||
{
|
||||
public class CUDTest
|
||||
{
|
||||
public void RunTest()
|
||||
{
|
||||
Console.WriteLine("Starting CUDTest Flow");
|
||||
|
||||
var client = Auth.CreateClient();
|
||||
client.IsReady.Should().BeTrue();
|
||||
|
||||
Console.WriteLine("Creating Account");
|
||||
|
||||
Entity acct = new Entity("account");
|
||||
acct.Attributes["name"] = "testaccount";
|
||||
|
||||
Guid id = client.Create(acct);
|
||||
|
||||
Console.WriteLine("Updating Account");
|
||||
|
||||
Entity acct2 = new Entity("account"); // changing to force a 'new' situation
|
||||
acct2.Id = id;
|
||||
acct2.Attributes["name"] = "testaccount2";
|
||||
|
||||
client.Update(acct2);
|
||||
|
||||
Console.WriteLine("Deleting Account");
|
||||
client.Delete("account", id);
|
||||
}
|
||||
|
||||
public void RunTest2()
|
||||
{
|
||||
Console.WriteLine("Starting CUDTest Flow - OrganziationContext");
|
||||
|
||||
var client = Auth.CreateClient();
|
||||
client.IsReady.Should().BeTrue();
|
||||
|
||||
using (DvServiceContext svcCtx = new DvServiceContext(client))
|
||||
{
|
||||
//svcCtx.MergeOption = Microsoft.Xrm.Sdk.Client.MergeOption.NoTracking; // So as to not keep cached copies while working on test cases.
|
||||
Console.WriteLine("Creating Account");
|
||||
|
||||
Account acct = new Account();
|
||||
acct.Name = "testaccount";
|
||||
svcCtx.AddObject(acct);
|
||||
svcCtx.SaveChanges();
|
||||
|
||||
Guid id = acct.Id;
|
||||
|
||||
Console.WriteLine("Query Account");
|
||||
var aQ = (from a1 in svcCtx.AccountSet
|
||||
where a1.Name.Equals("testaccount")
|
||||
select a1);
|
||||
|
||||
if (aQ != null )
|
||||
Console.WriteLine($"Found Account by Name {aQ.FirstOrDefault().Name}");
|
||||
|
||||
Console.WriteLine("Updating Account");
|
||||
|
||||
Entity acct2 = new Entity("account"); // changing to force a 'new' situation
|
||||
acct2.Id = id;
|
||||
acct2.Attributes["name"] = "testaccount2";
|
||||
client.Update(acct2);
|
||||
|
||||
Console.WriteLine("Deleting Account");
|
||||
client.Delete("account", id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@
|
|||
<RestoreAdditionalProjectSources>
|
||||
$(RepoRoot)\binSigned\$(Configuration)\packages
|
||||
</RestoreAdditionalProjectSources>
|
||||
<TargetDvClientPackageVersion Condition=" '$(targetDvClientPackageVersion)' == '' ">0.5.9</TargetDvClientPackageVersion>
|
||||
<TargetDvClientPackageVersion Condition=" '$(TargetDvClientPackageVersion)' == '' ">0.5.9</TargetDvClientPackageVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net462|AnyCPU'">
|
||||
|
@ -28,7 +28,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
||||
<PackageReference Include="Microsoft.PowerPlatform.Dataverse.Client" Version="$(TargetDvClientPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.PowerPlatform.Dataverse.Client.Dynamics" Version="$(targetDvClientPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.PowerPlatform.Dataverse.Client.Dynamics" Version="$(TargetDvClientPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -5,9 +5,10 @@ namespace LivePackageTestsConsole
|
|||
/// <summary>
|
||||
/// This program will test run against live tests on a known Nuget Package version.
|
||||
/// </summary>
|
||||
class Program
|
||||
public class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
public static bool SkipStop { get; set; } = false;
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Starting Tests");
|
||||
|
||||
|
@ -48,6 +49,12 @@ namespace LivePackageTestsConsole
|
|||
|
||||
tests.Run();
|
||||
}
|
||||
else if ( string.Compare(args[0], "CUDTest", StringComparison.OrdinalIgnoreCase) == 0)
|
||||
{
|
||||
var tests = new CUDTest();
|
||||
tests.RunTest();
|
||||
tests.RunTest2();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -55,7 +62,8 @@ namespace LivePackageTestsConsole
|
|||
tests.Run();
|
||||
}
|
||||
|
||||
Console.ReadKey();
|
||||
if (!SkipStop)
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,6 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ComponentAreaName>DataverseConnectControl</ComponentAreaName>
|
||||
<ComponentAreaName>DataverseClient</ComponentAreaName>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Microsoft.PowerPlatform.Dataverse.WebResourceUtility</RootNamespace>
|
||||
<AssemblyName>Microsoft.PowerPlatform.Dataverse.WebResourceUtility</AssemblyName>
|
||||
|
|
|
@ -6,6 +6,9 @@ Notice:
|
|||
https://docs.microsoft.com/en-us/dotnet/api/microsoft.crm.sdk.messages?view=dynamics-general-ce-9
|
||||
|
||||
++CURRENTRELEASEID++
|
||||
Updated Min dependency to DV ServiceClient 0.5.10
|
||||
|
||||
0.5.10:
|
||||
Updated Newtonsoft.Json to v11.0.2 to match server.
|
||||
|
||||
0.4.12:
|
||||
|
|
|
@ -15,19 +15,19 @@
|
|||
<tags>Dynamics CommonDataService CDS PowerApps PowerPlatform</tags>
|
||||
<dependencies>
|
||||
<group targetFramework=".NETFramework4.6.2">
|
||||
<dependency id="Microsoft.PowerPlatform.Dataverse.Client" version="0.5.0" exclude="Build,Analyzers" />
|
||||
<dependency id="Microsoft.PowerPlatform.Dataverse.Client" version="0.5.10" exclude="Build,Analyzers" />
|
||||
</group>
|
||||
<group targetFramework=".NETFramework4.7.2">
|
||||
<dependency id="Microsoft.PowerPlatform.Dataverse.Client" version="0.5.0" exclude="Build,Analyzers" />
|
||||
<dependency id="Microsoft.PowerPlatform.Dataverse.Client" version="0.5.10" exclude="Build,Analyzers" />
|
||||
</group>
|
||||
<group targetFramework=".NETFramework4.8">
|
||||
<dependency id="Microsoft.PowerPlatform.Dataverse.Client" version="0.5.0" exclude="Build,Analyzers" />
|
||||
<dependency id="Microsoft.PowerPlatform.Dataverse.Client" version="0.5.10" exclude="Build,Analyzers" />
|
||||
</group>
|
||||
<group targetFramework=".NETCoreApp3.0">
|
||||
<dependency id="Microsoft.PowerPlatform.Dataverse.Client" version="0.5.0" exclude="Build,Analyzers" />
|
||||
<dependency id="Microsoft.PowerPlatform.Dataverse.Client" version="0.5.10" exclude="Build,Analyzers" />
|
||||
</group>
|
||||
<group targetFramework=".NETCoreApp3.1">
|
||||
<dependency id="Microsoft.PowerPlatform.Dataverse.Client" version="0.5.0" exclude="Build,Analyzers" />
|
||||
<dependency id="Microsoft.PowerPlatform.Dataverse.Client" version="0.5.10" exclude="Build,Analyzers" />
|
||||
</group>
|
||||
</dependencies>
|
||||
<frameworkAssemblies>
|
||||
|
|
|
@ -3,6 +3,9 @@ Notice:
|
|||
This package is intended to work with .net full framework 4.6.2, 4.7.2 and 4.8, .net core 3.0 and 3.1
|
||||
|
||||
++CURRENTRELEASEID++
|
||||
updated min dependency to DV ServiceClient 0.5.10
|
||||
|
||||
0.5.10:
|
||||
Updated Newtonsoft.Json to v11.0.2 to match server.
|
||||
|
||||
0.4.12:
|
||||
|
|
|
@ -15,19 +15,19 @@
|
|||
<tags>Dynamics CommonDataService CDS PowerApps PowerPlatform CdsServiceClient Dataverse</tags>
|
||||
<dependencies>
|
||||
<group targetFramework=".NETFramework4.6.2">
|
||||
<dependency id="Microsoft.PowerPlatform.Dataverse.Client" version="0.5.0" exclude="Build,Analyzers" />
|
||||
<dependency id="Microsoft.PowerPlatform.Dataverse.Client" version="0.5.10" exclude="Build,Analyzers" />
|
||||
</group>
|
||||
<group targetFramework=".NETFramework4.7.2">
|
||||
<dependency id="Microsoft.PowerPlatform.Dataverse.Client" version="0.5.0" exclude="Build,Analyzers" />
|
||||
<dependency id="Microsoft.PowerPlatform.Dataverse.Client" version="0.5.10" exclude="Build,Analyzers" />
|
||||
</group>
|
||||
<group targetFramework=".NETFramework4.8">
|
||||
<dependency id="Microsoft.PowerPlatform.Dataverse.Client" version="0.5.0" exclude="Build,Analyzers" />
|
||||
<dependency id="Microsoft.PowerPlatform.Dataverse.Client" version="0.5.10" exclude="Build,Analyzers" />
|
||||
</group>
|
||||
<group targetFramework=".NETCoreApp3.0">
|
||||
<dependency id="Microsoft.PowerPlatform.Dataverse.Client" version="0.5.0" exclude="Build,Analyzers" />
|
||||
<dependency id="Microsoft.PowerPlatform.Dataverse.Client" version="0.5.10" exclude="Build,Analyzers" />
|
||||
</group>
|
||||
<group targetFramework=".NETCoreApp3.1">
|
||||
<dependency id="Microsoft.PowerPlatform.Dataverse.Client" version="0.5.0" exclude="Build,Analyzers" />
|
||||
<dependency id="Microsoft.PowerPlatform.Dataverse.Client" version="0.5.10" exclude="Build,Analyzers" />
|
||||
</group>
|
||||
</dependencies>
|
||||
<frameworkAssemblies>
|
||||
|
|
|
@ -8,6 +8,13 @@ Notice:
|
|||
Note: that only OAuth, Certificate, ClientSecret Authentication types are supported at this time.
|
||||
|
||||
++CURRENTRELEASEID++
|
||||
Accepted fix requested here: https://github.com/microsoft/PowerPlatform-DataverseServiceClient/issues/205
|
||||
fixing delete by alternate key request in client.
|
||||
Fixed dependency issue for System.Security.Premisions (git #203)
|
||||
Refactored AuthorityResolver to discover AAD authentication authorities to allow for direct access by other clients.
|
||||
|
||||
|
||||
0.5.10:
|
||||
Added new property "RecommendedDegreesOfParallelism".
|
||||
This property will report the recommended number of threads for communicating with Dataverse.
|
||||
|
||||
|
|
|
@ -58,10 +58,10 @@
|
|||
<dependency id="System.Reflection.TypeExtensions" version="4.7.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.Runtime.Serialization.Primitives" version="4.3.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.Runtime.Serialization.Xml" version="4.3.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.Security.Permissions" version="4.7.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.ServiceModel.Duplex" version="4.7.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.ServiceModel.Http" version="4.7.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.ServiceModel.Primitives" version="4.7.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.Security.Permissions" version="5.0.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.ServiceModel.Duplex" version="4.8.1" exclude="Build,Analyzers" />
|
||||
<dependency id="System.ServiceModel.Http" version="4.8.1" exclude="Build,Analyzers" />
|
||||
<dependency id="System.ServiceModel.Primitives" version="4.8.1" exclude="Build,Analyzers" />
|
||||
<dependency id="System.Text.Json" version="5.0.2" exclude="Build,Analyzers" />
|
||||
<dependency id="Microsoft.Extensions.DependencyInjection" version="3.1.8" exclude="Build,Analyzers" />
|
||||
<dependency id="Microsoft.Extensions.Http" version="3.1.8" exclude="Build,Analyzers" />
|
||||
|
@ -88,10 +88,10 @@
|
|||
<dependency id="System.Reflection.TypeExtensions" version="4.7.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.Runtime.Serialization.Primitives" version="4.3.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.Runtime.Serialization.Xml" version="4.3.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.Security.Permissions" version="4.7.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.ServiceModel.Duplex" version="4.7.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.ServiceModel.Http" version="4.7.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.ServiceModel.Primitives" version="4.7.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.Security.Permissions" version="5.0.0" exclude="Build,Analyzers" />
|
||||
<dependency id="System.ServiceModel.Duplex" version="4.8.1" exclude="Build,Analyzers" />
|
||||
<dependency id="System.ServiceModel.Http" version="4.8.1" exclude="Build,Analyzers" />
|
||||
<dependency id="System.ServiceModel.Primitives" version="4.8.1" exclude="Build,Analyzers" />
|
||||
<dependency id="System.Text.Json" version="5.0.2" exclude="Build,Analyzers" />
|
||||
<dependency id="Microsoft.Extensions.DependencyInjection" version="3.1.8" exclude="Build,Analyzers" />
|
||||
<dependency id="Microsoft.Extensions.Http" version="3.1.8" exclude="Build,Analyzers" />
|
||||
|
|
Загрузка…
Ссылка в новой задаче