Skip to content

Commit ecf8d3b

Browse files
Hamza AssyadRomainMuller
authored andcommitted
fix(dotnet): fix deep type conversion across the process boundary, intelisense docs, set target to netcoreapp2.1 (#772)
This PR fixes various issues due to how we convert collection elements, and how the type reference is passed when converting. These two issues are similar and are due to nested Arrays of object not being casted properly: Fixes aws/aws-cdk#3244 Fixes aws/aws-cdk#3672 Added a unit test to cover the case This issue is due to Maps of Arrays of Anys not being casted properly: Fixes aws/aws-cdk#3813 Added a unit test to cover the case Also added support for xml documentation which is now added to the NuGet packages and should allow intellisense to provide in IDE help: Fixes #749 Fixes aws/aws-cdk#1846 Migrated the target framework to netcoreapp2.1 instead of netstandard2.0: Fixes #714
1 parent 2e10060 commit ecf8d3b

File tree

7 files changed

+167
-18
lines changed

7 files changed

+167
-18
lines changed

packages/jsii-dotnet-runtime-test/test/Amazon.JSII.Runtime.IntegrationTests/Amazon.JSII.Runtime.IntegrationTests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>netcoreapp2.0</TargetFramework>
4+
<TargetFramework>netcoreapp2.1</TargetFramework>
55

66
<IsPackable>false</IsPackable>
77
</PropertyGroup>

packages/jsii-dotnet-runtime/src/Amazon.JSII.Runtime.UnitTests/Deputy/Converters/FrameworkToJsiiConverterTests.cs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,118 @@ public void RecursivelyConvertsArrayElements()
450450
}
451451
);
452452
}
453+
454+
[Fact(DisplayName = _Prefix + nameof(RecursivelyConvertsMapElementsWithMapOfAny))]
455+
public void RecursivelyConvertsMapElementsWithMapOfAny()
456+
{
457+
var instance = new OptionalValue(new TypeReference(
458+
collection: new CollectionTypeReference(CollectionKind.Map,
459+
new TypeReference(
460+
collection: new CollectionTypeReference(CollectionKind.Map,
461+
new TypeReference(primitive: PrimitiveType.Any)
462+
)
463+
)
464+
)
465+
));
466+
467+
var frameworkMap = new Dictionary<string, IDictionary<string, object>>
468+
{
469+
{ "myKey1", new Dictionary<string, object> { { "mySubKey1", "myValue1" } } },
470+
{ "myKey2", new Dictionary<string, object> { { "mySubKey2", "myValue2" } } },
471+
};
472+
473+
// This will test the call to FrameworkToJsiiConverter.TryConvertCollectionElement()
474+
// In the case of a of a Map of Map of Any
475+
bool success = _converter.TryConvert(instance, _referenceMap, frameworkMap, out object actual);
476+
477+
Assert.True(success);
478+
Assert.IsType<JObject>(actual);
479+
Assert.Collection
480+
(
481+
((IEnumerable<KeyValuePair<string, JToken>>)actual).OrderBy(kvp => kvp.Key),
482+
kvp =>
483+
{
484+
Assert.Equal("myKey1", kvp.Key);
485+
Assert.IsType<JObject>(kvp.Value);
486+
Assert.Collection
487+
(
488+
((IEnumerable<KeyValuePair<string, JToken>>)kvp.Value),
489+
subKvp =>
490+
{
491+
Assert.Equal("mySubKey1", subKvp.Key, ignoreLineEndingDifferences: true);
492+
Assert.Equal("myValue1", subKvp.Value);
493+
}
494+
);
495+
},
496+
kvp =>
497+
{
498+
Assert.Equal("myKey2", kvp.Key, ignoreLineEndingDifferences: true);
499+
Assert.IsType<JObject>(kvp.Value);
500+
Assert.Collection
501+
(
502+
((IEnumerable<KeyValuePair<string, JToken>>)kvp.Value),
503+
subKvp =>
504+
{
505+
Assert.Equal("mySubKey2", subKvp.Key, ignoreLineEndingDifferences: true);
506+
Assert.Equal("myValue2", subKvp.Value);
507+
}
508+
);
509+
}
510+
);
511+
}
512+
513+
[Fact(DisplayName = _Prefix + nameof(RecursivelyConvertsMapElementsWithArrayOfAny))]
514+
public void RecursivelyConvertsMapElementsWithArrayOfAny()
515+
{
516+
var instance = new OptionalValue(new TypeReference
517+
(
518+
collection: new CollectionTypeReference(CollectionKind.Map,
519+
new TypeReference
520+
(
521+
collection: new CollectionTypeReference(CollectionKind.Array,
522+
new TypeReference(primitive: PrimitiveType.Any)
523+
)
524+
)
525+
)
526+
));
527+
528+
var frameworkArray = new Dictionary<string, object>()
529+
{
530+
{"key", new [] { "true" }},
531+
{"key2", new [] { false }},
532+
};
533+
534+
// This will test the call to FrameworkToJsiiConverter.TryConvertCollectionElement()
535+
// In the case of a of a Map of Array of Any
536+
bool success = _converter.TryConvert(instance, _referenceMap, frameworkArray, out object actual);
537+
538+
Assert.True(success);
539+
Assert.IsType<JObject>(actual);
540+
Assert.Collection
541+
(
542+
((IEnumerable<KeyValuePair<string, JToken>>)actual).OrderBy(kvp => kvp.Key),
543+
kvp =>
544+
{
545+
Assert.Equal("key", kvp.Key);
546+
Assert.IsType<JArray>(kvp.Value);
547+
Assert.Collection
548+
(
549+
(JArray)kvp.Value,
550+
subValue => Assert.Equal("true", subValue)
551+
);
552+
},
553+
kvp =>
554+
{
555+
Assert.Equal("key2", kvp.Key, ignoreLineEndingDifferences: true);
556+
Assert.IsType<JArray>(kvp.Value);
557+
Assert.Collection
558+
(
559+
(JArray)kvp.Value,
560+
subValue => Assert.Equal(false, subValue)
561+
);
562+
}
563+
);
564+
}
453565

