Skip to content

Commit 887ae87

Browse files
authored
Enable custom subscription ID by attribute (#282)
* supports subscription id * address comments * add license header; revise unit tests
1 parent bd108fc commit 887ae87

File tree

9 files changed

+334
-13
lines changed

9 files changed

+334
-13
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
16+
using Xunit;
17+
18+
namespace Authentication.Abstractions.Test
19+
{
20+
public class ContextUnitTests
21+
{
22+
[Fact]
23+
public void TestDeepCopy()
24+
{
25+
IAzureSubscription subscription = new AzureSubscription()
26+
{
27+
Id = "DDDDDDDD-DDDD-DDDD-DDDD-DDDDDDDDDDDD",
28+
Name = "my sub",
29+
State = "my state",
30+
};
31+
const string SubHomeTenant = "my home tenant";
32+
subscription.SetHomeTenant(SubHomeTenant);
33+
34+
IAzureAccount account = new AzureAccount()
35+
{
36+
37+
Type = "User"
38+
};
39+
40+
IAzureEnvironment environment = new AzureEnvironment()
41+
{
42+
Name = "my environment"
43+
};
44+
IAzureTenant tenant = new AzureTenant()
45+
{
46+
Id = "DDDDDDDD-DDDD-DDDD-DDDD-DDDDDDDDDDDD"
47+
};
48+
IAzureContext original = new AzureContext(subscription, account, environment, tenant);
49+
const string PropertyKey = "customPropertyKey";
50+
const string PropertyValue = "customPropertyValue";
51+
original.SetProperty(PropertyKey, PropertyValue);
52+
53+
IAzureContext clone = original.DeepCopy();
54+
55+
// references are not equal
56+
Assert.NotSame(original, clone);
57+
Assert.NotSame(original.Subscription, clone.Subscription);
58+
Assert.NotSame(original.Account, clone.Account);
59+
Assert.NotSame(original.Environment, clone.Environment);
60+
Assert.NotSame(original.Tenant, clone.Tenant);
61+
62+
// values are equal
63+
Assert.Equal(original.Subscription.Id, clone.Subscription.Id);
64+
Assert.Equal(original.Account.Id, clone.Account.Id);
65+
Assert.Equal(original.Environment.Name, clone.Environment.Name);
66+
Assert.Equal(original.Tenant.Id, clone.Tenant.Id);
67+
68+
// custom property
69+
Assert.Equal(SubHomeTenant, clone.Subscription.GetHomeTenant());
70+
Assert.Equal(PropertyValue, clone.GetProperty(PropertyKey));
71+
}
72+
73+
[Fact]
74+
public void TestDeepCopyNull()
75+
{
76+
IAzureContext original = null;
77+
Assert.Null(original.DeepCopy());
78+
}
79+
}
80+
}

src/Authentication.Abstractions/Extensions/AzureContextExtensions.cs

+26
Original file line numberDiff line numberDiff line change
@@ -148,5 +148,31 @@ public static void Update(this IAzureContext context, IAzureContext other)
148148
context.UpdateProperties(other);
149149
}
150150
}
151+
152+
/// <summary>
153+
/// Deep copy a context.
154+
/// </summary>
155+
/// <returns>The copy.</returns>
156+
public static IAzureContext DeepCopy(this IAzureContext context)
157+
{
158+
if (context == null)
159+
{
160+
return null;
161+
}
162+
var deepCopy = new AzureContext()
163+
{
164+
Account = new AzureAccount(),
165+
Tenant = new AzureTenant(),
166+
Subscription = new AzureSubscription(),
167+
Environment = new AzureEnvironment(),
168+
VersionProfile = context.VersionProfile
169+
};
170+
deepCopy.Account.CopyFrom(context.Account);
171+
deepCopy.Tenant.CopyFrom(context.Tenant);
172+
deepCopy.Subscription.CopyFrom(context.Subscription);
173+
deepCopy.Environment.CopyFrom(context.Environment);
174+
deepCopy.CopyPropertiesFrom(context);
175+
return deepCopy;
176+
}
151177
}
152178
}

src/Authentication.Abstractions/Interfaces/IAzureContextContainer.cs

