Skip to content

Commit 6b18c6a

Browse files
author
Maddie Clayton
authored
Merge pull request #71 from maddieclayton/completer
add new class
2 parents 75e99f8 + 0cec6d4 commit 6b18c6a

File tree

2 files changed

+203
-0
lines changed

2 files changed

+203
-0
lines changed
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
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+
namespace Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters
16+
{
17+
using Commands.Common.Authentication.Abstractions;
18+
using Commands.Common.Authentication;
19+
using Internal.Subscriptions;
20+
using Properties;
21+
using Management.Internal.Resources.Models;
22+
using Management.Internal.Resources;
23+
using System;
24+
using System.Collections.Generic;
25+
using System.Linq;
26+
using System.Management.Automation;
27+
using Microsoft.Rest.Azure;
28+
using System.Threading.Tasks;
29+
using Microsoft.Azure.Management.Internal.Resources.Utilities.Models;
30+
using System.Text;
31+
using Microsoft.Rest.Azure.OData;
32+
33+
34+
/// <summary>
35+
/// This attribute will allow the user to autocomplete the -Location parameter of a cmdlet with valid locations (as determined by the list of ResourceTypes given)
36+
/// </summary>
37+
public class ResourceNameCompleterAttribute : ArgumentCompleterAttribute
38+
{
39+
private static int _timeout = 3;
40+
41+
/// <summary>
42+
/// Pass in a list of ResourceTypes and this class will provide a list of locations that are common to all ResourceTypes given. This will then be available to the user to tab through.
43+
/// Example: ResourceNameCompleter(new string[] { "Microsoft.Batch/operations", "ResourceGroupName" })]
44+
/// </summary>
45+
/// <param name="resourceTypes"></param>
46+
public ResourceNameCompleterAttribute(string resourceType, string[] parentResourceParameterNames) : base(CreateScriptBlock(resourceType, parentResourceParameterNames))
47+
{
48+
}
49+
50+
public static string[] FindResources(string resourceType, string[] parentResources, int timeout)
51+
{
52+
_timeout = timeout;
53+
return FindResources(resourceType, parentResources);
54+
}
55+
56+
public static string[] FindResources(string resourceType, string[] parentResources)
57+
{
58+
IAzureContext context = AzureRmProfileProvider.Instance?.Profile?.DefaultContext;
59+
try
60+
{
61+
IResourceManagementClient client = AzureSession.Instance.ClientFactory.CreateArmClient<ResourceManagementClient>(context, AzureEnvironment.Endpoint.ResourceManager);
62+
Task<IPage<GenericResource>> allProviders = null;
63+
var odataQuery = new ODataQuery<GenericResourceFilter>(r => r.ResourceType == resourceType);
64+
65+
if (string.IsNullOrWhiteSpace(parentResources[0]))
66+
{
67+
allProviders = client.Resources.ListAsync(odataQuery);
68+
}
69+
else
70+
{
71+
allProviders = client.ResourceGroups.ListResourcesAsync(parentResources[0], odataQuery);
72+
}
73+
74+
List<ResourceIdentifier> ids = new List<ResourceIdentifier>();
75+
if (_timeout == -1)
76+
{
77+
allProviders.Wait();
78+
if (allProviders.Result != null)
79+
{
80+
foreach(var resource in allProviders.Result.ToList())
81+
{
82+
ids.Add(new ResourceIdentifier(resource.Id));
83+
}
84+
}
85+
else
86+
{
87+
#if DEBUG
88+
throw new InvalidOperationException("Result from client.Providers is null");
89+
#endif
90+
}
91+
}
92+
else if (allProviders.Wait(TimeSpan.FromSeconds(_timeout)))
93+
{
94+
if (allProviders.Result != null)
95+
{
96+
foreach (var resource in allProviders.Result.ToList())
97+
{
98+
ids.Add(new ResourceIdentifier(resource.Id));
99+
}
100+
}
101+
else
102+
{
103+
#if DEBUG
104+
throw new InvalidOperationException("Result from client.Providers is null");
105+
#endif
106+
}
107+
}
108+
else
109+
{
110+
#if DEBUG
111+
throw new InvalidOperationException(Resources.TimeOutForProviderList);
112+
#endif
113+
}
114+
115+
List<string> output = new List<string>();
116+
foreach (var resource in ids)
117+
{
118+
var include = true;
119+
120+
if (resource.ParentResource != null)
121+
{
122+
var actualParentResource = resource.ParentResource.Split('/');
123+
if (actualParentResource.Count() / 2 == parentResources.Count() - 1)
124+
{
125+
for (int i = 0; i < actualParentResource.Count() / 2; i++)
126+
{
127+
if (!string.IsNullOrEmpty(parentResources[i + 1]) && !string.Equals(actualParentResource[i * 2 + 1], parentResources[i + 1], StringComparison.OrdinalIgnoreCase))
128+
{
129+
include = false;
130+
}
131+
}
132+
}
133+
else
134+
{
135+
#if DEBUG
136+
throw new InvalidOperationException("Improper number of parent resources were given");
137+
#endif
138+
}
139+
}
140+
141+
if (include)
142+
{
143+
output.Add(resource.ResourceName);
144+
}
145+
}
146+
147+
return output.ToArray();
148+
}
149+
catch (Exception ex)
150+
{
151+
if (ex == null) { }
152+
#if DEBUG
153+
throw ex;
154+
#endif
155+
}
156+
#if !DEBUG
157+
return new string[0];
158+
#endif
159+
}
160+
161+
/// <summary>
162+
/// Create ScriptBlock that registers the correct location for tab completetion of the -Location parameter
163+
/// </summary>
164+
/// <param name="resourceTypes"></param>
165+
/// <returns></returns>
166+
public static ScriptBlock CreateScriptBlock(string resourceType, string[] parentResourceNames)
167+
{
168+
string script = "param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)\n" +
169+
"$parentResources = @()\n";
170+
foreach (var parentResourceName in parentResourceNames)
171+
{
172+
script += String.Format("$parentResources += $fakeBoundParameter[\"{0}\"]\n", parentResourceName);
173+
}
174+
script += String.Format("$resourceType = \"{0}\"\n", resourceType) +
175+
"$resources = [Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters.ResourceNameCompleterAttribute]::FindResources($resourceType, $parentResources)\n" +
176+
"$resources | Where-Object { $_ -Like \"$wordToComplete*\" } | Sort-Object | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }";
177+
ScriptBlock scriptBlock = ScriptBlock.Create(script);
178+
return scriptBlock;
179+
}
180+
181+
public static string CreateFilter(
182+
string resourceType,
183+
string filter)
184+
{
185+
var filterStringBuilder = new StringBuilder();
186+
187+
if (!string.IsNullOrWhiteSpace(resourceType))
188+
{
189+
if (filterStringBuilder.Length > 0)
190+
{
191+
filterStringBuilder.Append(" AND ");
192+
}
193+
194+
filterStringBuilder.AppendFormat("resourceType EQ '{0}'", resourceType);
195+
}
196+
197+
return filterStringBuilder.Length > 0
198+
? filterStringBuilder.ToString()
199+
: filter.CoalesceString();
200+
}
201+
}
202+
}

src/ResourceManager/ResourceManager.csproj

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

0 commit comments

Comments
 (0)