55using System . Collections . Generic ;
66using System . ComponentModel . Design ;
77using System . Linq ;
8- using System . Net ;
98using System . Net . Mail ;
109using System . Threading ;
1110using System . Threading . Tasks ;
1817using Microsoft . WindowsAzure . Storage ;
1918using Newtonsoft . Json ;
2019using NuGet . Jobs ;
20+ using NuGet . Services . Messaging ;
21+ using NuGet . Services . Messaging . Email ;
22+ using NuGet . Services . ServiceBus ;
2123using NuGet . Services . Storage ;
2224
2325namespace Gallery . CredentialExpiration
@@ -28,22 +30,27 @@ public class Job : JsonConfigurationJob
2830
2931 private readonly string _cursorFile = "cursorv2.json" ;
3032
31- private InitializationConfiguration Configuration { get ; set ; }
33+ private InitializationConfiguration InitializationConfiguration { get ; set ; }
34+ private MailAddress FromAddress { get ; set ; }
35+ private AsynchronousEmailMessageService EmailService { get ; set ; }
3236
3337 private Storage Storage { get ; set ; }
3438
35- private SmtpClient SmtpClient { get ; set ; }
36-
3739 public override void Init ( IServiceContainer serviceContainer , IDictionary < string , string > jobArgsDictionary )
3840 {
3941 base . Init ( serviceContainer , jobArgsDictionary ) ;
4042
41- Configuration = _serviceProvider . GetRequiredService < IOptionsSnapshot < InitializationConfiguration > > ( ) . Value ;
43+ InitializationConfiguration = _serviceProvider . GetRequiredService < IOptionsSnapshot < InitializationConfiguration > > ( ) . Value ;
44+
45+ var serializer = new ServiceBusMessageSerializer ( ) ;
46+ var topicClient = new TopicClientWrapper ( InitializationConfiguration . EmailPublisherConnectionString , InitializationConfiguration . EmailPublisherTopicName ) ;
47+ var enqueuer = new EmailMessageEnqueuer ( topicClient , serializer , LoggerFactory . CreateLogger < EmailMessageEnqueuer > ( ) ) ;
48+ EmailService = new AsynchronousEmailMessageService ( enqueuer ) ;
4249
43- SmtpClient = CreateSmtpClient ( Configuration . SmtpUri ) ;
50+ FromAddress = new MailAddress ( InitializationConfiguration . MailFrom ) ;
4451
45- var storageAccount = CloudStorageAccount . Parse ( Configuration . DataStorageAccount ) ;
46- var storageFactory = new AzureStorageFactory ( storageAccount , Configuration . ContainerName , LoggerFactory ) ;
52+ var storageAccount = CloudStorageAccount . Parse ( InitializationConfiguration . DataStorageAccount ) ;
53+ var storageFactory = new AzureStorageFactory ( storageAccount , InitializationConfiguration . ContainerName , LoggerFactory ) ;
4754 Storage = storageFactory . Create ( ) ;
4855 }
4956
@@ -53,7 +60,7 @@ public override async Task Run()
5360 // Default values
5461 var jobCursor = new JobRunTimeCursor ( jobCursorTime : jobRunTime , maxProcessedCredentialsTime : jobRunTime ) ;
5562 var galleryCredentialExpiration = new GalleryCredentialExpiration ( this ,
56- new CredentialExpirationJobMetadata ( jobRunTime , Configuration . WarnDaysBeforeExpiration , jobCursor ) ) ;
63+ new CredentialExpirationJobMetadata ( jobRunTime , InitializationConfiguration . WarnDaysBeforeExpiration , jobCursor ) ) ;
5764
5865 try
5966 {
@@ -70,7 +77,7 @@ public override async Task Run()
7077 new JsonSerializerSettings ( ) { MissingMemberHandling = MissingMemberHandling . Error } ) ;
7178
7279 galleryCredentialExpiration = new GalleryCredentialExpiration ( this ,
73- new CredentialExpirationJobMetadata ( jobRunTime , Configuration . WarnDaysBeforeExpiration , jobCursor ) ) ;
80+ new CredentialExpirationJobMetadata ( jobRunTime , InitializationConfiguration . WarnDaysBeforeExpiration , jobCursor ) ) ;
7481 }
7582
7683 // Connect to database
@@ -86,11 +93,11 @@ public override async Task Run()
8693 . ForEach ( ecd => ecd . Description = Constants . NonScopedApiKeyDescription ) ;
8794
8895 // Group credentials for each user
89- var userToExpiredCredsMapping = credentialsInRange
96+ var userToCredentialsMapping = credentialsInRange
9097 . GroupBy ( x => x . Username )
9198 . ToDictionary ( user => user . Key , value => value . ToList ( ) ) ;
9299
93- foreach ( var userCredMapping in userToExpiredCredsMapping )
100+ foreach ( var userCredMapping in userToCredentialsMapping )
94101 {
95102 var username = userCredMapping . Key ;
96103 var credentialList = userCredMapping . Value ;
@@ -99,10 +106,10 @@ public override async Task Run()
99106 var expiringCredentialList = galleryCredentialExpiration . GetExpiringCredentials ( credentialList ) ;
100107 var expiredCredentialList = galleryCredentialExpiration . GetExpiredCredentials ( credentialList ) ;
101108
102- await HandleExpiredCredentialEmail ( username , expiringCredentialList , jobRunTime , expired : false ) ;
109+ await HandleExpiredCredentialEmail ( username , expiringCredentialList , jobRunTime , areCredentialsExpired : false ) ;
103110
104111 // send expired API keys email notification
105- await HandleExpiredCredentialEmail ( username , expiredCredentialList , jobRunTime , expired : true ) ;
112+ await HandleExpiredCredentialEmail ( username , expiredCredentialList , jobRunTime , areCredentialsExpired : true ) ;
106113 }
107114 }
108115 finally
@@ -117,53 +124,35 @@ public override async Task Run()
117124 }
118125 }
119126
120- private async Task HandleExpiredCredentialEmail ( string username , List < ExpiredCredentialData > credentialList , DateTimeOffset jobRunTime , bool expired )
127+ private async Task HandleExpiredCredentialEmail ( string username , List < ExpiredCredentialData > credentials , DateTimeOffset jobRunTime , bool areCredentialsExpired )
121128 {
122- if ( credentialList == null || credentialList . Count == 0 )
129+ if ( credentials == null || credentials . Count == 0 )
123130 {
124131 return ;
125132 }
126133
127134 Logger . LogInformation ( "Handling {Expired} credential(s) (Keys: {Descriptions})..." ,
128- expired ? "expired" : "expiring" ,
129- string . Join ( ", " , credentialList . Select ( x => x . Description ) . ToList ( ) ) ) ;
130-
131- // Build message
132- var userEmail = credentialList . FirstOrDefault ( ) . EmailAddress ;
133- var mailMessage = new MailMessage ( Configuration . MailFrom , userEmail ) ;
135+ areCredentialsExpired ? "expired" : "expiring" ,
136+ string . Join ( ", " , credentials . Select ( x => x . Description ) . ToList ( ) ) ) ;
134137
135- var apiKeyExpiryMessageList = credentialList
136- . Select ( x => BuildApiKeyExpiryMessage ( x . Description , x . Expires , jobRunTime ) )
137- . ToList ( ) ;
138-
139- var apiKeyExpiryMessage = string . Join ( Environment . NewLine , apiKeyExpiryMessageList ) ;
140- // Build email body
141- if ( expired )
142- {
143- mailMessage . Subject = string . Format ( Strings . ExpiredEmailSubject , Configuration . GalleryBrand ) ;
144- mailMessage . Body = string . Format ( Strings . ExpiredEmailBody , username , Configuration . GalleryBrand , apiKeyExpiryMessage , Configuration . GalleryAccountUrl ) ;
145- }
146- else
147- {
148- mailMessage . Subject = string . Format ( Strings . ExpiringEmailSubject , Configuration . GalleryBrand ) ;
149- mailMessage . Body = string . Format ( Strings . ExpiringEmailBody , username , Configuration . GalleryBrand , apiKeyExpiryMessage , Configuration . GalleryAccountUrl ) ;
150- }
138+ var emailBuilder = new CredentialExpirationEmailBuilder (
139+ InitializationConfiguration ,
140+ FromAddress ,
141+ username ,
142+ credentials ,
143+ jobRunTime ,
144+ areCredentialsExpired ) ;
151145
152146 // Send email
153147 try
154148 {
155- if ( ! Configuration . WhatIf ) // if WhatIf is passed, we will not send e-mails (e.g. dev/int don't have to annoy users)
149+ if ( ! InitializationConfiguration . WhatIf ) // if WhatIf is passed, we will not send e-mails (e.g. dev/int don't have to annoy users)
156150 {
157- await SmtpClient . SendMailAsync ( mailMessage ) ;
151+ await EmailService . SendMessageAsync ( emailBuilder ) ;
158152 }
159153
160154 Logger . LogInformation ( "Handled {Expired} credential ." ,
161- expired ? "expired" : "expiring" ) ;
162- }
163- catch ( SmtpFailedRecipientException ex )
164- {
165- var logMessage = "Failed to handle credential - recipient failed!" ;
166- Logger . LogWarning ( LogEvents . FailedToSendMail , ex , logMessage ) ;
155+ areCredentialsExpired ? "expired" : "expiring" ) ;
167156 }
168157 catch ( Exception ex )
169158 {
@@ -174,36 +163,6 @@ private async Task HandleExpiredCredentialEmail(string username, List<ExpiredCre
174163 }
175164 }
176165
177- private static string BuildApiKeyExpiryMessage ( string description , DateTimeOffset expiry , DateTimeOffset currentTime )
178- {
179- var expiryInDays = ( expiry - currentTime ) . TotalDays ;
180- var message = expiryInDays < 0
181- ? string . Format ( Strings . ApiKeyExpired , description )
182- : string . Format ( Strings . ApiKeyExpiring , description , ( int ) expiryInDays ) ;
183-
184- // \u2022 - Unicode for bullet point.
185- return "\u2022 " + message + Environment . NewLine ;
186- }
187-
188- private SmtpClient CreateSmtpClient ( string smtpUriString )
189- {
190- var smtpUri = new SmtpUri ( new Uri ( smtpUriString ) ) ;
191- var smtpClient = new SmtpClient ( smtpUri . Host , smtpUri . Port )
192- {
193- EnableSsl = smtpUri . Secure
194- } ;
195-
196- if ( ! string . IsNullOrWhiteSpace ( smtpUri . UserName ) )
197- {
198- smtpClient . UseDefaultCredentials = false ;
199- smtpClient . Credentials = new NetworkCredential (
200- smtpUri . UserName ,
201- smtpUri . Password ) ;
202- }
203-
204- return smtpClient ;
205- }
206-
207166 protected override void ConfigureAutofacServices ( ContainerBuilder containerBuilder )
208167 {
209168 }
0 commit comments