Skip to content

Commit 035dd6d

Browse files
authored
Merge pull request #120 from microsoft/release/update/210413103754
Bug Fix's and updates for Import Solution.
2 parents 9f25d39 + 00a74be commit 035dd6d

File tree

9 files changed

+177
-21
lines changed

9 files changed

+177
-21
lines changed

src/Build.Shared.props

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
<PackageVersion_AppInsights>2.9.1</PackageVersion_AppInsights>
66
<PackageVersion_Adal>3.19.8</PackageVersion_Adal>
77
<PackageVersion_MSAL>4.25.0</PackageVersion_MSAL>
8-
<PackageVersion_CdsSdk>4.6.875-weekly-2011.2</PackageVersion_CdsSdk>
9-
<PackageVersion_CDSServerNuget>4.6.875-weekly-2011.2</PackageVersion_CDSServerNuget>
8+
<PackageVersion_CdsSdk>4.6.3082-weekly-2103.4</PackageVersion_CdsSdk>
9+
<PackageVersion_CDSServerNuget>4.6.3082-weekly-2103.4</PackageVersion_CDSServerNuget>
1010
<PackageVersion_Newtonsoft>10.0.3</PackageVersion_Newtonsoft>
1111
<PackageVersion_RestClientRuntime>2.3.20</PackageVersion_RestClientRuntime>
1212
<PackageVersion_XrmSdk>9.0.2.25</PackageVersion_XrmSdk>
13+
<PackageVersion_BatchedTelemetry>2.0.5</PackageVersion_BatchedTelemetry>
1314
<!-- Test: -->
1415
<PackageVersion_Moq>4.16.0</PackageVersion_Moq>
1516
<PackageVersion_XUnit>2.4.1</PackageVersion_XUnit>

src/GeneralTools/DataverseClient/Client/MetadataUtility.cs

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Microsoft.Xrm.Sdk.Metadata;
55
using Microsoft.Xrm.Sdk.Messages;
66
using System.Collections.Concurrent;
7+
using Microsoft.PowerPlatform.Dataverse.Client.Utils;
78

