Skip to content

Commit 25bff11

Browse files
authored
Merge branch 'develop' into myget-symbols
2 parents 70dcb7c + 2c49680 commit 25bff11

File tree

2 files changed

+179
-0
lines changed

2 files changed

+179
-0
lines changed
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
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+
try
59+
{
60+
List<ResourceIdentifier> ids = GetResourceIdsFromClient(resourceType, parentResources[0]);
61+
62+
List<string> output = FilterByParentResource(ids, parentResources);
63+
64+
return output.ToArray();
65+
}
66+
catch (Exception ex)
67+
{
68+
if (ex == null) { }
69+
#if DEBUG
70+
throw ex;
71+
#endif
72+
}
73+
#if !DEBUG
74+
return new string[0];
75+
#endif
76+
}
77+
78+
/// <summary>
79+
/// Create ScriptBlock that registers the correct location for tab completetion of the -Location parameter
80+
/// </summary>
81+
/// <param name="resourceTypes"></param>
82+
/// <returns></returns>
83+
public static ScriptBlock CreateScriptBlock(string resourceType, string[] parentResourceNames)
84+
{
85+
string script = "param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)\n" +
86+
"$parentResources = @()\n";
87+
foreach (var parentResourceName in parentResourceNames)
88+
{
89+
script += String.Format("$parentResources += $fakeBoundParameter[\"{0}\"]\n", parentResourceName);
90+
}
91+
script += String.Format("$resourceType = \"{0}\"\n", resourceType) +
92+
"$resources = [Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters.ResourceNameCompleterAttribute]::FindResources($resourceType, $parentResources)\n" +
93+
"$resources | Where-Object { $_ -Like \"$wordToComplete*\" } | Sort-Object | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }";
94+
ScriptBlock scriptBlock = ScriptBlock.Create(script);
95+
return scriptBlock;
96+
}
97+
98+
public static string CreateFilter(
99+
string resourceType,
100+
string filter)
101+
{
102+
var filterStringBuilder = new StringBuilder();
103+
104+
if (!string.IsNullOrWhiteSpace(resourceType))
105+
{
106+
if (filterStringBuilder.Length > 0)
107+
{
108+
filterStringBuilder.Append(" AND ");
109+
}
110+
111+
filterStringBuilder.AppendFormat("resourceType EQ '{0}'", resourceType);
112+
}
113+
114+
return filterStringBuilder.Length > 0
115+
? filterStringBuilder.ToString()
116+
: filter.CoalesceString();
117+
}
118+
119+
public static List<ResourceIdentifier> GetResourceIdsFromClient(string resourceType, string resourceGroupName)
120+
{
121+
IAzureContext context = AzureRmProfileProvider.Instance?.Profile?.DefaultContext;
122+
IResourceManagementClient client = AzureSession.Instance.ClientFactory.CreateArmClient<ResourceManagementClient>(context, AzureEnvironment.Endpoint.ResourceManager);
123+
var odataQuery = new ODataQuery<GenericResourceFilter>(r => r.ResourceType == resourceType);
124+
125+
var allProviders = string.IsNullOrWhiteSpace(resourceGroupName)
126+
? client.Resources.ListAsync(odataQuery)
127+
: client.ResourceGroups.ListResourcesAsync(resourceGroupName, odataQuery);
128+
129+
var timeoutDuration = _timeout == -1 ? TimeSpan.FromMinutes(5) : TimeSpan.FromSeconds(_timeout);
130+
var hasNotTimedOut = allProviders.Wait(timeoutDuration);
131+
var hasResult = allProviders.Result != null;
132+
var isSuccessful = hasNotTimedOut && hasResult;
133+
134+
#if DEBUG
135+
if (!isSuccessful)
136+
{
137+
throw new InvalidOperationException(!hasResult ? "Result from client.Providers is null" : Resources.TimeOutForProviderList);
138+
}
139+
#endif
140+
141+
return isSuccessful
142+
? allProviders.Result.Select(resource => new ResourceIdentifier(resource.Id)).ToList()
143+
: new List<ResourceIdentifier>();
144+
}
145+
146+
public static List<string> FilterByParentResource(List<ResourceIdentifier> ids, string[] parentResources)
147+
{
148+
return ids.Where(resource =>
149+
{
150+
if (resource.ParentResource == null)
151+
{
152+
return true;
153+
}
154+
155+
var actualParentResources = resource.ParentResource.Split('/').Where((pr, i) => i % 2 != 0).ToArray();
156+
var parentResourcesExceptFirst = parentResources.Skip(1).ToArray();
157+
if (actualParentResources.Count() != parentResourcesExceptFirst.Count())
158+
{
159+
#if DEBUG
160+
throw new InvalidOperationException("Improper number of parent resources were given");
161+
#else
162+
return true;
163+
#endif
164+
}
165+
166+
for (int i = 0; i < actualParentResources.Count(); i++)
167+
{
168+
if (!string.IsNullOrEmpty(parentResourcesExceptFirst[i]) && !string.Equals(actualParentResources[i], parentResourcesExceptFirst[i], StringComparison.OrdinalIgnoreCase))
169+
{
170+
return false;
171+
}
172+
}
173+
174+
return true;
175+
}).Select(resource => resource.ResourceName).ToList();
176+
}
177+
}
178+
}

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)