Skip to content

Commit

Permalink
Fixed API Connection might be missing dynamic properties Azure#2424 (A…
Browse files Browse the repository at this point in the history
  • Loading branch information
BernieWhite authored Nov 4, 2023
1 parent 431a35f commit 5f7b0bf
Show file tree
Hide file tree
Showing 7 changed files with 390 additions and 2 deletions.
2 changes: 2 additions & 0 deletions docs/CHANGELOG-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ What's changed since pre-release v1.31.0-B0020:
[#2505](https://github.com/Azure/PSRule.Rules.Azure/issues/2505)
- Fixed nullable parameters for custom types by @BernieWhite.
[#2489](https://github.com/Azure/PSRule.Rules.Azure/issues/2489)
- Fixed API Connection might be missing dynamic properties by @BernieWhite.
[#2424](https://github.com/Azure/PSRule.Rules.Azure/issues/2424)

## v1.31.0-B0020 (pre-release)

Expand Down
51 changes: 49 additions & 2 deletions src/PSRule.Rules.Azure/Data/Template/Mocks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,13 @@ public TValue GetValue<TValue>()

public JToken GetValue(TypePrimitive type)
{
throw new NotImplementedException();
if (type == TypePrimitive.None || BaseType == type)
return this;

if (TryMutate(type, this, out var value))
return value;

return null;
}

public JToken GetValue(object key)
Expand Down Expand Up @@ -315,7 +321,12 @@ public virtual JToken GetValue(object key)
var result = base[key];
if (result == null)
{
result = new MockUnknownObject(IsSecret);
// Guess string type for common scenarios.
if (key is string s_key && TryWellKnownStringProperty(this, s_key, out var s_value))
result = s_value;
else
result = new MockUnknownObject(IsSecret);

base[key] = result;
}
else if (result is JObject jObject)
Expand Down Expand Up @@ -464,5 +475,41 @@ private static TypePrimitive GetTypePrimitive(JToken token)

throw new NotImplementedException();
}

private static bool TryWellKnownStringProperty(JObject o, string key, out JValue value)
{
value = default;
if (StringComparer.OrdinalIgnoreCase.Equals(key, "primaryConnectionString") ||
StringComparer.OrdinalIgnoreCase.Equals(key, "secondaryConnectionString"))
{
value = new JValue(key);
return true;
}

// Handle name and type
if (StringComparer.OrdinalIgnoreCase.Equals(key, "name"))
{
value = TryExpandId(o) && o.TryGetProperty<JValue>("name", out var nameValue) ? nameValue : new JValue(key);
return true;
}
else if (StringComparer.OrdinalIgnoreCase.Equals(key, "type") && TryExpandId(o))
{
value = o.TryGetProperty<JValue>("type", out var nameValue) ? nameValue : default;
return true;
}
return false;
}

[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0007:Use implicit type", Justification = "Don't convert to var to ignore ambiguous type reference.")]
private static bool TryExpandId(JObject o)
{
if (o.TryGetProperty("id", out var id) && ResourceHelper.TryResourceIdComponents(id, out _, out _, out string resourceType, out string resourceName))
{
o["name"] = resourceName;
o["type"] = resourceType;
return true;
}
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,9 @@
<None Update="Tests.Bicep.27.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Tests.Bicep.29.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Tests.Bicep.3.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
18 changes: 18 additions & 0 deletions tests/PSRule.Rules.Azure.Tests/TemplateVisitorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,24 @@ public void NullableParameters()
Assert.Empty(actual["resources"][0]["properties"]["cors"]["corsRules"].Value<JArray>());
}

[Fact]
public void MockWellKnownProperties()
{
var resources = ProcessTemplate(GetSourcePath("Tests.Bicep.29.json"), null, out _);
Assert.Equal(6, resources.Length);

var actual = resources[2];
Assert.Equal("Microsoft.ServiceBus/namespaces", actual["type"].Value<string>());

actual = resources[3];
Assert.Equal("Microsoft.Web/connections", actual["type"].Value<string>());
Assert.Equal("/subscriptions/ffffffff-ffff-ffff-ffff-ffffffffffff/providers/Microsoft.Web/locations/eastus/managedApis/servicebus", actual["properties"]["api"]["id"].Value<string>());

actual = resources[4];
Assert.Equal("Microsoft.Web/connections", actual["type"].Value<string>());
Assert.Equal("/subscriptions/ffffffff-ffff-ffff-ffff-ffffffffffff/providers/Microsoft.Web/locations/eastus/managedApis/servicebus", actual["properties"]["api"]["id"].Value<string>());
}

#region Helper methods

private static string GetSourcePath(string fileName)
Expand Down
44 changes: 44 additions & 0 deletions tests/PSRule.Rules.Azure.Tests/Tests.Bicep.29.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// Community provided sample from: https://github.com/Azure/PSRule.Rules.Azure/issues/2424

#disable-next-line no-hardcoded-location
var location = 'eastus'

module serviceBus './Tests.Bicep.29.child.bicep' = {
name: 'my-test-service-bus'
params: {
Location: location
ServiceBusSkuName: 'Standard'
}
}

var logicAppParams = {
'$connections': {
value: {
serviceBusSender: {
connectionId: serviceBus.outputs.SendConnection.Id
connectionName: serviceBus.outputs.SendConnection.Name
id: subscriptionResourceId(serviceBus.outputs.SendConnection.Api.Type, location, serviceBus.outputs.SendConnection.Api.Name)
}
serviceBusListener: {
connectionId: serviceBus.outputs.ListenConnection.Id
connectionName: serviceBus.outputs.ListenConnection.Name
id: subscriptionResourceId(serviceBus.outputs.ListenConnection.Api.Type, location, serviceBus.outputs.ListenConnection.Api.Name)
}
}
}
}

resource workflow 'Microsoft.Logic/workflows@2019-05-01' = {
name: 'a-test-logic-app'
location: location
properties: {
state: 'Enabled'
definition: {
// Actual definition omitted
}
parameters: logicAppParams
}
}
86 changes: 86 additions & 0 deletions tests/PSRule.Rules.Azure.Tests/Tests.Bicep.29.child.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// Community provided sample from: https://github.com/Azure/PSRule.Rules.Azure/issues/2424

param Location string = resourceGroup().location
param ServiceBusSkuName string = 'Standard'

resource serviceBus 'Microsoft.ServiceBus/namespaces@2022-01-01-preview' = {
name: 'myServiceBus'
location: Location
sku: {
name: ServiceBusSkuName
tier: ServiceBusSkuName
}
properties: {
minimumTlsVersion: '1.2'
}

resource sendAuthRule 'AuthorizationRules' = {
name: 'SendAccess'
properties: {
rights: [
'Send'
]
}
}

resource listenAuthRule 'AuthorizationRules' = {
name: 'ListenAccess'
properties: {
rights: [
'Listen'
]
}
}

// Queues and Topics omitted

}

resource sendApiConnection 'Microsoft.Web/connections@2016-06-01' = {
name: 'sender-connection'
location: Location
properties: {
api: {
id: subscriptionResourceId('Microsoft.Web/locations/managedApis', Location, 'servicebus')
}
displayName: 'sender-connection'
parameterValues: {
connectionString: listKeys(serviceBus::sendAuthRule.name, serviceBus.apiVersion).primaryConnectionString
}
}
}

resource listenApiConnection 'Microsoft.Web/connections@2016-06-01' = {
name: 'listener-connection'
location: Location
properties: {
api: {
id: subscriptionResourceId('Microsoft.Web/locations/managedApis', Location, 'servicebus')
}
displayName: 'listener-connection'
parameterValues: {
connectionString: listKeys(serviceBus::listenAuthRule.name, serviceBus.apiVersion).primaryConnectionString
}
}
}

output SendConnection object = {
Name: sendApiConnection.name
Id: sendApiConnection.id
Api: {
Name: sendApiConnection.properties.api.name
Type: sendApiConnection.properties.api.type
}
}

output ListenConnection object = {
Name: listenApiConnection.name
Id: listenApiConnection.id
Api: {
Name: listenApiConnection.properties.api.name
Type: listenApiConnection.properties.api.type
}
}
Loading

0 comments on commit 5f7b0bf

Please sign in to comment.