Skip to content

Commit 916dd9f

Browse files
Resource Id completer added
1 parent a99ffe9 commit 916dd9f

File tree

6 files changed

+339
-0
lines changed

6 files changed

+339
-0
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Management.Automation;
5+
using System.Threading;
6+
using Microsoft.Azure.Commands.Common.Authentication;
7+
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
8+
using Microsoft.Azure.Management.Internal.Resources;
9+
using Microsoft.Azure.Management.Internal.Resources.Models;
10+
using Microsoft.Rest.Azure.OData;
11+
12+
namespace Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters
13+
{
14+
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
15+
public class ResourceIdCompleterAttribute : ArgumentCompleterAttribute
16+
{
17+
private static readonly object Lock = new object();
18+
19+
/// <summary>
20+
/// Consturctor
21+
/// </summary>
22+
/// <param name="resourceType">Azure recource type</param>
23+
public ResourceIdCompleterAttribute(string resourceType)
24+
: base(CreateScriptBlock(resourceType))
25+
{}
26+
27+
public static ScriptBlock CreateScriptBlock(string resourceType)
28+
{
29+
string script = "param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)\n" +
30+
$"$resourceType = \"{resourceType}\"\n" +
31+
"$resourceIds = [Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters.ResourceIdCompleterAttribute]::GetResourceIds($resourceType)\n" +
32+
"$resourceIds | Where-Object { $_ -Like \"*$wordToComplete*\" } | Sort-Object | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }";
33+
var scriptBlock = ScriptBlock.Create(script);
34+
return scriptBlock;
35+
}
36+
37+
private class CacheItem
38+
{
39+
public DateTime Timestamp { get; set; }
40+
public IEnumerable<string> ResourceInfoList { get; set; }
41+
}
42+
43+
private static readonly IDictionary<int, CacheItem> Cache = new Dictionary<int, CacheItem>();
44+
45+
public static TimeSpan TimeToUpdate { get; set; } = TimeSpan.FromMinutes(5);
46+
47+
public static TimeSpan RequestTimeout { get; set; } = TimeSpan.FromSeconds(3);
48+
49+
public static IEnumerable<string> GetResourceIds(string resourceType)
50+
{
51+
lock (Lock)
52+
{
53+
var context = AzureRmProfileProvider.Instance.Profile.DefaultContext;
54+
var contextHash = HashContext(context, resourceType);
55+
var cacheItem = Cache.ContainsKey(contextHash) ? Cache[contextHash] : null;
56+
57+
if (cacheItem != null && DateTime.Now.Subtract(cacheItem.Timestamp).CompareTo(TimeToUpdate) < 0)
58+
{
59+
return Cache[contextHash].ResourceInfoList;
60+
}
61+
62+
var client = AzureSession.Instance.ClientFactory.CreateArmClient<ResourceManagementClient>(context, AzureEnvironment.Endpoint.ResourceManager);
63+
64+
client.SubscriptionId = context.Subscription.Id;
65+
66+
var odata = new ODataQuery<GenericResourceFilter>(r => r.ResourceType == resourceType);
67+
68+
IEnumerable<string> resourceInfoList = new List<string>();
69+
70+
try
71+
{
72+
using (var cancelSource = new CancellationTokenSource())
73+
{
74+
var task = client.Resources.ListAsync(odata, cancelSource.Token);
75+
76+
if (!task.Wait(RequestTimeout))
77+
{
78+
cancelSource.Cancel();
79+
return resourceInfoList;
80+
}
81+
82+
var result = task.Result;
83+
resourceInfoList = result
84+
.Select(r => r.Id)
85+
.ToList();
86+
}
87+
}
88+
catch (ArgumentException)
89+
{
90+
return resourceInfoList;
91+
}
92+
93+
if (cacheItem != null)
94+
{
95+
cacheItem.Timestamp = DateTime.Now;
96+
cacheItem.ResourceInfoList = resourceInfoList;
97+
Cache[contextHash] = cacheItem;
98+
}
99+
else
100+
{
101+
Cache.Add(contextHash, new CacheItem
102+
{
103+
Timestamp = DateTime.Now,
104+
ResourceInfoList = resourceInfoList
105+
});
106+
}
107+
108+
return resourceInfoList;
109+
}
110+
}
111+
112+
private static int HashContext(IAzureContext context, string resourceType)
113+
{
114+
return (context.Account.Id + context.Environment.Name + context.Subscription.Id + context.Tenant.Id + resourceType).GetHashCode();
115+
}
116+
}
117+
}