89
namespace Microsoft.PowerPlatform.Dataverse.Client
910
{
@@ -22,7 +23,7 @@ internal class MetadataUtility
2223
/// </summary>
2324
private ConcurrentDictionary<String, AttributeMetadata> _attributeMetadataCache = new ConcurrentDictionary<String, AttributeMetadata>();
2425
/// <summary>
25-
/// Global option metadata cache object.
26+
/// Global option metadata cache object.
2627
/// </summary>
2728
private ConcurrentDictionary<String, OptionSetMetadata> _globalOptionMetadataCache = new ConcurrentDictionary<String, OptionSetMetadata>();
2829
/// <summary>
@@ -34,7 +35,7 @@ internal class MetadataUtility
3435
/// </summary>
3536
private static Object _lockObject = new Object();
3637
/// <summary>
37-
/// Last time Entity data was validated.
38+
/// Last time Entity data was validated.
3839
/// </summary>
3940
private DateTime _metadataLastValidatedAt;
4041

@@ -54,7 +55,7 @@ public MetadataUtility(ServiceClient svcActions)
5455
public void ClearCachedEntityMetadata(string entityName)
5556
{
5657
TouchMetadataDate();
57-
// Not clearing the ETC ID's as they do not change...
58+
// Not clearing the ETC ID's as they do not change...
5859
if (_entityMetadataCache.ContainsKey(entityName))
5960
{
6061
EntityMetadata removedEntData;
@@ -88,32 +89,39 @@ public List<EntityMetadata> GetAllEntityMetadata(bool onlyPublished, EntityFilte
8889
foreach (var entity in response.EntityMetadata)
8990
{
9091
if (_entityMetadataCache.ContainsKey(entity.LogicalName))
91-
_entityMetadataCache[entity.LogicalName] = entity; // Update local copy of the entity...
92+
_entityMetadataCache[entity.LogicalName] = entity; // Update local copy of the entity...
9293
else
9394
_entityMetadataCache.TryAdd(entity.LogicalName, entity);
9495

9596
results.Add(entity);
96-
// Preload the entity data catch as this has been called already
97+
// Preload the entity data catch as this has been called already
9798
if (_entityNameCache.ContainsKey(entity.ObjectTypeCode.Value))
9899
continue;
99100
else
100101
_entityNameCache.TryAdd(entity.ObjectTypeCode.Value, entity.LogicalName);
101102
}
102103
TouchMetadataDate();
103104
}
105+
else
106+
{
107+
if (svcAct.LastException != null)
108+
throw new DataverseOperationException($"Failed to get metadata from Dataverse.", svcAct.LastException);
109+
else
110+
throw new DataverseOperationException($"Failed to get metadata from Dataverse", null);
111+
}
104112

105113
return results;
106114
}
107115

108116
/// <summary>
109-
/// Returns Entity Metadata for requested entity.
117+
/// Returns Entity Metadata for requested entity.
110118
/// Applies returns all data available based on CRM version type
111119
/// </summary>
112120
/// <param name="entityName">Name of the Entity, data is being requested on</param>
113121
/// <returns>Entity data</returns>
114122
public EntityMetadata GetEntityMetadata(string entityName)
115123
{
116-
// Filter the EntityFitlers based on the version of CRM being connected too.
124+
// Filter the EntityFitlers based on the version of CRM being connected too.
117125
if (svcAct.ConnectedOrgVersion < Version.Parse("7.1.0.0"))
118126
return GetEntityMetadata(EntityFilters.Attributes | EntityFilters.Entity | EntityFilters.Privileges | EntityFilters.Relationships , entityName);
119127
else
@@ -180,6 +188,13 @@ public EntityMetadata GetEntityMetadata(EntityFilters requestType, String entity
180188
if (!bSelectiveUpdate)
181189
TouchMetadataDate();
182190
}
191+
else
192+
{
193+
if (svcAct.LastException != null)
194+
throw new DataverseOperationException($"Failed to resolve entity metadata for {entityName}.", svcAct.LastException);
195+
else
196+
throw new DataverseOperationException($"Failed to resolve entity metadata for {entityName}.", null);
197+
}
183198
}
184199
return entityMetadata;
185200
}
@@ -208,22 +223,29 @@ public string GetEntityLogicalName(int entityTypeCode)
208223
{
209224
_entityNameCache.TryAdd(metadata.ObjectTypeCode.Value, metadata.LogicalName);
210225

211-
// reload metadata cache.
226+
// reload metadata cache.
212227
if (_entityMetadataCache.ContainsKey(metadata.LogicalName))
213228
continue;
214229
else
215230
_entityMetadataCache.TryAdd(metadata.LogicalName, metadata);
216231
}
217232
TouchMetadataDate();
218233
}
234+
else
235+
{
236+
if (svcAct.LastException != null)
237+
throw new DataverseOperationException($"Failed to resolve entity name from typecode {entityTypeCode}.", svcAct.LastException);
238+
else
239+
throw new DataverseOperationException($"Failed to resolve entity name from typecode {entityTypeCode}.", null);
240+
}
219241
}
220242
}
221243
_entityNameCache.TryGetValue(entityTypeCode, out name);
222244
return name;
223245
}
224246

225247
/// <summary>
226-
///
248+
///
227249
/// </summary>
228250
/// <param name="entityName"></param>
229251
/// <param name="attributeName"></param>
@@ -249,13 +271,20 @@ public AttributeMetadata GetAttributeMetadata(string entityName, string attribut
249271
_attributeMetadataCache.TryAdd(String.Format(CultureInfo.InvariantCulture, "{0}.{1}", entityName, attributeName), attributeMetadata);
250272
_metadataLastValidatedAt = DateTime.UtcNow;
251273
}
274+
else
275+
{
276+
if (svcAct.LastException != null)
277+
throw new DataverseOperationException($"Failed to resolve attribute metadata for {attributeName} in entity {entityName}.", svcAct.LastException);
278+
else
279+
throw new DataverseOperationException($"Failed to resolve attribute metadata for {attributeName} in entity {entityName}.", null);
280+
}
252281
}
253282
}
254283
return attributeMetadata;
255284
}
256285

