12
12
// limitations under the License.
13
13
// ----------------------------------------------------------------------------------
14
14
15
- //#define SERIALIZE
16
-
17
15
using Newtonsoft . Json ;
18
16
19
17
using NJsonSchema ;
23
21
using System . Collections . Generic ;
24
22
using System . IO ;
25
23
using System . Linq ;
26
- using System . Management . Automation ;
27
24
using System . Reflection ;
25
+ using System . Text . RegularExpressions ;
26
+
28
27
using Tools . Common . Issues ;
29
28
using Tools . Common . Loaders ;
30
29
using Tools . Common . Loggers ;
31
30
using Tools . Common . Models ;
32
31
using Tools . Common . Utilities ;
33
32
33
+ using ParameterMetadata = Tools . Common . Models . ParameterMetadata ;
34
+ using ParameterSetMetadata = Tools . Common . Models . ParameterSetMetadata ;
35
+
34
36
namespace StaticAnalysis . UXMetadataAnalyzer
35
37
{
36
38
public class UXMetadataAnalyzer : IStaticAnalyzer
@@ -75,7 +77,7 @@ public void Analyze(
75
77
Func < string , bool > cmdletFilter ,
76
78
IEnumerable < string > modulesToAnalyze )
77
79
{
78
- var processedHelpFiles = new List < string > ( ) ;
80
+ var savedDirectory = Directory . GetCurrentDirectory ( ) ;
79
81
var issueLogger = Logger . CreateLogger < UXMetadataIssue > ( "UXMetadataIssues.csv" ) ;
80
82
81
83
if ( directoryFilter != null )
@@ -102,6 +104,10 @@ public void Analyze(
102
104
}
103
105
string moduleName = Path . GetFileName ( directory ) ;
104
106
107
+ Directory . SetCurrentDirectory ( directory ) ;
108
+
109
+ var moduleMetadata = MetadataLoader . GetModuleMetadata ( moduleName ) ;
110
+
105
111
string UXFolder = Path . Combine ( directory , "UX" ) ;
106
112
if ( ! Directory . Exists ( UXFolder ) )
107
113
{
@@ -111,24 +117,154 @@ public void Analyze(
111
117
var UXMetadataPathList = Directory . EnumerateFiles ( UXFolder , "*.json" , SearchOption . AllDirectories ) ;
112
118
foreach ( var UXMetadataPath in UXMetadataPathList )
113
119
{
114
- ValidateUXMetadata ( moduleName , UXMetadataPath , issueLogger ) ;
120
+ ValidateUXMetadata ( moduleName , UXMetadataPath , moduleMetadata , issueLogger ) ;
115
121
}
122
+ Directory . SetCurrentDirectory ( savedDirectory ) ;
116
123
}
117
124
}
118
125
}
119
126
120
- private void ValidateUXMetadata ( string moduleName , string UXMatadataPath , ReportLogger < UXMetadataIssue > issueLogger )
127
+ private void ValidateSchema ( string moduleName , string resourceType , string subResourceType , string UXMetadataContent , ReportLogger < UXMetadataIssue > issueLogger )
121
128
{
122
- string data = File . ReadAllText ( UXMatadataPath ) ;
123
- var result = schemaValidator . Validate ( data , schema ) ;
124
- string resourceType = Path . GetFileName ( Path . GetDirectoryName ( UXMatadataPath ) ) ;
129
+ var result = schemaValidator . Validate ( UXMetadataContent , schema ) ;
125
130
if ( result != null && result . Count != 0 )
126
131
{
127
132
foreach ( ValidationError error in result )
128
133
{
129
- issueLogger . LogUXMetadataIssue ( moduleName , resourceType , UXMatadataPath , 1 , error . ToString ( ) . Replace ( "\n " , "\\ n" ) ) ;
134
+ issueLogger . LogUXMetadataIssue ( moduleName , resourceType , subResourceType , null , 1 , error . ToString ( ) . Replace ( "\n " , "\\ n" ) ) ;
135
+ }
136
+ }
137
+ }
138
+
139
+ private void ValidateMetadata ( string moduleName , string resourceType , string subResourceType , string UXMetadataContent , ModuleMetadata moduleMetadata , ReportLogger < UXMetadataIssue > issueLogger )
140
+ {
141
+ UXMetadata UXMetadata = JsonConvert . DeserializeObject < UXMetadata > ( UXMetadataContent ) ;
142
+
143
+ foreach ( UXMetadataCommand command in UXMetadata . Commands )
144
+ {
145
+ string expectLearnUrl = string . Format ( "https://learn.microsoft.com/powershell/module/{0}/{1}" , moduleName , command . Name ) . ToLower ( ) ;
146
+
147
+ if ( ! expectLearnUrl . Equals ( command . Help . LearnMore . Url , StringComparison . OrdinalIgnoreCase ) )
148
+ {
149
+ string description = string . Format ( "Doc url is expect: {0} but get: {1}" , expectLearnUrl , command . Help . LearnMore . Url ) ;
150
+ issueLogger . LogUXMetadataIssue ( moduleName , resourceType , subResourceType , command . Name , 1 , description ) ;
151
+ }
152
+ if ( command . Path . IndexOf ( resourceType , StringComparison . CurrentCultureIgnoreCase ) == - 1 )
153
+ {
154
+ string description = string . Format ( "The path {0} doesn't contains the right resource tpye: {1}" , command . Path , resourceType ) ;
155
+ issueLogger . LogUXMetadataIssue ( moduleName , resourceType , subResourceType , command . Name , 2 , description ) ;
156
+ }
157
+
158
+ CmdletMetadata cmdletMetadata = moduleMetadata . Cmdlets . Find ( x => x . Name == command . Name ) ;
159
+ if ( cmdletMetadata == null )
160
+ {
161
+ string description = string . Format ( "Cmdlet {0} is not contained in {1}." , command . Name , moduleName ) ;
162
+ issueLogger . LogUXMetadataIssue ( moduleName , resourceType , subResourceType , command . Name , 1 , description ) ;
163
+ }
164
+
165
+ foreach ( UXMetadataCommandExample example in command . Examples )
166
+ {
167
+ ValidateExample ( moduleName , resourceType , subResourceType , command . Name , cmdletMetadata , example , issueLogger ) ;
168
+ }
169
+ }
170
+ }
171
+
172
+ private void ValidateExample ( string moduleName , string resourceType , string subResourceType , string commandName , CmdletMetadata cmdletMetadata , UXMetadataCommandExample example , ReportLogger < UXMetadataIssue > issueLogger )
173
+ {
174
+ List < string > parameterListConvertedFromAlias = example . Parameters . Select ( x =>
175
+ {
176
+ string parameterNameInExample = x . Name . Trim ( '-' ) ;
177
+ foreach ( ParameterMetadata parameterMetadata in cmdletMetadata . Parameters )
178
+ {
179
+ if ( parameterMetadata . Name . Equals ( parameterNameInExample , StringComparison . CurrentCultureIgnoreCase ) )
180
+ {
181
+ return parameterMetadata . Name ;
182
+ }
183
+ foreach ( string alias in parameterMetadata . AliasList )
184
+ {
185
+ if ( alias . Equals ( parameterNameInExample , StringComparison . CurrentCultureIgnoreCase ) )
186
+ {
187
+ return parameterMetadata . Name ;
188
+ }
189
+ }
190
+ }
191
+ string description = string . Format ( "Cannot find the defination of parameter {0} in example" , parameterNameInExample ) ;
192
+ issueLogger . LogUXMetadataIssue ( moduleName , resourceType , subResourceType , commandName , 1 , description ) ;
193
+ return null ;
194
+ } ) . ToList ( ) ;
195
+
196
+ HashSet < string > parametersInExample = new HashSet < string > ( parameterListConvertedFromAlias . Where ( x => x != null ) ) ;
197
+ foreach ( string parameter in parametersInExample )
198
+ {
199
+ if ( parameterListConvertedFromAlias . Count ( x => parameter . Equals ( x ) ) != 1 )
200
+ {
201
+ string description = string . Format ( "Multiply reference of parameter {0} in example" , parameter ) ;
202
+ issueLogger . LogUXMetadataIssue ( moduleName , resourceType , subResourceType , commandName , 1 , description ) ;
130
203
}
131
204
}
205
+ if ( parameterListConvertedFromAlias . Contains ( null ) )
206
+ {
207
+ return ;
208
+ }
209
+
210
+ bool findMatchedParameterSet = false ;
211
+ foreach ( ParameterSetMetadata parameterSetMetadata in cmdletMetadata . ParameterSets )
212
+ {
213
+ if ( IsExampleMatchParameterSet ( parametersInExample , parameterSetMetadata ) )
214
+ {
215
+ findMatchedParameterSet = true ;
216
+ }
217
+ }
218
+
219
+ if ( ! findMatchedParameterSet )
220
+ {
221
+ string description = string . Format ( "Cannot find a matched parameter set for example of {0}" , commandName ) ;
222
+ issueLogger . LogUXMetadataIssue ( moduleName , resourceType , subResourceType , commandName , 1 , description ) ;
223
+ }
224
+ }
225
+
226
+ private bool IsExampleMatchParameterSet ( HashSet < string > parametersInExample , ParameterSetMetadata parameterSetMetadata )
227
+ {
228
+ List < Parameter > mandatoryParameters = parameterSetMetadata . Parameters . Where ( x => x . Mandatory ) . ToList ( ) ;
229
+ foreach ( Parameter parameter in mandatoryParameters )
230
+ {
231
+ if ( ! parametersInExample . Contains ( parameter . ParameterMetadata . Name ) )
232
+ {
233
+ return false ;
234
+ }
235
+ }
236
+
237
+ foreach ( string parameterName in parametersInExample )
238
+ {
239
+ if ( ! IsParameterContainedInParameterSet ( parameterName , parameterSetMetadata ) )
240
+ {
241
+ return false ;
242
+ }
243
+ }
244
+
245
+ return true ;
246
+ }
247
+
248
+ private bool IsParameterContainedInParameterSet ( string paramenterName , ParameterSetMetadata parameterSetMetadata )
249
+ {
250
+ foreach ( Parameter parameterInfo in parameterSetMetadata . Parameters )
251
+ {
252
+ if ( parameterInfo . ParameterMetadata . Name . Equals ( paramenterName , StringComparison . CurrentCultureIgnoreCase ) )
253
+ {
254
+ return true ;
255
+ }
256
+ }
257
+
258
+ return false ;
259
+ }
260
+
261
+ private void ValidateUXMetadata ( string moduleName , string UXMetadataPath , ModuleMetadata moduleMetadata , ReportLogger < UXMetadataIssue > issueLogger )
262
+ {
263
+ string UXMetadataContent = File . ReadAllText ( UXMetadataPath ) ;
264
+ string resourceType = Path . GetFileName ( Path . GetDirectoryName ( UXMetadataPath ) ) ;
265
+ string subResourceType = Path . GetFileName ( UXMetadataPath ) . Replace ( ".json" , "" ) ;
266
+ ValidateSchema ( moduleName , resourceType , subResourceType , UXMetadataContent , issueLogger ) ;
267
+ ValidateMetadata ( moduleName , resourceType , subResourceType , UXMetadataContent , moduleMetadata , issueLogger ) ;
132
268
}
133
269
134
270
@@ -150,14 +286,15 @@ public AnalysisReport GetAnalysisReport()
150
286
public static class LogExtensions
151
287
{
152
288
public static void LogUXMetadataIssue (
153
- this ReportLogger < UXMetadataIssue > issueLogger , string module , string resourceType , string filePath ,
289
+ this ReportLogger < UXMetadataIssue > issueLogger , string module , string resourceType , string subResourceType , string command ,
154
290
int severity , string description )
155
291
{
156
292
issueLogger . LogRecord ( new UXMetadataIssue
157
293
{
158
294
Module = module ,
159
295
ResourceType = resourceType ,
160
- FilePath = filePath ,
296
+ SubResourceType = subResourceType ,
297
+ Command = command ,
161
298
Description = description ,
162
299
Severity = severity ,
163
300
} ) ;
0 commit comments