1+ using System . Text . Json ;
2+ using Microsoft . OpenApi . Readers ;
3+
4+ namespace openapiinfo ;
5+ internal class Program
6+ {
7+ public static string openApiInfoFile = "openApiInfo.json" ;
8+ public static string openApiFileError = "openAPIErrors.csv" ;
9+ private const string openApiInfoMetadataUrl_v1 = "https://raw.githubusercontent.com/microsoftgraph/msgraph-sdk-powershell/metadata-changes-detection/openApiDocs/v1.0/OpenApiInfo/openApiInfo.json" ;
10+ private const string openApiInfoMetadataUrl_beta = "https://raw.githubusercontent.com/microsoftgraph/msgraph-sdk-powershell/metadata-changes-detection/openApiDocs/beta/OpenApiInfo/openApiInfo.json" ;
11+ private static IList < Model > openApiInfo_v1 = new OriginalMetadata ( openApiInfoMetadataUrl_v1 ) . GetOpenApiInfo ( ) ;
12+ private static IList < Model > openApiInfo_beta = new OriginalMetadata ( openApiInfoMetadataUrl_beta ) . GetOpenApiInfo ( ) ;
13+ private static IDictionary < string , IList < Model > > openApiVersions = new Dictionary < string , IList < Model > > ( ) ;
14+ private static void Main ( string [ ] args )
15+ {
16+ openApiVersions . Add ( "v1.0" , openApiInfo_v1 ) ;
17+ openApiVersions . Add ( "beta" , openApiInfo_beta ) ;
18+
19+ foreach ( var version in openApiVersions )
20+ {
21+ CompareOpenApiInfo ( version . Key , version . Value ) ;
22+ }
23+
24+ }
25+
26+ private static void CompareOpenApiInfo ( string version , IList < Model > openApiInfoMetadata )
27+ {
28+ List < Model > models = new List < Model > ( ) ;
29+ var newPathsAdded = new HashSet < string > ( ) ;
30+ var openApiErrors = new HashSet < string > ( ) ;
31+ newPathsAdded . Add ( "Module,Path,Method" ) ;
32+ openApiErrors . Add ( "Module,ApiPath,Method,From,To" ) ;
33+ var filePath = FileHandler . GetOpenApiFolder ( ) ;
34+ var combinedPath = filePath != null ? Path . Combine ( filePath , version ) : null ;
35+ var openAPiInfoPath = combinedPath != null ? Path . Combine ( combinedPath , "OpenAPiInfo" ) : null ;
36+ var combinedErrorPath = openAPiInfoPath != null ? Path . Combine ( openAPiInfoPath , openApiFileError ) : null ;
37+ if ( combinedErrorPath != null && File . Exists ( combinedErrorPath ) )
38+ {
39+ File . WriteAllText ( combinedErrorPath , string . Empty ) ;
40+ }
41+ if ( combinedPath != null )
42+ {
43+ //Go through list of openapi files
44+ foreach ( var file in Directory . GetFiles ( combinedPath ) )
45+ {
46+ using ( var sr = new StreamReader ( file ) )
47+ {
48+ var fileName = Path . GetFileNameWithoutExtension ( file ) ;
49+ var openApiDoc = new OpenApiStreamReader ( ) . Read ( sr . BaseStream , out var diagnostic ) ;
50+ if ( diagnostic . Errors . Count > 0 )
51+ {
52+ throw new Exception ( $ "Error reading openapi file { file } ") ;
53+ }
54+ //Go through each path in the openapi file
55+ foreach ( var path in openApiDoc . Paths )
56+ {
57+ var model = new Model ( ) ;
58+ var pathInfo = new PathInfo ( ) ;
59+ //Get the path key
60+ var apiPath = path . Key ;
61+ pathInfo . Path = apiPath ;
62+ pathInfo . Module = fileName ;
63+ model . PathInfo = pathInfo ;
64+ //Go through each operation in the path
65+ foreach ( var operation in path . Value . Operations )
66+ {
67+ var methodInfo = new MethodInfo ( ) ;
68+ //Get the operationId
69+ var operationId = operation . Value . OperationId ;
70+ methodInfo . OperationId = operationId ;
71+ //Get the method
72+ var method = operation . Key . ToString ( ) ;
73+ methodInfo . Method = method ;
74+ //Get the parameters
75+ var parameters = new List < Parameters > ( ) ;
76+ foreach ( var parameter in operation . Value . Parameters )
77+ {
78+ var param = new Parameters ( ) ;
79+ param . Name = parameter . Name ;
80+ param . Location = parameter . In . ToString ( ) ?? "NA" ;
81+ parameters . Add ( param ) ;
82+ }
83+ methodInfo . Parameters = parameters ;
84+ model . MethodInfo = methodInfo ;
85+ var originalPathDetails = PathDetails ( openApiInfoMetadata , operationId , apiPath , method , fileName ) ;
86+ if ( originalPathDetails == null || originalPathDetails . PathInfo == null )
87+ {
88+ newPathsAdded . Add ( $ "{ fileName } ,{ apiPath } ,{ method } ") ;
89+ continue ;
90+ }
91+ if ( originalPathDetails . MethodInfo != null && originalPathDetails . MethodInfo . Parameters != null && originalPathDetails . MethodInfo . Parameters . Count > methodInfo . Parameters . Count )
92+ {
93+ openApiErrors . Add ( $ "{ fileName } ,{ apiPath } , { method } ,Parameter Count: { methodInfo . Parameters . Count } , Parameter Count: { originalPathDetails . MethodInfo . Parameters . Count } ") ;
94+ }
95+
96+ if ( originalPathDetails . MethodInfo != null && originalPathDetails . MethodInfo . OperationId != methodInfo . OperationId )
97+ {
98+ openApiErrors . Add ( $ "{ fileName } ,{ apiPath } , { method } ,OperationId changed from: { originalPathDetails . MethodInfo . OperationId } , OperationId changed to: { methodInfo . OperationId } ") ;
99+ }
100+
101+
102+ }
103+ models . Add ( model ) ;
104+ }
105+ }
106+ }
107+ }
108+ //convert list to json and add it to file
109+ if ( newPathsAdded . Count > 1 )
110+ {
111+ var options = new JsonSerializerOptions { WriteIndented = true } ;
112+ var json = JsonSerializer . Serialize ( models , options ) ;
113+ //Clear file first
114+ File . WriteAllText ( $ "{ openAPiInfoPath } \\ { openApiInfoFile } ", string . Empty ) ;
115+ //then write to it with new content.
116+ File . WriteAllText ( $ "{ openAPiInfoPath } \\ { openApiInfoFile } ", json ) ;
117+ }
118+ if ( openApiErrors . Count > 1 )
119+ {
120+ foreach ( var error in openApiErrors )
121+ {
122+ var errList = error . Split ( "," ) ;
123+ var report = $ "{ errList [ 0 ] } ,{ errList [ 1 ] } ,{ errList [ 2 ] } ,{ errList [ 3 ] } ,{ errList [ 4 ] } ";
124+ File . AppendAllText ( $ "{ openAPiInfoPath } \\ { openApiFileError } ", report + Environment . NewLine ) ;
125+ }
126+ }
127+
128+ }
129+ private static Model PathDetails ( IList < Model > openApiInfo , string operationId , string apiPath , string method , string module )
130+ {
131+ if ( openApiInfo == null || openApiInfo . Count == 0 )
132+ {
133+ throw new Exception ( "OpenApiInfo is empty" ) ;
134+ }
135+
136+ var matchedPaths = openApiInfo . Where ( c => c . MethodInfo != null && c . MethodInfo . Method == method
137+ && c . PathInfo != null && c . PathInfo . Path == apiPath && c . PathInfo . Module == module ) . ToList ( ) ;
138+ var pathDetails = matchedPaths . FirstOrDefault ( ) ;
139+
140+ return pathDetails ?? new Model ( ) ;
141+ }
142+ }
0 commit comments