src/ResourceManager/Common/Commands.ResourceManager.Common/Commands.ResourceManager.Common.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
<ItemGroup>
5757
<Compile Include="AccessTokenExtensions.cs" />
5858
<Compile Include="ArgumentCompleters\PSArgumentCompleter.cs" />
59+
<Compile Include="ArgumentCompleters\ResourceIdCompleter.cs" />
5960
<Compile Include="ArgumentCompleters\ResourceTypeCompleter.cs" />
6061
<Compile Include="ArgumentCompleters\ScopeCompleter.cs" />
6162
<Compile Include="AzureRmCmdlet.cs" />

src/ResourceManager/Profile/Commands.Profile.Test/ArgumentCompleterTests.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,12 @@ public void TestResourceGroupCompleter()
4848
{
4949
ProfileController.NewInstance.RunPsTest(xunitLogger, "72f988bf-86f1-41af-91ab-2d7cd011db47", "Test-ResourceGroupCompleter");
5050
}
51+
52+
[Fact]
53+
[Trait(Category.AcceptanceType, Category.CheckIn)]
54+
public void TestResourceIdCompleter()
55+
{
56+
ProfileController.NewInstance.RunPsTest(xunitLogger, "72f988bf-86f1-41af-91ab-2d7cd011db47", "Test-ResourceIdCompleter");
57+
}
5158
}
5259
}

src/ResourceManager/Profile/Commands.Profile.Test/ArgumentCompleterTests.ps1

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,27 @@ function Test-ResourceGroupCompleter
3939
$resourceGroups = [Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters.ResourceGroupCompleterAttribute]::GetResourceGroups(-1)
4040
$expectResourceGroups = Get-AzureRmResourceGroup | ForEach-Object {$_.ResourceGroupName}
4141
Assert-AreEqualArray $resourceGroups $expectResourceGroups
42+
}
43+
44+
<#
45+
.SYNOPSIS
46+
Tests resource id completer
47+
#>
48+
function Test-ResourceIdCompleter
49+
{
50+
$filePath = Join-Path -Path $PSScriptRoot -ChildPath "\Microsoft.Azure.Commands.ResourceManager.Common.dll"
51+
[System.Reflection.Assembly]::LoadFrom($filePath)
52+
$resourceType = "Microsoft.Storage/storageAccounts"
53+
$expectResourceIds = Get-AzureRmResource -ResourceType $resourceType | ForEach-Object {$_.Id}
54+
# take data from Azure and put to cache
55+
$resourceIds = [Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters.ResourceIdCompleterAttribute]::GetResourceIds($resourceType)
56+
Assert-AreEqualArray $resourceIds $expectResourceIds
57+
# take data from the cache
58+
$resourceIds = [Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters.ResourceIdCompleterAttribute]::GetResourceIds($resourceType)
59+
Assert-AreEqualArray $resourceIds $expectResourceIds
60+
# change time to update the cache
61+
[Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters.ResourceIdCompleterAttribute]::TimeToUpdate = [System.TimeSpan]::FromSeconds(0)
62+
# take data from Azure again and put to cache
63+
$resourceIds = [Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters.ResourceIdCompleterAttribute]::GetResourceIds($resourceType)
64+
Assert-AreEqualArray $resourceIds $expectResourceIds
4265
}

