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