257286
/// <summary>
258-
///
287+
///
259288
/// </summary>
260289
/// <param name="entityName"></param>
261290
/// <returns></returns>
@@ -264,7 +293,7 @@ public List<AttributeMetadata> GetAllAttributesMetadataByEntity(string entityNam
264293
EntityMetadata entityMetadata = GetEntityMetadata(entityName);
265294
if (entityMetadata != null)
266295
{
267-
// Added to deal with failed call to CRM.
296+
// Added to deal with failed call to CRM.
268297
if (entityMetadata.Attributes != null)
269298
{
270299
List<AttributeMetadata> results = new List<AttributeMetadata>();
@@ -281,7 +310,7 @@ public List<AttributeMetadata> GetAllAttributesMetadataByEntity(string entityNam
281310
}
282311

283312
/// <summary>
284-
///
313+
///
285314
/// </summary>
286315
/// <param name="entityName"></param>
287316
/// <returns></returns>
@@ -304,7 +333,7 @@ public List<String> GetRequiredAttributesByEntity(string entityName)
304333
}
305334

306335
/// <summary>
307-
/// Retrieve Global OptionSet Information.
336+
/// Retrieve Global OptionSet Information.
308337
/// </summary>
309338
/// <param name="optionSetName"></param>
310339
/// <returns></returns>
@@ -313,7 +342,7 @@ public OptionSetMetadata GetGlobalOptionSetMetadata(string optionSetName)
313342
if (string.IsNullOrEmpty(optionSetName))
314343
return null;
315344

316-
ValidateMetadata(); // Check to see if Metadata has expired.
345+
ValidateMetadata(); // Check to see if Metadata has expired.
317346

318347
if (_globalOptionMetadataCache.ContainsKey(optionSetName))
319348
return _globalOptionMetadataCache[optionSetName];
@@ -332,11 +361,18 @@ public OptionSetMetadata GetGlobalOptionSetMetadata(string optionSetName)
332361
return _globalOptionMetadataCache[optionSetName];
333362
}
334363
}
364+
else
365+
{
366+
if (svcAct.LastException != null)
367+
throw new DataverseOperationException($"Failed to resolve global optionset metadata for {optionSetName}.", svcAct.LastException);
368+
else
369+
throw new DataverseOperationException($"Failed to resolve global optionset metadata for {optionSetName}.", null);
370+
}
335371
return null;
336372
}
337373

338374
/// <summary>
339-
///
375+
///
340376
/// </summary>
341377
private void ValidateMetadata()
342378
{

src/GeneralTools/DataverseClient/Client/ServiceClient.cs

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,29 @@ public string CurrentAccessToken
297297
}
298298
}
299299