src/ResourceManager/Profile/Commands.Profile.Test/Commands.Profile.Test.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@
199199
<None Include="SessionRecords\Microsoft.Azure.Commands.Profile.Test.ArgumentCompleterTests\TestResourceGroupCompleter.json">
200200
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
201201
</None>
202+
<None Include="SessionRecords\Microsoft.Azure.Commands.Profile.Test.ArgumentCompleterTests\TestResourceIdCompleter.json">
203+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
204+
</None>
202205
<None Include="SessionRecords\Microsoft.Azure.Commands.Profile.Test.DefaultCmdletTests\DefaultResourceGroup.json">
203206
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
204207
</None>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
{
2+
"Entries": [
3+
{
4+
"RequestUri": "/subscriptions/c9cbd920-c00c-427c-852b-8aaf38badaeb/resources?$filter=resourceType%20EQ%20'Microsoft.Storage/storageAccounts'&api-version=2017-05-10",
5+
"EncodedRequestUri": "L3N1YnNjcmlwdGlvbnMvYzljYmQ5MjAtYzAwYy00MjdjLTg1MmItOGFhZjM4YmFkYWViL3Jlc291cmNlcz8kZmlsdGVyPXJlc291cmNlVHlwZSUyMEVRJTIwJ01pY3Jvc29mdC5TdG9yYWdlL3N0b3JhZ2VBY2NvdW50cycmYXBpLXZlcnNpb249MjAxNy0wNS0xMA==",
6+
"RequestMethod": "GET",
7+
"RequestBody": "",
8+
"RequestHeaders": {
9+
"x-ms-client-request-id": [
10+
"a2fa5037-a986-4220-ba17-ddc91a591dbe"
11+
],
12+
"accept-language": [
13+
"en-US"
14+
],
15+
"User-Agent": [
16+
"FxVersion/4.7.3056.0",
17+
"OSName/Windows10Enterprise",
18+
"OSVersion/6.3.17134",
19+
"Microsoft.Azure.Management.ResourceManager.ResourceManagementClient/1.6.0.0"
20+
]
21+
},
22+
"ResponseBody": "{\r\n \"value\": [\r\n {\r\n \"id\": \"/subscriptions/c9cbd920-c00c-427c-852b-8aaf38badaeb/resourceGroups/shch/providers/Microsoft.Storage/storageAccounts/shch\",\r\n \"name\": \"shch\",\r\n \"type\": \"Microsoft.Storage/storageAccounts\",\r\n \"sku\": {\r\n \"name\": \"Standard_LRS\",\r\n \"tier\": \"Standard\"\r\n },\r\n \"kind\": \"Storage\",\r\n \"location\": \"westus\",\r\n \"tags\": {}\r\n },\r\n {\r\n \"id\": \"/subscriptions/c9cbd920-c00c-427c-852b-8aaf38badaeb/resourceGroups/shch1/providers/Microsoft.Storage/storageAccounts/shch1\",\r\n \"name\": \"shch1\",\r\n \"type\": \"Microsoft.Storage/storageAccounts\",\r\n \"sku\": {\r\n \"name\": \"Standard_LRS\",\r\n \"tier\": \"Standard\"\r\n },\r\n \"kind\": \"Storage\",\r\n \"location\": \"westus\",\r\n \"tags\": {}\r\n }\r\n ]\r\n}",
23+
"ResponseHeaders": {
24+
"Content-Length": [
25+
"578"
26+
],
27+
"Content-Type": [
28+
"application/json; charset=utf-8"
29+
],
30+
"Expires": [
31+
"-1"
32+
],
33+
"Pragma": [
34+
"no-cache"
35+
],
36+
"x-ms-ratelimit-remaining-subscription-reads": [
37+
"14999"
38+
],
39+
"x-ms-request-id": [
40+
"d621edc8-12b9-4d82-a819-7ca024273e65"
41+
],
42+
"x-ms-correlation-request-id": [
43+
"d621edc8-12b9-4d82-a819-7ca024273e65"
44+
],
45+
"x-ms-routing-request-id": [
46+
"WESTUS2:20180514T171249Z:d621edc8-12b9-4d82-a819-7ca024273e65"
47+
],
48+
"Strict-Transport-Security": [
49+
"max-age=31536000; includeSubDomains"
50+
],
51+
"X-Content-Type-Options": [
52+
"nosniff"
53+
],
54+
"Cache-Control": [
55+
"no-cache"
56+
],
57+
"Date": [
58+
"Mon, 14 May 2018 17:12:48 GMT"
59+
]
60+
},
61+
"StatusCode": 200
62+
},
63+
{
64+
"RequestUri": "/subscriptions/c9cbd920-c00c-427c-852b-8aaf38badaeb/resources?$filter=resourceType%20eq%20'Microsoft.Storage%2FstorageAccounts'&api-version=2016-09-01",
65+
"EncodedRequestUri": "L3N1YnNjcmlwdGlvbnMvYzljYmQ5MjAtYzAwYy00MjdjLTg1MmItOGFhZjM4YmFkYWViL3Jlc291cmNlcz8kZmlsdGVyPXJlc291cmNlVHlwZSUyMGVxJTIwJ01pY3Jvc29mdC5TdG9yYWdlJTJGc3RvcmFnZUFjY291bnRzJyZhcGktdmVyc2lvbj0yMDE2LTA5LTAx",
66+
"RequestMethod": "GET",
67+
"RequestBody": "",
68+
"RequestHeaders": {
69+
"x-ms-client-request-id": [
70+
"48617096-aa87-4f19-9e77-d95d612aaa95"
71+
],
72+
"accept-language": [
73+
"en-US"
74+
],
75+
"User-Agent": [
76+
"FxVersion/4.7.3056.0",
77+
"OSName/Windows10Enterprise",
78+
"OSVersion/6.3.17134",
79+
"Microsoft.Azure.Management.Internal.Resources.ResourceManagementClient/4.1.0"
80+
]
81+
},
82+
"ResponseBody": "{\r\n \"value\": [\r\n {\r\n \"id\": \"/subscriptions/c9cbd920-c00c-427c-852b-8aaf38badaeb/resourceGroups/shch/providers/Microsoft.Storage/storageAccounts/shch\",\r\n \"name\": \"shch\",\r\n \"type\": \"Microsoft.Storage/storageAccounts\",\r\n \"sku\": {\r\n \"name\": \"Standard_LRS\",\r\n \"tier\": \"Standard\"\r\n },\r\n \"kind\": \"Storage\",\r\n \"location\": \"westus\",\r\n \"tags\": {}\r\n },\r\n {\r\n \"id\": \"/subscriptions/c9cbd920-c00c-427c-852b-8aaf38badaeb/resourceGroups/shch1/providers/Microsoft.Storage/storageAccounts/shch1\",\r\n \"name\": \"shch1\",\r\n \"type\": \"Microsoft.Storage/storageAccounts\",\r\n \"sku\": {\r\n \"name\": \"Standard_LRS\",\r\n \"tier\": \"Standard\"\r\n },\r\n \"kind\": \"Storage\",\r\n \"location\": \"westus\",\r\n \"tags\": {}\r\n }\r\n ]\r\n}",
83+
"ResponseHeaders": {
84+
"Content-Length": [
85+
"578"
86+
],
87+
"Content-Type": [
88+
"application/json; charset=utf-8"
89+
],
90+
"Expires": [
91+
"-1"
92+
],
93+
"Pragma": [
94+
"no-cache"
95+
],
96+
"x-ms-ratelimit-remaining-subscription-reads": [
97+
"14999"
98+
],
99+
"x-ms-request-id": [
100+
"5c5c1d6a-5b68-4804-b78c-e8c9ccf2ca9e"
101+
],
102+
"x-ms-correlation-request-id": [
103+
"5c5c1d6a-5b68-4804-b78c-e8c9ccf2ca9e"
104+
],
105+
"x-ms-routing-request-id": [
106+
"WESTUS2:20180514T171249Z:5c5c1d6a-5b68-4804-b78c-e8c9ccf2ca9e"
107+
],
108+
"Strict-Transport-Security": [
109+
"max-age=31536000; includeSubDomains"
110+
],
111+
"X-Content-Type-Options": [
112+
"nosniff"
113+
],
114+
"Cache-Control": [
115+
"no-cache"
116+
],
117+
"Date": [
118+
"Mon, 14 May 2018 17:12:48 GMT"
119+
]
120+
},
121+
"StatusCode": 200
122+
},
123+
{
124+
"RequestUri": "/subscriptions/c9cbd920-c00c-427c-852b-8aaf38badaeb/resources?$filter=resourceType%20eq%20'Microsoft.Storage%2FstorageAccounts'&api-version=2016-09-01",
125+
"EncodedRequestUri": "L3N1YnNjcmlwdGlvbnMvYzljYmQ5MjAtYzAwYy00MjdjLTg1MmItOGFhZjM4YmFkYWViL3Jlc291cmNlcz8kZmlsdGVyPXJlc291cmNlVHlwZSUyMGVxJTIwJ01pY3Jvc29mdC5TdG9yYWdlJTJGc3RvcmFnZUFjY291bnRzJyZhcGktdmVyc2lvbj0yMDE2LTA5LTAx",
126+
"RequestMethod": "GET",
127+
"RequestBody": "",
128+
"RequestHeaders": {
129+
"x-ms-client-request-id": [
130+
"b249f9de-8c34-4bef-9cab-e38f4cb50d80"
131+
],
132+
"accept-language": [
133+
"en-US"
134+
],
135+
"User-Agent": [
136+
"FxVersion/4.7.3056.0",
137+
"OSName/Windows10Enterprise",
138+
"OSVersion/6.3.17134",
139+
"Microsoft.Azure.Management.Internal.Resources.ResourceManagementClient/4.1.0"
140+
]
141+
},
142+
"ResponseBody": "{\r\n \"value\": [\r\n {\r\n \"id\": \"/subscriptions/c9cbd920-c00c-427c-852b-8aaf38badaeb/resourceGroups/shch/providers/Microsoft.Storage/storageAccounts/shch\",\r\n \"name\": \"shch\",\r\n \"type\": \"Microsoft.Storage/storageAccounts\",\r\n \"sku\": {\r\n \"name\": \"Standard_LRS\",\r\n \"tier\": \"Standard\"\r\n },\r\n \"kind\": \"Storage\",\r\n \"location\": \"westus\",\r\n \"tags\": {}\r\n },\r\n {\r\n \"id\": \"/subscriptions/c9cbd920-c00c-427c-852b-8aaf38badaeb/resourceGroups/shch1/providers/Microsoft.Storage/storageAccounts/shch1\",\r\n \"name\": \"shch1\",\r\n \"type\": \"Microsoft.Storage/storageAccounts\",\r\n \"sku\": {\r\n \"name\": \"Standard_LRS\",\r\n \"tier\": \"Standard\"\r\n },\r\n \"kind\": \"Storage\",\r\n \"location\": \"westus\",\r\n \"tags\": {}\r\n }\r\n ]\r\n}",
143+
"ResponseHeaders": {
144+
"Content-Length": [
145+
"578"
146+
],
147+
"Content-Type": [
148+
"application/json; charset=utf-8"
149+
],
150+
"Expires": [
151+
"-1"
152+
],
153+
"Pragma": [
154+
"no-cache"
155+
],
156+
"x-ms-ratelimit-remaining-subscription-reads": [
157+
"14998"
158+
],
159+
"x-ms-request-id": [
160+
"53a15da9-0a0d-41e7-8cf8-29e346b6103e"
161+
],
162+
"x-ms-correlation-request-id": [
163+
"53a15da9-0a0d-41e7-8cf8-29e346b6103e"
164+
],
165+
"x-ms-routing-request-id": [
166+
"WESTUS2:20180514T171250Z:53a15da9-0a0d-41e7-8cf8-29e346b6103e"
167+
],
168+
"Strict-Transport-Security": [
169+
"max-age=31536000; includeSubDomains"
170+
],
171+
"X-Content-Type-Options": [
172+
"nosniff"
173+
],
174+
"Cache-Control": [
175+
"no-cache"
176+
],
177+
"Date": [
178+
"Mon, 14 May 2018 17:12:49 GMT"
179+
]
180+
},
181+
"StatusCode": 200
182+
}
183+
],
184+
"Names": {},
185+
"Variables": {
186+
"SubscriptionId": "c9cbd920-c00c-427c-852b-8aaf38badaeb"
187+
}
188+
}

0 commit comments

Comments
 (0)