+7
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,12 @@ public interface IAzureContextContainer: IExtensibleModel
4949
/// Remove all contexts from the container
5050
/// </summary>
5151
void Clear();
52+
53+
/// <summary>
54+
/// Copy the context container for overriding default context.
55+
/// See <see cref="SupportsSubscriptionIdAttribute"/>
56+
/// </summary>
57+
/// <returns>The copy.</returns>
58+
IAzureContextContainer CopyForContextOverriding();
5259
}
5360
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
using System;
16+
17+
namespace Microsoft.WindowsAzure.Commands.Common.Attributes
18+
{
19+
/// <summary>
20+
/// Indicates a cmdlet supports overriding subscription ID via `-SubscriptionId` parameter.
21+
/// </summary>
22+
public class SupportsSubscriptionIdAttribute : Attribute
23+
{
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
using Microsoft.Azure.Commands.ResourceManager.Common;
16+
using Microsoft.WindowsAzure.Commands.Common.Attributes;
17+
using System;
18+
using System.Collections.ObjectModel;
19+
using System.Management.Automation;
20+
using Xunit;
21+
22+
namespace Microsoft.Azure.Commands.ResourceManager.Test
23+
{
24+
public class AzureRMCmdletUnitTests
25+
{
26+
[Fact]
27+
public void HasAttributeHasSubId()
28+
{
29+
var positive = new PositiveCmdlet();
30+
var dynamicParameters = positive.GetDynamicParameters() as RuntimeDefinedParameterDictionary;
31+
Assert.NotNull(dynamicParameters);
32+
Assert.Single(dynamicParameters);
33+
Assert.NotNull(dynamicParameters["SubscriptionId"]);
34+
}
35+
36+
[Fact]
37+
public void NoAttributeNoSubId()
38+
{
39+
var negative = new NegativeCmdlet();
40+
var dynamicParameters = negative.GetDynamicParameters() as RuntimeDefinedParameterDictionary;
41+
Assert.Empty(dynamicParameters);
42+
}
43+
44+
[Fact]
45+
public void DoubleDynamicParameters()
46+
{
47+
// when child cmdlet also has dynamic parameter
48+
// it should combine with base class
49+
var positiveWithOwnParam = new PositiveCmdletWithOwnDynamicParam();
50+
var dynamicParameters = positiveWithOwnParam.GetDynamicParameters() as RuntimeDefinedParameterDictionary;
51+
Assert.NotNull(dynamicParameters);
52+
Assert.Collection(dynamicParameters,
53+
pair => { Assert.Equal("SubscriptionId", pair.Key); },
54+
pair => { Assert.Equal("DP", pair.Key); }
55+
);
56+
}
57+
58+
[SupportsSubscriptionId]
59+
class PositiveCmdlet : AzureRMCmdlet
60+
{
61+
}
62+
63+
class NegativeCmdlet : AzureRMCmdlet { }
64+
65+
[SupportsSubscriptionId]
66+
class PositiveCmdletWithOwnDynamicParam : AzureRMCmdlet, IDynamicParameters
67+
{
68+
public new object GetDynamicParameters()
69+
{
70+
var parameters = base.GetDynamicParameters() as RuntimeDefinedParameterDictionary;
71+
parameters.Add("DP", new RuntimeDefinedParameter(
72+
"DP",
73+
typeof(string),
74+
new Collection<Attribute>()
75+
{
76+
new ParameterAttribute { }
77+
}
78+
));
79+
return parameters;
80+
}
81+
}
82+
}
83+
}

src/ResourceManager/Properties/Resources.Designer.cs

+24-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/ResourceManager/Properties/Resources.resx

+8
Original file line numberDiff line numberDiff line change
@@ -222,4 +222,12 @@ Use the Enable-AzureDataCollection cmdlet to turn the feature On. The cmdlet can
222222
<data name="RunConnectAccount" xml:space="preserve">
223223
<value>Run Connect-AzAccount to login.</value>
224224
</data>
225+
<data name="SubscriptionIdHelpMessage" xml:space="preserve">
226+
<value>The ID of the subscription.
227+
By default, cmdlets are executed in the subscription that is set in the current context. If the user specifies another subscription, the current cmdlet is executed in the subscription specified by the user.
228+
Overriding subscriptions only take effect during the lifecycle of the current cmdlet. It does not change the subscription in the context, and does not affect subsequent cmdlets.</value>
229+
</data>
230+
<data name="CustomSubscriptionNotFound" xml:space="preserve">
231+
<value>Could not found subscription '{0}' in context. Please run 'Connect-AzAccount' with correct user.</value>
232+
</data>
225233
</root>

src/ResourceManager/ResourceManager.csproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,15 @@
4444

4545
<ItemGroup>
4646
<Compile Update="Properties\Resources.Designer.cs">
47-
<DesignTime>true</DesignTime>
47+
<DesignTime>True</DesignTime>
4848
<AutoGen>true</AutoGen>
4949
<DependentUpon>Resources.resx</DependentUpon>
5050
</Compile>
5151
</ItemGroup>
5252

5353
<ItemGroup>
5454
<EmbeddedResource Update="Properties\Resources.resx">
55-
<Generator>ResXFileCodeGenerator</Generator>
55+
<Generator>PublicResXFileCodeGenerator</Generator>
5656
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
5757
</EmbeddedResource>
5858
</ItemGroup>

0 commit comments

Comments
 (0)