300+
/// <summary>
301+
/// Defaults to True.
302+
/// <para>When true, this setting applies the default connection routing strategy to connections to Dataverse.</para>
303+
/// <para>This will 'prefer' a given node when interacting with Dataverse which improves overall connection performance.</para>
304+
/// <para>When set to false, each call to Dataverse will be routed to any given node supporting your organization. </para>
305+
/// <para>See https://docs.microsoft.com/en-us/powerapps/developer/data-platform/api-limits#remove-the-affinity-cookie for proper use.</para>
306+
/// </summary>
307+
public bool EnableAffinityCookie
308+
{
309+
get
310+
{
311+
if (_connectionSvc != null)
312+
return _connectionSvc.EnableCookieRelay;
313+
else
314+
return true;
315+
}
316+
set
317+
{
318+
if (_connectionSvc != null)
319+
_connectionSvc.EnableCookieRelay = value;
320+
}
321+
}
322+
300323
/// <summary>
301324
/// Pointer to Dataverse Service.
302325
/// </summary>
@@ -5303,12 +5326,18 @@ internal Guid ImportSolutionToImpl(string solutionPath, out Guid importId, bool
53035326
LayerDesiredOrder desiredLayerOrder = null;
53045327
bool? asyncRibbonProcessing = null;
53055328
EntityCollection componetsToProcess = null;
5329+
bool? convertToManaged = null;
5330+
bool? isTemplateModeImport = null;
5331+
string templateSuffix = null;
53065332

53075333
if (extraParameters != null)
53085334
{
53095335
solutionName = extraParameters.ContainsKey(ImportSolutionProperties.SOLUTIONNAMEPARAM) ? extraParameters[ImportSolutionProperties.SOLUTIONNAMEPARAM].ToString() : string.Empty;
53105336
desiredLayerOrder = extraParameters.ContainsKey(ImportSolutionProperties.DESIREDLAYERORDERPARAM) ? extraParameters[ImportSolutionProperties.DESIREDLAYERORDERPARAM] as LayerDesiredOrder : null;
53115337
componetsToProcess = extraParameters.ContainsKey(ImportSolutionProperties.COMPONENTPARAMETERSPARAM) ? extraParameters[ImportSolutionProperties.COMPONENTPARAMETERSPARAM] as EntityCollection : null;
5338+
convertToManaged = extraParameters.ContainsKey(ImportSolutionProperties.CONVERTTOMANAGED) ? extraParameters[ImportSolutionProperties.CONVERTTOMANAGED] as bool? : null;
5339+
isTemplateModeImport = extraParameters.ContainsKey(ImportSolutionProperties.ISTEMPLATEMODE) ? extraParameters[ImportSolutionProperties.ISTEMPLATEMODE] as bool? : null;
5340+
templateSuffix = extraParameters.ContainsKey(ImportSolutionProperties.TEMPLATESUFFIX) ? extraParameters[ImportSolutionProperties.TEMPLATESUFFIX].ToString() : string.Empty;
53125341

53135342
// Pick up the data from the request, if the request has the AsyncRibbonProcessing flag, pick up the value of it.
53145343
asyncRibbonProcessing = extraParameters.ContainsKey(ImportSolutionProperties.ASYNCRIBBONPROCESSING) ? extraParameters[ImportSolutionProperties.ASYNCRIBBONPROCESSING] as bool? : null;
@@ -5350,6 +5379,24 @@ internal Guid ImportSolutionToImpl(string solutionPath, out Guid importId, bool
53505379
}
53515380
}
53525381
}
5382+
5383+
if (isTemplateModeImport != null)
5384+
{
5385+
if (isConnectedToOnPrem)
5386+
{
5387+
this._logEntry.Log($"ImportSolution request contains {ImportSolutionProperties.ISTEMPLATEMODE} property. This is not valid for OnPremise deployments and will be removed", TraceEventType.Warning);
5388+
isTemplateModeImport = null;
5389+
}
5390+
else
5391+
{
5392+
if (!Utilities.FeatureVersionMinimums.IsFeatureValidForEnviroment(_connectionSvc?.OrganizationVersion, Utilities.FeatureVersionMinimums.AllowTemplateSolutionImport))
5393+
{
5394+
// Not supported on this version of Dataverse
5395+
this._logEntry.Log($"ImportSolution request contains {ImportSolutionProperties.ISTEMPLATEMODE} property. This request Dataverse version {Utilities.FeatureVersionMinimums.AllowTemplateSolutionImport.ToString()} or above. Current Dataverse version is {_connectionSvc?.OrganizationVersion}. This property will be removed", TraceEventType.Warning);
5396+
isTemplateModeImport = null;
5397+
}
5398+
}
5399+
}
53535400
}
53545401

