Skip to content

Commit 436f2a7

Browse files
committed
Sync up for 0.6 release
1 parent 07b3400 commit 436f2a7

File tree

111 files changed

+11937
-7357
lines changed

Some content is hidden

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

111 files changed

+11937
-7357
lines changed

.vscode/launch.json

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
// Use IntelliSense to find out which attributes exist for C# debugging
6+
// Use hover for the description of the existing attributes
7+
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
8+
"name": ".NET Core Launch (console)",
9+
"type": "coreclr",
10+
"request": "launch",
11+
"preLaunchTask": "build",
12+
// If you have changed target frameworks, make sure to update the program path.
13+
"program": "${workspaceFolder}/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/bin/Debug/netcoreapp3.1/LivePackageTestsConsole.dll",
14+
"args": [],
15+
"cwd": "${workspaceFolder}/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole",
16+
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
17+
"console": "internalConsole",
18+
"stopAtEntry": false
19+
},
20+
{
21+
"name": ".NET Core Attach",
22+
"type": "coreclr",
23+
"request": "attach"
24+
}
25+
]
26+
}

.vscode/tasks.json

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"label": "build",
6+
"command": "dotnet",
7+
"type": "process",
8+
"args": [
9+
"build",
10+
"${workspaceFolder}/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/LivePackageTestsConsole.csproj",
11+
"/property:GenerateFullPaths=true",
12+
"/consoleloggerparameters:NoSummary"
13+
],
14+
"problemMatcher": "$msCompile"
15+
},
16+
{
17+
"label": "publish",
18+
"command": "dotnet",
19+
"type": "process",
20+
"args": [
21+
"publish",
22+
"${workspaceFolder}/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/LivePackageTestsConsole.csproj",
23+
"/property:GenerateFullPaths=true",
24+
"/consoleloggerparameters:NoSummary"
25+
],
26+
"problemMatcher": "$msCompile"
27+
},
28+
{
29+
"label": "watch",
30+
"command": "dotnet",
31+
"type": "process",
32+
"args": [
33+
"watch",
34+
"run",
35+
"${workspaceFolder}/src/GeneralTools/DataverseClient/UnitTests/LivePackageTestsConsole/LivePackageTestsConsole.csproj",
36+
"/property:GenerateFullPaths=true",
37+
"/consoleloggerparameters:NoSummary"
38+
],
39+
"problemMatcher": "$msCompile"
40+
}
41+
]
42+
}

README.md

+9-10
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Current release notes and change log:
99

1010
[Microsoft.PowerPlatform.Dataverse.Client.Dynamics](src/nuspecs/Microsoft.PowerPlatform.Dataverse.Client.Dynamics.ReleaseNotes.txt)
1111

12-
[Microsoft.Dynamics.Sdk.Messages](src/nuspecs/Microsoft.Dynamics.Sdk.Messages.ReleaseNotes.txt)
12+
This nuget package has been deprecated (for now) ~~[Microsoft.Dynamics.Sdk.Messages](src/nuspecs/Microsoft.Dynamics.Sdk.Messages.ReleaseNotes.txt)~~
1313

1414
## Overview
1515
This repository contains the code for the Microsoft.PowerPlatform.Dataverse.Client and its supporting assemblies and classes.
@@ -27,34 +27,33 @@ This encompasses the contents of the following nuget packages:
2727