454566
[Fact(DisplayName = _Prefix + nameof(ConvertsNullMap))]
455567
public void ConvertsNullMap()

packages/jsii-dotnet-runtime/src/Amazon.JSII.Runtime/Services/Converters/FrameworkToJsiiConverter.cs

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ protected override bool TryConvertJson(object value, out object result)
157157
return true;
158158
}
159159

160-
if (value.GetType().IsAssignableFrom(typeof(JObject)))
160+
if (value.GetType().IsAssignableFrom(typeof(JObject)) || value.GetType().IsAssignableFrom(typeof(JArray)))
161161
{
162162
result = value;
163163
return true;
@@ -223,7 +223,7 @@ protected override bool TryConvertArray(IReferenceMap referenceMap, TypeReferenc
223223
JArray resultArray = new JArray();
224224
foreach (object element in array)
225225
{
226-
if (!TryConvert(elementType, referenceMap, element, out object convertedElement))
226+
if (!TryConvertCollectionElement(element, referenceMap, elementType, out object convertedElement))
227227
{
228228
result = null;
229229
return false;
@@ -264,15 +264,7 @@ protected override bool TryConvertMap(IReferenceMap referenceMap, TypeReference
264264
{
265265
object element = indexer.GetValue(value, new object[] {key});
266266

267-
TypeReference childElementType = InferType(referenceMap, element);
268-
269-
// We should not pass the parent element type as we are in a map
270-
// A map<string, object> could be a map<string, map<string, object> etc
271-
// If we pass the parent referenceMap then it will try to convert it as Any
272-
// So by inferring the child element type we are always converting the correct type.
273-
// See https://github.com/aws/aws-cdk/issues/2496
274-
275-
if (!TryConvert(childElementType, referenceMap, element, out object convertedElement))
267+
if (!TryConvertCollectionElement(element, referenceMap, elementType, out object convertedElement))
276268
{
277269
result = null;
278270
return false;
@@ -285,6 +277,47 @@ protected override bool TryConvertMap(IReferenceMap referenceMap, TypeReference
285277
return true;
286278
}
287279

280+
/// <summary>
281+
/// Converts a collection element
282+
/// </summary>
283+
/// <param name="element">The element to convert in the collection</param>
284+
/// <param name="referenceMap">The known references map</param>
285+
/// <param name="elementType">The TypeReference of the element, as seen by Jsii</param>
286+
/// <param name="convertedElement">out: the converted element</param>
287+
/// <returns>True if the conversion was successful, false otherwise</returns>
288+
private bool TryConvertCollectionElement(object element, IReferenceMap referenceMap, TypeReference elementType,
289+
out object convertedElement)
290+
{
291+
if (element is IDictionary<string, object> || element is object[])
292+
{
293+
var objectType = InferType(referenceMap, element);
294+
var nestedType = elementType.Primitive == PrimitiveType.Any ? elementType : objectType.Collection.ElementType;
295+
switch (objectType.Collection?.Kind)
296+
{
297+
case CollectionKind.Map:
298+
// We should not pass the parent element type as we are
299+
// in a map<string, object> containing another map.
300+
// If we pass the parent elementType then it will try to convert it as Any
301+
// So we can directly convert to another map here, and forgo the type hierarchy
302+
// induced by elementType
303+
// See https://github.com/aws/aws-cdk/issues/2496
304+
return TryConvertMap(referenceMap, nestedType, element,
305+
out convertedElement);
306+
case CollectionKind.Array:
307+
// The [object] could be another array. (ie Tags)
308+
// https://github.com/aws/aws-cdk/issues/3244
309+
return TryConvertArray(referenceMap, nestedType, element,
310+
out convertedElement);
311+
default:
312+
return TryConvert(elementType, referenceMap, element, out convertedElement);
313+
}
314+
}
315+
else
316+
{
317+
return TryConvert(elementType, referenceMap, element, out convertedElement);
318+
}
319+
}
320+
288321
protected override TypeReference InferType(IReferenceMap referenceMap, object value)
289322
{
290323
value = value ?? throw new ArgumentNullException(nameof(value));
@@ -328,7 +361,7 @@ TypeReference InferType(IReferenceMap referenceMap, Type type)
328361
return new TypeReference(primitive: PrimitiveType.Date);
329362
}
330363

331-
if (type.IsAssignableFrom(typeof(JObject)))
364+
if (type.IsAssignableFrom(typeof(JObject)) || type.IsAssignableFrom(typeof(JArray)))
332365
{
333366
return new TypeReference(primitive: PrimitiveType.Json);
334367
}

packages/jsii-pacmak/lib/targets/dotnet/filegenerator.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,9 @@ export class FileGenerator {
5050
const rootNode = xmlbuilder.create('Project', {encoding: 'UTF-8', headless: true});
5151
rootNode.att("Sdk", "Microsoft.NET.Sdk");
5252
const propertyGroup = rootNode.ele("PropertyGroup");
53-
propertyGroup.ele("TargetFramework", "netstandard2.0");
53+
propertyGroup.ele("TargetFramework", "netcoreapp2.1");
5454
propertyGroup.ele("GeneratePackageOnBuild", "true");
55+
propertyGroup.ele("GenerateDocumentationFile", "true");
5556
propertyGroup.ele("IncludeSymbols", "true");
5657
propertyGroup.ele("IncludeSource", "true");
5758
propertyGroup.ele("PackageVersion", this.getDecoratedVersion(assembly));
@@ -77,7 +78,7 @@ export class FileGenerator {
7778
}
7879

7980
if (dotnetInfo!.iconUrl != null) {
80-
propertyGroup.ele("IconUrl", dotnetInfo!.iconUrl);
81+
propertyGroup.ele("PackageIconUrl", dotnetInfo!.iconUrl);
8182
}
8283

8384
const itemGroup1 = rootNode.ele("ItemGroup");

packages/jsii-pacmak/test/expected.jsii-calc-base/dotnet/Amazon.JSII.Tests.CalculatorPackageId.BasePackageId/Amazon.JSII.Tests.CalculatorPackageId.BasePackageId.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<TargetFramework>netstandard2.0</TargetFramework>
3+
<TargetFramework>netcoreapp2.1</TargetFramework>
44
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
5+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
56
<IncludeSymbols>true</IncludeSymbols>
67
<IncludeSource>true</IncludeSource>
78
<PackageVersion>0.16.0</PackageVersion>

packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<TargetFramework>netstandard2.0</TargetFramework>
3+
<TargetFramework>netcoreapp2.1</TargetFramework>
44
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
5+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
56
<IncludeSymbols>true</IncludeSymbols>
67
<IncludeSource>true</IncludeSource>
78
<PackageVersion>0.16.0-devpreview</PackageVersion>

packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon.JSII.Tests.CalculatorPackageId.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
3-
<TargetFramework>netstandard2.0</TargetFramework>
3+
<TargetFramework>netcoreapp2.1</TargetFramework>
44
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
5+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
56
<IncludeSymbols>true</IncludeSymbols>
67
<IncludeSource>true</IncludeSource>
78
<PackageVersion>0.16.0</PackageVersion>

0 commit comments

Comments
 (0)