53555402
string solutionNameForLogging = string.IsNullOrWhiteSpace(solutionName) ? string.Empty : string.Concat(solutionName, " - ");
@@ -5394,6 +5441,17 @@ internal Guid ImportSolutionToImpl(string solutionPath, out Guid importId, bool
53945441
SolutionImportRequest.ComponentParameters = componetsToProcess;
53955442
}
53965443

5444+
if (convertToManaged != null)
5445+
{
5446+
SolutionImportRequest.ConvertToManaged = convertToManaged.Value;
5447+
}
5448+
5449+
if (isTemplateModeImport != null && isTemplateModeImport.Value)
5450+
{
5451+
SolutionImportRequest.Parameters[ImportSolutionProperties.ISTEMPLATEMODE] = isTemplateModeImport.Value;
5452+
SolutionImportRequest.Parameters[ImportSolutionProperties.TEMPLATESUFFIX] = templateSuffix;
5453+
}
5454+
53975455
if (IsBatchOperationsAvailable)
53985456
{
53995457
// Support for features added in UR12
@@ -5891,10 +5949,6 @@ private void LogException(OrganizationRequest req, Exception ex, string errorStr
58915949
}
58925950
}
58935951

5894-
// Add authorization header.
5895-
if (!customHeaders.ContainsKey(Utilities.RequestHeaders.AUTHORIZATION_HEADER))
5896-
customHeaders.Add(Utilities.RequestHeaders.AUTHORIZATION_HEADER, new List<string>() { string.Format("Bearer {0}", await _connectionSvc.RefreshWebProxyClientTokenAsync()) });
5897-
58985952
// Add tracking headers
58995953
// Request id
59005954
if (!customHeaders.ContainsKey(Utilities.RequestHeaders.X_MS_CLIENT_REQUEST_ID))
@@ -5929,6 +5983,10 @@ private void LogException(OrganizationRequest req, Exception ex, string errorStr
59295983
HttpResponseMessage resp = null;
59305984
do
59315985
{
5986+
// Add authorization header. - Here to catch the situation where a token expires during retry.
5987+
if (!customHeaders.ContainsKey(Utilities.RequestHeaders.AUTHORIZATION_HEADER))
5988+
customHeaders.Add(Utilities.RequestHeaders.AUTHORIZATION_HEADER, new List<string>() { string.Format("Bearer {0}", await _connectionSvc.RefreshWebProxyClientTokenAsync()) });
5989+
59325990
logDt.Restart(); // start clock.
59335991

59345992
_logEntry.Log(string.Format(CultureInfo.InvariantCulture, "Execute Command - {0}{1}: RequestID={2} {3}",

src/GeneralTools/DataverseClient/Client/Utils/ImportSolutionProperties.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,18 @@ public static class ImportSolutionProperties
2727
/// Parameter used to pass a collection of component parameters to the import job.
2828
/// </summary>
2929
public static string COMPONENTPARAMETERSPARAM = "ComponentParameters";
30+
/// <summary>
31+
/// Direct the system to convert any matching unmanaged customizations into your managed solution
32+
/// </summary>
33+
public static string CONVERTTOMANAGED = "ConvertToManaged";
34+
/// <summary>
35+
/// Internal use only
36+
/// </summary>
37+
public static string TEMPLATESUFFIX = "TemplateSuffix";
38+
/// <summary>
39+
/// Internal use only
40+
/// </summary>
41+
public static string ISTEMPLATEMODE = "IsTemplateMode";
42+
3043
}
3144
}

src/GeneralTools/DataverseClient/Client/Utils/Utils.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,12 @@ internal static bool IsFeatureValidForEnviroment ( Version instanceVersion , Ver
797797
/// Minimum version supported for Passing Component data to Dataverse as part of solution deployment..
798798
/// </summary>
799799
internal static Version AllowComponetInfoProcessing = new Version("9.1.0.16547");
800+
801+
/// <summary>
802+
/// Minimum version support for Solution tagging.
803+
/// </summary>
804+
internal static Version AllowTemplateSolutionImport = new Version("9.2.21013.00131");
805+
800806
}
801807

802808

0 commit comments

Comments
 (0)