44using Microsoft . Extensions . Configuration ;
55using Microsoft . Graph ;
66using System ;
7+ using System . Collections . Generic ;
78using System . Threading . Tasks ;
89
910namespace DeltaQuery
1011{
1112 class Program
1213 {
1314 // The Microsoft Graph permission scopes used by the app
14- static string [ ] _scopes = { "User.Read" , "Mail.Read" } ;
15+ private static string [ ] _scopes = { "User.Read" , "Mail.Read" } ;
1516
1617 // The number of seconds to wait between delta queries
17- static int _pollIntervalInSecs = 30 ;
18- static GraphServiceClient _graphClient ;
18+ private static int _pollIntervalInSecs = 30 ;
19+
20+ // Graph client
21+ private static GraphServiceClient _graphClient ;
22+
23+ // In-memory "database" of mail folders
24+ private static List < MailFolder > _localMailFolders = new List < MailFolder > ( ) ;
1925
2026 static async Task Main ( string [ ] args )
2127 {
@@ -38,8 +44,6 @@ static async Task WatchMailFolders(int pollInterval)
3844 . Request ( )
3945 . GetAsync ( ) ;
4046
41- // TODO: Keep an in-memory dictionary of folder names and ids, use this to give more info
42- // Example: "Folder test renamed to test2" or "Folder test2 deleted" or "Folder foo moved to deleted items"
4347 while ( true )
4448 {
4549 if ( deltaCollection . CurrentPage . Count <= 0 )
@@ -51,24 +55,24 @@ static async Task WatchMailFolders(int pollInterval)
5155 bool morePagesAvailable = false ;
5256 do
5357 {
58+ // If there is a NextPageRequest, there are more pages
5459 morePagesAvailable = deltaCollection . NextPageRequest != null ;
5560 foreach ( var mailFolder in deltaCollection . CurrentPage )
5661 {
57- bool isDeleted = mailFolder . AdditionalData != null ?
58- mailFolder . AdditionalData . ContainsKey ( "@removed" ) :
59- false ;
60-
61- Console . WriteLine ( $ "Folder { mailFolder . DisplayName } { ( isDeleted ? "deleted" : "created/updated" ) } ") ;
62+ await ProcessChanges ( mailFolder ) ;
6263 }
6364
6465 if ( morePagesAvailable )
6566 {
67+ // Get the next page of results
6668 deltaCollection = await deltaCollection . NextPageRequest . GetAsync ( ) ;
6769 }
6870 }
6971 while ( morePagesAvailable ) ;
7072 }
7173
74+ // Once we've iterated through all of the pages, there should
75+ // be a delta link, which is used to request all changes since our last query
7276 var deltaLink = deltaCollection . AdditionalData [ "@odata.deltaLink" ] ;
7377 if ( ! string . IsNullOrEmpty ( deltaLink . ToString ( ) ) )
7478 {
@@ -81,6 +85,69 @@ static async Task WatchMailFolders(int pollInterval)
8185 }
8286 }
8387
88+ static async Task ProcessChanges ( MailFolder mailFolder )
89+ {
90+ // Check if the local list of folders already contains this one
91+ var localFolder = _localMailFolders . Find ( f => f . Id == mailFolder . Id ) ;
92+
93+ bool isDeleted = mailFolder . AdditionalData != null ?
94+ mailFolder . AdditionalData . ContainsKey ( "@removed" ) :
95+ false ;
96+
97+ if ( localFolder != null )
98+ {
99+ // In this case it's a delete or an update of a folder
100+ // we already know about
101+ if ( isDeleted )
102+ {
103+ // Remove the entry from the local list
104+ Console . WriteLine ( $ "Folder { localFolder . DisplayName } deleted") ;
105+ _localMailFolders . Remove ( localFolder ) ;
106+ }
107+ else
108+ {
109+ Console . WriteLine ( $ "Folder { localFolder . DisplayName } updated:") ;
110+
111+ // Was it renamed?
112+ if ( string . Compare ( localFolder . DisplayName , mailFolder . DisplayName ) != 0 )
113+ {
114+ Console . WriteLine ( $ " - Renamed to { mailFolder . DisplayName } ") ;
115+ }
116+
117+ // Was it moved?
118+ if ( string . Compare ( localFolder . ParentFolderId , mailFolder . ParentFolderId ) != 0 )
119+ {
120+ // Get the parent folder
121+ var parent = await _graphClient . Me
122+ . MailFolders [ mailFolder . ParentFolderId ]
123+ . Request ( )
124+ . GetAsync ( ) ;
125+
126+ Console . WriteLine ( $ " - Moved to { parent . DisplayName } folder") ;
127+ }
128+
129+ // Remove old entry and add new one
130+ _localMailFolders . Remove ( localFolder ) ;
131+ _localMailFolders . Add ( mailFolder ) ;
132+ }
133+ }
134+ else
135+ {
136+ // No local match
137+ if ( isDeleted )
138+ {
139+ // Folder deleted, but we never knew about it anyway
140+ Console . WriteLine ( $ "Unknown folder with ID { mailFolder . Id } deleted") ;
141+ }
142+ else
143+ {
144+ // New folder, add to local list
145+ Console . WriteLine ( $ "Folder { mailFolder . DisplayName } added") ;
146+ _localMailFolders . Add ( mailFolder ) ;
147+ }
148+ }
149+ }
150+
84151 static IConfigurationRoot LoadAppSettings ( )
85152 {
86153 // Load the values stored in the secret
0 commit comments