2828
[Microsoft.PowerPlatform.Dataverse.Client.Dynamics](https://www.nuget.org/packages/Microsoft.PowerPlatform.Dataverse.Client.Dynamics)
2929

30-
[Microsoft.Dynamics.Sdk.Messages](https://www.nuget.org/packages/Microsoft.Dynamics.Sdk.Messages)
30+
This nuget package has been deprecated (for now) ~~[Microsoft.Dynamics.Sdk.Messages](https://www.nuget.org/packages/Microsoft.Dynamics.Sdk.Messages)~~
3131

3232

3333
This library is and its supporting assemblies are a revision and update of the Microsoft.Xrm.Tooling.Connector.CrmServiceClient and the underlying Microsoft.Xrm.Sdk.Client libraries.
3434

3535
We are using this effort to for a few key things we have wanted to get done for a number of years,
3636

37-
1. Refactor and update our client libraries to allow us to spit up Powerplatform Common Data Service SDK support from Microsoft Dynamics 365.
37+
1. Refactor and update our client libraries to allow us to spit up PowerPlatform Common Data Service SDK support from Microsoft Dynamics 365.
3838
2. Provide multi targeted library build that targets our supported .net client platforms.
3939
3. Update connection patterns and behaviors to be consistent with many of the broadly accepted patterns.
40-
4. Create a pattern to allow developers focus on the use of Common Data Service, or CDS + Dynamics as they need.
40+
4. Create a pattern to allow developers focus on the use of Dataverse, or Dataverse + Dynamics as they need.
4141

4242
We encourage you to read the release notes we provide with each nuget packages. As with most of our Nuget packages that are intended as tools or for developer consumption, we extensively comment in release notes.
4343

44-
At this time: (08/16/2021)
44+
At this time: (03/06/2022)
4545
The Client SDK libs supports the following and has the following notices:
4646

47+
* 0.6.x is **expected** to be the final preview release, followed by 1.0
48+
* 0.6.x refactors much of the primary ServiceClient interface, narrowing its focus to primary operations against Dataverse. the ballance of the feature set has been moved to Microsoft.PowerPlatform.Dataverse.Client.Extensions. - We are seeking feedback on this refactor.
4749
* .net full framework 4.6.2, 4.7.2, 4.8 and .net core 3.0, 3.1, 5.0, 6.0
48-
* We now support all authentication types from CrmServiceClient, ( Client\Secret, Client\Cert, UID\PW Noninteractive, UID\PW interactive.)
50+
* We now support all authentication types from CrmServiceClient for .net framework, ( Client\Secret, Client\Cert, UID\PW Noninteractive, UID\PW interactive.)
51+
* We support the following authentication types from CrmServiceClient for .net core: Client\Secret, Client\Cert, UID\PW interactive.
4952
* MSAL Port has been completed, this Lib is now using MSAL 4.35+
50-
* The Message types that are part of the client have been reduced to Dataverse Core server messages only. Things like “QualifyLeadRequest” have been removed to their own Nuget package ( Microsoft.Dynamics.Sdk.Messages )
51-
* We will likely ship more extension packages that will contain the “CRM” messages, though over time, we will likely split the namespaces of those messages up based on service line, think Field Service or Sales or Customer Service, etc..
5253
* Plugin Development using this Client is NOT supported at this time.
5354

5455
From a scenario point of view, we are particularity interested in any issues or challenges when using these library in either Asp.net Core, Azure Functions, and Linux based scenarios.
5556

56-
We believe for the vast majority of applications working against the older dynamices sdk libs, you should only need Microsoft.PowerPlatform.Dataverse.Client and Microsoft.Dynamics.Sdk.Messages.
57-
5857
If your working against Dataverse Only and or custom entities and sdk messages, you should only need Microsoft.PowerPlatform.Dataverse.Client. If you do not experience that, or find missing messages in the Dataverse only scenarios, please let us know in the issues area.
5958

6059
<b>Note: We are currently providing support for these nuget packages primarily via GitHub and Microsoft Support.

src/Build.Common.core.props

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
</PropertyGroup>
66

77
<PropertyGroup Condition="'$(ProjectSpecificFx)' == ''">
8-
<TargetFrameworks>net462;net472;net48;netcoreapp3.0;netcoreapp3.1;netstandard2.0</TargetFrameworks>
8+
<TargetFrameworks>net462;net472;net48;netcoreapp3.1;netstandard2.0</TargetFrameworks>
99
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
1010
</PropertyGroup>
1111

src/Build.Common.props

+2
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,7 @@
77

88
<Import Project=".\Build.Shared.props" />
99

10+
<!-- TODO: Import Microsoft.Common.props BEFORE any other shared .props files in order to ensure correct ordering
11+
semantics that exist between Sdk-style projects and non-Sdk-style projects for Directory.Build.props files. -->
1012
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
1113
</Project>

src/Build.Shared.props

+20-6
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
<PackageVersion_AppInsights>2.9.1</PackageVersion_AppInsights>
66
<PackageVersion_Adal>3.19.8</PackageVersion_Adal>
77
<PackageVersion_MSAL>4.35.1</PackageVersion_MSAL>
8-
<PackageVersion_CdsSdk>4.6.6061-weekly-2108.5</PackageVersion_CdsSdk>
8+
<PackageVersion_CdsSdk>4.7.7698-v9.0-master.release</PackageVersion_CdsSdk>
9+
<PackageVersion_CrmProxy>4.7.7698-v9.0-master.release</PackageVersion_CrmProxy>
910
<PackageVersion_CDSServerNuget>4.6.6061-weekly-2108.5</PackageVersion_CDSServerNuget>
1011
<PackageVersion_CdsSdkProxy>4.7.6346-master</PackageVersion_CdsSdkProxy>
1112
<PackageVersion_Newtonsoft>11.0.2</PackageVersion_Newtonsoft>
@@ -35,6 +36,22 @@
3536
<GenerateAssemblyDescriptionAttribute>false</GenerateAssemblyDescriptionAttribute>
3637
</PropertyGroup>
3738

39+
<PropertyGroup>
40+
<!--
41+
Create a corse level define to identity .net framework vs .net core
42+
43+
This is to align with https://github.com/dotnet/designs/blob/main/accepted/2020/or-greater-defines/or-greater-defines.md
44+
which pre-defines some preprocessor symbols for source files.
45+
46+
Also this has to be defined in Directory.build.targets to ensure it is evaluated after evaluating all properties
47+
in .csproj files.
48+
-->
49+
<NETFRAMEWORK Condition="$(TargetFramework.ToLower().StartsWith('net4'))">true</NETFRAMEWORK>
50+
<NETFRAMEWORK Condition="!$(TargetFramework.ToLower().StartsWith('net4'))">false</NETFRAMEWORK>
51+
<NETFRAMEWORK Condition="'$(NETFRAMEWORK)'==''">true</NETFRAMEWORK>
52+
</PropertyGroup>
53+
54+
3855
<PropertyGroup>
3956
<!-- this property must be re-defined in individual .csprojs or a .props file per component area -->
4057
<ComponentAreaName Condition="'$(ComponentAreaName)' == ''">FORGOT-To-Set-ComponentAreaName</ComponentAreaName>
@@ -43,7 +60,7 @@
4360
<PropertyGroup>
4461
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
4562
<!-- These variables define the object and binary roots, respectively. -->
46-
<RepoRoot>$([System.IO.Path]::GetFullPath($([System.IO.Path]::Combine($(MSBuildThisFileDirectory), ".."))))</RepoRoot>
63+
<RepoRoot Condition="'$(RepoRoot)' == ''">$([System.IO.Path]::GetFullPath($([System.IO.Path]::Combine($(MSBuildThisFileDirectory), ".."))))</RepoRoot>
4764
<OutputRootDir Condition=" '$(OutputRootDir)' == '' ">$(RepoRoot)\bin\$(Configuration)\$(ComponentAreaName)\$(TargetFramework)</OutputRootDir>
4865
<OutputRootDir>$(OutputRootDir.TrimEnd({'\\'}))</OutputRootDir>
4966
<!-- These variables are the ones that the standard MSBuild targets recognize. -->
@@ -63,6 +80,7 @@
6380
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
6481
<FileAlignment>512</FileAlignment>
6582
<Deterministic>true</Deterministic>
83+
<NoWarn>$(NoWarn);CS8032</NoWarn>
6684
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
6785
<ErrorReport>prompt</ErrorReport>
6886
<WarningLevel>4</WarningLevel>
@@ -102,8 +120,4 @@
102120
<DefineConstants>DEBUG;TRACE;CRMINTERNAL</DefineConstants>
103121
<Optimize>false</Optimize>
104122
</PropertyGroup>
105-
106-
<PropertyGroup Condition=" '$(Enable_Telemetry)' == 'true' " >
107-
<DefineConstants>$(DefineConstants);PROD_CUSTOMER_TELEMETRY</DefineConstants>
108-
</PropertyGroup>
109123
</Project>

src/Directory.Build.props

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project>
2+
<!-- See: https://docs.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2019#directorybuildprops-and-directorybuildtargets -->
3+
<Import Project="$([MSBuild]::GetPathOfFileAbove($(MSBuildThisFile), '$(MSBuildThisFileDirectory)../'))" />
4+
5+
<PropertyGroup>
6+
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
7+
</PropertyGroup>
8+
9+
<PropertyGroup>
10+
<!-- These variables define the object and binary roots, respectively. -->
11+
<!-- The parent area folder name is intended to resolve potentially duplicate MSBuildProjectName's across the repo -->
12+
<_parentAreaFolderName>$([System.IO.Path]::GetFileName( $([System.IO.Path]::GetFullPath( $([System.IO.Path]::Combine($(MSBuildProjectDirectory), "..")) )) ))</_parentAreaFolderName>
13+
<!-- Setting the base obj folder to the root of the repo to help avoid build errors due to extremely long file paths -->
14+
<BaseIntermediateOutputPath>$(RepoRoot)\obj\$(_parentAreaFolderName)\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
15+
</PropertyGroup>
16+
</Project>

src/GeneralTools/DataverseClient/Client/Auth/AuthProcessor.cs

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
using Microsoft.Extensions.DependencyInjection;
22
using Microsoft.Identity.Client;
33
using Microsoft.PowerPlatform.Dataverse.Client.Auth.TokenCache;
4+
using Microsoft.PowerPlatform.Dataverse.Client.InternalExtensions;
45
using Microsoft.PowerPlatform.Dataverse.Client.Utils;
5-
using Microsoft.Xrm.Sdk.WebServiceClient;
66
using System;
77
using System.Collections.Generic;
88
using System.Diagnostics;
@@ -452,12 +452,13 @@ internal static UriBuilder GetUriBuilderWithVersion(Uri discoveryServiceUri)
452452
/// <param name="targetServiceUrl">URI to query</param>
453453
/// <param name="logger">Logger to write info too</param>
454454
/// <param name="clientFactory">HTTP Client factory to use for this request.</param>
455+
/// <param name="isOnPrem">if true, login is for an onprem server</param>
455456
/// <returns></returns>
456-
private static async Task<AuthenticationDetails> GetAuthorityFromTargetServiceAsync(IHttpClientFactory clientFactory, Uri targetServiceUrl, DataverseTraceLogger logger)
457+
private static async Task<AuthenticationDetails> GetAuthorityFromTargetServiceAsync(IHttpClientFactory clientFactory, Uri targetServiceUrl, DataverseTraceLogger logger, bool isOnPrem = false)
457458
{
458459
var client = clientFactory.CreateClient("DataverseHttpClientFactory");
459460
var resolver = new AuthorityResolver(client, (t, msg) => logger.Log(msg, t));
460-
return await resolver.ProbeForExpectedAuthentication(targetServiceUrl);
461+
return await resolver.ProbeForExpectedAuthentication(targetServiceUrl, isOnPrem);
461462
}
462463

463464
/// <summary>

src/GeneralTools/DataverseClient/Client/Auth/AuthorityResolver.cs

+47-37
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,11 @@ public AuthorityResolver(HttpClient httpClient, Action<TraceEventType, string> l
6060
/// Attemtps to solicit a WWW-Authenticate reply using an unauthenticated GET call to the given endpoint.
6161
/// Parses returned header for details
6262
/// </summary>
63-
/// <param name="endpoint"></param>
63+
/// <param name="endpoint">endpoint to challenge for authority and resource</param>
64+
/// <param name="isOnPrem">if true, this is an OnPremsies server</param>
6465
/// <returns></returns>
6566
/// <exception cref="ArgumentNullException"></exception>
66-
public async Task<AuthenticationDetails> ProbeForExpectedAuthentication(Uri endpoint)
67+
public async Task<AuthenticationDetails> ProbeForExpectedAuthentication(Uri endpoint, bool isOnPrem = false)
6768
{
6869
_ = endpoint ?? throw new ArgumentNullException(nameof(endpoint));
6970
var details = new AuthenticationDetails();
@@ -94,53 +95,62 @@ public async Task<AuthenticationDetails> ProbeForExpectedAuthentication(Uri endp
9495

9596
if (response.Headers.Contains(AuthenticateHeader))
9697
{
97-
var authenticateHeader = response.Headers.GetValues(AuthenticateHeader).FirstOrDefault();
98-
authenticateHeader = authenticateHeader.Trim();
99-
100-
// This also checks for cases like "BearerXXXX authorization_uri=...." and "Bearer" and "Bearer "
101-
if (!authenticateHeader.StartsWith(Bearer, StringComparison.OrdinalIgnoreCase)
102-
|| authenticateHeader.Length < Bearer.Length + 2
103-
|| !char.IsWhiteSpace(authenticateHeader[Bearer.Length]))
98+
var authenticateHeaders = response.Headers.GetValues(AuthenticateHeader);
99+
// need to support OnPrem returning multiple Authentication headers.
100+
foreach (var authenticateHeaderraw in authenticateHeaders)
104101
{
105-
LogError($"Malformed 'Bearer' format: {authenticateHeader}");
106-
return details;
107-
}
102+
if (details.Success)
103+
break;
108104

109-
authenticateHeader = authenticateHeader.Substring(Bearer.Length).Trim();
110-
111-
IDictionary<string, string> authenticateHeaderItems = null;
112-
try
113-
{
114-
authenticateHeaderItems =
115-
EncodingHelper.ParseKeyValueListStrict(authenticateHeader, ',', false, true);
116-
}
117-
catch (ArgumentException)
118-
{
119-
LogError($"Malformed arguments in '{AuthenticateHeader}: {authenticateHeader}");
120-
return details;
121-
}
105+
string authenticateHeader = authenticateHeaderraw.Trim();
122106

123-
if (authenticateHeaderItems != null)
124-
{
125-
if (!authenticateHeaderItems.TryGetValue(AuthorityKey, out var auth))
107+
// This also checks for cases like "BearerXXXX authorization_uri=...." and "Bearer" and "Bearer "
108+
if (!authenticateHeader.StartsWith(Bearer, StringComparison.OrdinalIgnoreCase)
109+
|| authenticateHeader.Length < Bearer.Length + 2
110+
|| !char.IsWhiteSpace(authenticateHeader[Bearer.Length]))
126111
{
127-
LogError($"Response header from {endpoint} is missing expected key/value for {AuthorityKey}");
112+
if (isOnPrem)
113+
continue;
114+
115+
LogError($"Malformed 'Bearer' format: {authenticateHeader}");
128116
return details;
129117
}
130-
details.Authority = new Uri(
131-
auth.Replace("oauth2/authorize", "") // swap out the old oAuth pattern.
132-
.Replace("common", "organizations")); // swap common for organizations because MSAL reasons.
133118

134-
if (!authenticateHeaderItems.TryGetValue(ResourceKey, out var res))
119+
authenticateHeader = authenticateHeader.Substring(Bearer.Length).Trim();
120+
121+
IDictionary<string, string> authenticateHeaderItems = null;
122+
try
123+
{
124+
authenticateHeaderItems =
125+
EncodingHelper.ParseKeyValueListStrict(authenticateHeader, ',', false, true);
126+
}
127+
catch (ArgumentException)
135128
{
136-
LogError($"Response header from {endpoint} is missing expected key/value for {ResourceKey}");
129+
LogError($"Malformed arguments in '{AuthenticateHeader}: {authenticateHeader}");
137130
return details;
138131
}
139-
details.Resource = new Uri(res);
140-
details.Success = true;
132+
133+
if (authenticateHeaderItems != null)
134+
{
135+
if (!authenticateHeaderItems.TryGetValue(AuthorityKey, out var auth))
136+
{
137+
LogError($"Response header from {endpoint} is missing expected key/value for {AuthorityKey}");
138+
return details;
139+
}
140+
details.Authority = new Uri(
141+
auth.Replace("oauth2/authorize", "") // swap out the old oAuth pattern.
142+
.Replace("common", "organizations")); // swap common for organizations because MSAL reasons.
143+
144+
if (!authenticateHeaderItems.TryGetValue(ResourceKey, out var res))
145+
{
146+
LogError($"Response header from {endpoint} is missing expected key/value for {ResourceKey}");
147+
return details;
148+
}
149+
details.Resource = new Uri(res);
150+
details.Success = true;
151+
}
141152
}
142153
}
143-
144154
return details;
145155
}
146156

0 commit comments

Comments
 (0)