77using Microsoft . Extensions . Logging ;
88using Microsoft . OpenApi . Models ;
99using Microsoft . OpenApi . Readers ;
10+ using System . Net ;
11+ using System . Net . Http . Headers ;
1012
1113namespace DevProxy . Abstractions . Data ;
1214
@@ -49,14 +51,29 @@ public async Task<int> GenerateDbAsync(bool skipIfUpdatedToday, CancellationToke
4951 try
5052 {
5153 var dbFileInfo = new FileInfo ( MSGraphDbFilePath ) ;
52- var modifiedToday = dbFileInfo . Exists && dbFileInfo . LastWriteTime . Date == DateTime . Now . Date ;
54+ var modifiedToday = IsModifiedToday ( dbFileInfo ) ;
5355 if ( modifiedToday && skipIfUpdatedToday )
5456 {
55- _logger . LogInformation ( "Microsoft Graph database already updated today" ) ;
57+ _logger . LogInformation ( "Microsoft Graph database has already been updated today" ) ;
58+ return 1 ;
59+ }
60+
61+ var ( isApiModified , hasErrors ) = await UpdateOpenAPIGraphFilesIfNecessaryAsync ( appFolder , cancellationToken ) ;
62+
63+ if ( hasErrors )
64+ {
65+ _logger . LogWarning ( "Unable to update Microsoft Graph database" ) ;
66+ return 1 ;
67+ }
68+
69+ if ( ! isApiModified )
70+ {
71+ UpdateLastWriteTime ( dbFileInfo ) ;
72+ _logger . LogDebug ( "Updated the last-write-time attribute of Microsoft Graph database {File}" , dbFileInfo ) ;
73+ _logger . LogInformation ( "Microsoft Graph database is already up-to-date" ) ;
5674 return 1 ;
5775 }
5876
59- await UpdateOpenAPIGraphFilesIfNecessaryAsync ( appFolder , cancellationToken ) ;
6077 await LoadOpenAPIFilesAsync ( appFolder , cancellationToken ) ;
6178 if ( _openApiDocuments . Count < 1 )
6279 {
@@ -65,24 +82,21 @@ public async Task<int> GenerateDbAsync(bool skipIfUpdatedToday, CancellationToke
6582 }
6683
6784 await CreateDbAsync ( cancellationToken ) ;
68-
69- SetDbJournaling ( false ) ;
7085 await FillDataAsync ( cancellationToken ) ;
71- SetDbJournaling ( true ) ;
72-
73- _logger . LogInformation ( "Microsoft Graph database successfully updated" ) ;
7486
87+ _logger . LogInformation ( "Microsoft Graph database is successfully updated" ) ;
7588 return 0 ;
7689 }
7790 catch ( Exception ex )
7891 {
7992 _logger . LogError ( ex , "Error generating Microsoft Graph database" ) ;
8093 return 1 ;
8194 }
82-
8395 }
8496
85- private static string GetGraphOpenApiYamlFileName ( string version ) => $ "graph-{ version . Replace ( "." , "_" , StringComparison . OrdinalIgnoreCase ) } -openapi.yaml";
97+ private static bool IsModifiedToday ( FileInfo fileInfo ) => fileInfo . Exists && fileInfo . LastWriteTime . Date == DateTime . Now . Date ;
98+
99+ private static void UpdateLastWriteTime ( FileInfo fileInfo ) => fileInfo . LastWriteTime = DateTime . Now ;
86100
87101 private async Task CreateDbAsync ( CancellationToken cancellationToken )
88102 {
@@ -110,6 +124,8 @@ private async Task FillDataAsync(CancellationToken cancellationToken)
110124 {
111125 _logger . LogInformation ( "Filling database..." ) ;
112126
127+ SetDbJournaling ( false ) ;
128+
113129 await using var transaction = await Connection . BeginTransactionAsync ( cancellationToken ) ;
114130
115131 var i = 0 ;
@@ -158,38 +174,86 @@ private async Task FillDataAsync(CancellationToken cancellationToken)
158174
159175 await transaction . CommitAsync ( cancellationToken ) ;
160176
177+ SetDbJournaling ( true ) ;
178+
161179 _logger . LogInformation ( "Inserted {EndpointCount} endpoints in the database" , i ) ;
162180 }
163181
164- private async Task UpdateOpenAPIGraphFilesIfNecessaryAsync ( string folder , CancellationToken cancellationToken )
182+ private async Task < ( bool isApiUpdated , bool hasErrors ) > UpdateOpenAPIGraphFilesIfNecessaryAsync ( string folder , CancellationToken cancellationToken )
165183 {
166184 _logger . LogInformation ( "Checking for updated OpenAPI files..." ) ;
167185
186+ var isApiUpdated = false ;
187+ var hasErrors = false ;
188+
168189 foreach ( var version in graphVersions )
169190 {
170191 try
171192 {
172- var file = new FileInfo ( Path . Combine ( folder , GetGraphOpenApiYamlFileName ( version ) ) ) ;
173- _logger . LogDebug ( "Checking for updated OpenAPI file {File}..." , file ) ;
174- if ( file . Exists && file . LastWriteTime . Date == DateTime . Now . Date )
193+ var yamlFile = new FileInfo ( Path . Combine ( folder , GetGraphOpenApiYamlFileName ( version ) ) ) ;
194+ _logger . LogDebug ( "Checking for updated OpenAPI file {File}..." , yamlFile ) ;
195+ if ( IsModifiedToday ( yamlFile ) )
175196 {
176- _logger . LogInformation ( "File {File} already updated today" , file ) ;
197+ _logger . LogInformation ( "File {File} has already been updated today" , yamlFile ) ;
177198 continue ;
178199 }
179200
180- var url = $ "https://raw.githubusercontent.com/microsoftgraph/msgraph-metadata/master/openapi/ { version } /openapi.yaml" ;
201+ var url = GetOpenApiSpecUrl ( version ) ;
181202 _logger . LogInformation ( "Downloading OpenAPI file from {Url}..." , url ) ;
182203
183- var response = await _httpClient . GetStringAsync ( url , cancellationToken ) ;
184- await File . WriteAllTextAsync ( file . FullName , response , cancellationToken ) ;
185-
186- _logger . LogDebug ( "Downloaded OpenAPI file from {Url} to {File}" , url , file ) ;
204+ var etagFile = new FileInfo ( Path . Combine ( folder , GetGraphOpenApiEtagFileName ( version ) ) ) ;
205+ isApiUpdated |= await DownloadOpenAPIFileAsync ( url , yamlFile , etagFile , cancellationToken ) ;
187206 }
188207 catch ( Exception ex )
189208 {
209+ hasErrors = true ;
190210 _logger . LogError ( ex , "Error updating OpenAPI files" ) ;
191211 }
192212 }
213+ return ( isApiUpdated , hasErrors ) ;
214+ }
215+
216+ private async Task < bool > DownloadOpenAPIFileAsync ( string url , FileInfo yamlFile , FileInfo etagFile , CancellationToken cancellationToken )
217+ {
218+ var tag = string . Empty ;
219+ if ( etagFile . Exists )
220+ {
221+ tag = await File . ReadAllTextAsync ( etagFile . FullName , cancellationToken ) ;
222+ }
223+
224+ using var requestMessage = new HttpRequestMessage ( HttpMethod . Get , url ) ;
225+ if ( ! string . IsNullOrWhiteSpace ( tag ) )
226+ {
227+ requestMessage . Headers . IfNoneMatch . Add ( new EntityTagHeaderValue ( tag ) ) ;
228+ }
229+
230+ var response = await _httpClient . SendAsync ( requestMessage , cancellationToken ) ;
231+
232+ if ( response . StatusCode == HttpStatusCode . NotModified )
233+ {
234+ UpdateLastWriteTime ( yamlFile ) ;
235+ _logger . LogDebug ( "File {File} already up-to-date. Updated the last-write-time attribute" , yamlFile ) ;
236+ return false ;
237+ }
238+
239+ // Save the new OpenAPI spec.
240+ _ = response . EnsureSuccessStatusCode ( ) ;
241+ await using var contentStream = await response . Content . ReadAsStreamAsync ( cancellationToken ) ;
242+ await using var fileStream = new FileStream ( yamlFile . FullName ,
243+ new FileStreamOptions { Mode = FileMode . Create , Access = FileAccess . Write , Share = FileShare . None } ) ;
244+ await contentStream . CopyToAsync ( fileStream , cancellationToken ) ;
245+
246+ if ( response . Headers . ETag is not null )
247+ {
248+ await File . WriteAllTextAsync ( etagFile . FullName , response . Headers . ETag . Tag , cancellationToken ) ;
249+ }
250+ else
251+ {
252+ etagFile . Delete ( ) ;
253+ }
254+
255+ _logger . LogDebug ( "Downloaded OpenAPI file from {Url} to {File}" , url , yamlFile ) ;
256+ return true ;
193257 }
194258
195259 private async Task LoadOpenAPIFilesAsync ( string folder , CancellationToken cancellationToken )
@@ -238,6 +302,14 @@ private void SetDbJournaling(bool enabled)
238302 }
239303 }
240304
305+ private static string GetOpenApiSpecUrl ( string version ) => $ "https://raw.githubusercontent.com/microsoftgraph/msgraph-metadata/master/openapi/{ version } /openapi.yaml";
306+
307+ private static string GetBaseGraphOpenApiFileName ( string version ) => $ "graph-{ version . Replace ( "." , "_" , StringComparison . OrdinalIgnoreCase ) } -openapi";
308+
309+ private static string GetGraphOpenApiYamlFileName ( string version ) => $ "{ GetBaseGraphOpenApiFileName ( version ) } .yaml";
310+
311+ private static string GetGraphOpenApiEtagFileName ( string version ) => $ "{ GetBaseGraphOpenApiFileName ( version ) } .etag.txt";
312+
241313 public void Dispose ( )
242314 {
243315 _connection ? . Dispose ( ) ;
0 commit comments