• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Jericho / StrongGrid / 1200

18 Apr 2024 01:42PM UTC coverage: 73.71% (-0.8%) from 74.544%
1200

push

appveyor

Jericho
Merge branch 'release/0.107.0'

2829 of 3838 relevant lines covered (73.71%)

82.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

34.35
/Source/StrongGrid/Extensions/Public.cs
1
using StrongGrid.Models;
2
using StrongGrid.Models.Search;
3
using StrongGrid.Models.Webhooks;
4
using StrongGrid.Resources;
5
using StrongGrid.Utilities;
6
using StrongGrid.Warmup;
7
using System;
8
using System.Collections.Generic;
9
using System.IO;
10
using System.Linq;
11
using System.Reflection;
12
using System.Security;
13
using System.Security.Cryptography;
14
using System.Text;
15
using System.Threading;
16
using System.Threading.Tasks;
17

18
namespace StrongGrid
19
{
20
        /// <summary>
21
        /// Public extension methods.
22
        /// </summary>
23
        public static class Public
24
        {
25
                /// <summary>
26
                /// Send an email to a single recipient without using a template (which means you must provide the subject, html content and text content).
27
                /// </summary>
28
                /// <param name="mailResource">The mail resource.</param>
29
                /// <param name="to">To.</param>
30
                /// <param name="from">From.</param>
31
                /// <param name="subject">The subject.</param>
32
                /// <param name="htmlContent">Content of the HTML.</param>
33
                /// <param name="textContent">Content of the text.</param>
34
                /// <param name="trackOpens">if set to <c>true</c> [track opens].</param>
35
                /// <param name="trackClicks">if set to <c>true</c> [track clicks].</param>
36
                /// <param name="subscriptionTracking">The subscription tracking.</param>
37
                /// <param name="replyTo">The reply-to address.</param>
38
                /// <param name="attachments">The attachments.</param>
39
                /// <param name="headers">The headers.</param>
40
                /// <param name="categories">The categories.</param>
41
                /// <param name="customArgs">The custom arguments.</param>
42
                /// <param name="sendAt">The send at.</param>
43
                /// <param name="batchId">The batch identifier.</param>
44
                /// <param name="unsubscribeOptions">The unsubscribe options.</param>
45
                /// <param name="ipPoolName">Name of the ip pool.</param>
46
                /// <param name="mailSettings">The mail settings.</param>
47
                /// <param name="priority">The priority.</param>
48
                /// <param name="cancellationToken">The cancellation token.</param>
49
                /// <returns>
50
                /// The message id.
51
                /// </returns>
52
                /// <remarks>
53
                /// This overload is ideal when sending an email without using a template.
54
                /// This is a convenience method with simplified parameters.
55
                /// If you need more options, use the <see cref="IMail.SendAsync" /> method.
56
                /// </remarks>
57
                /// <exception cref="ArgumentOutOfRangeException">Too many recipients.</exception>
58
                /// <exception cref="Exception">Email exceeds the size limit.</exception>
59
                public static Task<string> SendToSingleRecipientAsync(
60
                        this IMail mailResource,
61
                        MailAddress to,
62
                        MailAddress from,
63
                        string subject,
64
                        string htmlContent,
65
                        string textContent,
66
                        bool trackOpens = true,
67
                        bool trackClicks = true,
68
                        SubscriptionTrackingSettings subscriptionTracking = null,
69
                        MailAddress replyTo = null,
70
                        IEnumerable<Attachment> attachments = null,
71
                        IEnumerable<KeyValuePair<string, string>> headers = null,
72
                        IEnumerable<string> categories = null,
73
                        IEnumerable<KeyValuePair<string, string>> customArgs = null,
74
                        DateTime? sendAt = null,
75
                        string batchId = null,
76
                        UnsubscribeOptions unsubscribeOptions = null,
77
                        string ipPoolName = null,
78
                        MailSettings mailSettings = null,
79
                        MailPriority priority = MailPriority.Normal,
80
                        CancellationToken cancellationToken = default)
81
                {
82
                        var recipients = new[] { to };
2✔
83
                        return mailResource.SendToMultipleRecipientsAsync(recipients, from, subject, htmlContent, textContent, trackOpens, trackClicks, subscriptionTracking, replyTo, attachments, headers, categories, customArgs, sendAt, batchId, unsubscribeOptions, ipPoolName, mailSettings, priority, cancellationToken);
2✔
84
                }
85

86
                /// <summary>
87
                /// Send an email to a single recipient using a legacy template.
88
                /// </summary>
89
                /// <param name="mailResource">The mail resource.</param>
90
                /// <param name="to">To.</param>
91
                /// <param name="from">From.</param>
92
                /// <param name="subject">The subject.</param>
93
                /// <param name="htmlContent">Content of the HTML.</param>
94
                /// <param name="textContent">Content of the text.</param>
95
                /// <param name="templateId">The template identifier.</param>
96
                /// <param name="substitutions">Data to be merged in the content.</param>
97
                /// <param name="trackOpens">if set to <c>true</c> [track opens].</param>
98
                /// <param name="trackClicks">if set to <c>true</c> [track clicks].</param>
99
                /// <param name="subscriptionTracking">The subscription tracking.</param>
100
                /// <param name="replyTo">The reply-to address.</param>
101
                /// <param name="attachments">The attachments.</param>
102
                /// <param name="headers">The headers.</param>
103
                /// <param name="categories">The categories.</param>
104
                /// <param name="customArgs">The custom arguments.</param>
105
                /// <param name="sendAt">The send at.</param>
106
                /// <param name="batchId">The batch identifier.</param>
107
                /// <param name="unsubscribeOptions">The unsubscribe options.</param>
108
                /// <param name="ipPoolName">Name of the ip pool.</param>
109
                /// <param name="mailSettings">The mail settings.</param>
110
                /// <param name="priority">The priority.</param>
111
                /// <param name="cancellationToken">The cancellation token.</param>
112
                /// <returns>
113
                /// The message id.
114
                /// </returns>
115
                /// <remarks>
116
                /// This is a convenience method with simplified parameters.
117
                /// If you need more options, use the <see cref="IMail.SendAsync" /> method.
118
                /// </remarks>
119
                /// <exception cref="ArgumentOutOfRangeException">Too many recipients.</exception>
120
                /// <exception cref="Exception">Email exceeds the size limit.</exception>
121
                public static Task<string> SendToSingleRecipientAsync(
122
                        this IMail mailResource,
123
                        MailAddress to,
124
                        MailAddress from,
125
                        string subject,
126
                        string htmlContent,
127
                        string textContent,
128
                        string templateId,
129
                        IEnumerable<KeyValuePair<string, string>> substitutions = null,
130
                        bool trackOpens = true,
131
                        bool trackClicks = true,
132
                        SubscriptionTrackingSettings subscriptionTracking = null,
133
                        MailAddress replyTo = null,
134
                        IEnumerable<Attachment> attachments = null,
135
                        IEnumerable<KeyValuePair<string, string>> headers = null,
136
                        IEnumerable<string> categories = null,
137
                        IEnumerable<KeyValuePair<string, string>> customArgs = null,
138
                        DateTime? sendAt = null,
139
                        string batchId = null,
140
                        UnsubscribeOptions unsubscribeOptions = null,
141
                        string ipPoolName = null,
142
                        MailSettings mailSettings = null,
143
                        MailPriority priority = MailPriority.Normal,
144
                        CancellationToken cancellationToken = default)
145
                {
146
                        var recipients = new[] { to };
×
147
                        return mailResource.SendToMultipleRecipientsAsync(recipients, from, subject, htmlContent, textContent, templateId, substitutions, trackOpens, trackClicks, subscriptionTracking, replyTo, attachments, headers, categories, customArgs, sendAt, batchId, unsubscribeOptions, ipPoolName, mailSettings, priority, cancellationToken);
×
148
                }
149

150
                /// <summary>
151
                /// Send an email to a single recipient using a dynamic template.
152
                /// </summary>
153
                /// <param name="mailResource">The mail resource.</param>
154
                /// <param name="to">To.</param>
155
                /// <param name="from">From.</param>
156
                /// <param name="dynamicTemplateId">The identifier of the template.</param>
157
                /// <param name="dynamicData">The data to be merged in the content.</param>
158
                /// <param name="trackOpens">if set to <c>true</c> [track opens].</param>
159
                /// <param name="trackClicks">if set to <c>true</c> [track clicks].</param>
160
                /// <param name="subscriptionTracking">The subscription tracking.</param>
161
                /// <param name="replyTo">The reply-to address.</param>
162
                /// <param name="attachments">The attachments.</param>
163
                /// <param name="headers">The headers.</param>
164
                /// <param name="categories">The categories.</param>
165
                /// <param name="customArgs">The custom arguments.</param>
166
                /// <param name="sendAt">The send at.</param>
167
                /// <param name="batchId">The batch identifier.</param>
168
                /// <param name="unsubscribeOptions">The unsubscribe options.</param>
169
                /// <param name="ipPoolName">Name of the ip pool.</param>
170
                /// <param name="mailSettings">The mail settings.</param>
171
                /// <param name="priority">The priority.</param>
172
                /// <param name="cancellationToken">The cancellation token.</param>
173
                /// <returns>
174
                /// The message id.
175
                /// </returns>
176
                /// <remarks>
177
                /// This is a convenience method with simplified parameters.
178
                /// If you need more options, use the <see cref="IMail.SendAsync" /> method.
179
                /// </remarks>
180
                /// <exception cref="ArgumentOutOfRangeException">Too many recipients.</exception>
181
                /// <exception cref="Exception">Email exceeds the size limit.</exception>
182
                public static Task<string> SendToSingleRecipientAsync(
183
                        this IMail mailResource,
184
                        MailAddress to,
185
                        MailAddress from,
186
                        string dynamicTemplateId,
187
                        object dynamicData = null,
188
                        bool trackOpens = true,
189
                        bool trackClicks = true,
190
                        SubscriptionTrackingSettings subscriptionTracking = null,
191
                        MailAddress replyTo = null,
192
                        IEnumerable<Attachment> attachments = null,
193
                        IEnumerable<KeyValuePair<string, string>> headers = null,
194
                        IEnumerable<string> categories = null,
195
                        IEnumerable<KeyValuePair<string, string>> customArgs = null,
196
                        DateTime? sendAt = null,
197
                        string batchId = null,
198
                        UnsubscribeOptions unsubscribeOptions = null,
199
                        string ipPoolName = null,
200
                        MailSettings mailSettings = null,
201
                        MailPriority priority = MailPriority.Normal,
202
                        CancellationToken cancellationToken = default)
203
                {
204
                        var recipients = new[] { to };
×
205
                        return mailResource.SendToMultipleRecipientsAsync(recipients, from, dynamicTemplateId, dynamicData, trackOpens, trackClicks, subscriptionTracking, replyTo, attachments, headers, categories, customArgs, sendAt, batchId, unsubscribeOptions, ipPoolName, mailSettings, priority, cancellationToken);
×
206
                }
207

208
                /// <summary>
209
                /// Send an AMP email to a single recipient without using a template (which means you must provide the subject, html content and text content).
210
                /// </summary>
211
                /// <param name="mailResource">The mail resource.</param>
212
                /// <param name="to">To.</param>
213
                /// <param name="from">From.</param>
214
                /// <param name="subject">The subject.</param>
215
                /// <param name="ampContent">Content of the AMP.</param>
216
                /// <param name="htmlContent">Content of the HTML.</param>
217
                /// <param name="textContent">Content of the text.</param>
218
                /// <param name="trackOpens">if set to <c>true</c> [track opens].</param>
219
                /// <param name="trackClicks">if set to <c>true</c> [track clicks].</param>
220
                /// <param name="subscriptionTracking">The subscription tracking.</param>
221
                /// <param name="replyTo">The reply-to address.</param>
222
                /// <param name="attachments">The attachments.</param>
223
                /// <param name="headers">The headers.</param>
224
                /// <param name="categories">The categories.</param>
225
                /// <param name="customArgs">The custom arguments.</param>
226
                /// <param name="sendAt">The send at.</param>
227
                /// <param name="batchId">The batch identifier.</param>
228
                /// <param name="unsubscribeOptions">The unsubscribe options.</param>
229
                /// <param name="ipPoolName">Name of the ip pool.</param>
230
                /// <param name="mailSettings">The mail settings.</param>
231
                /// <param name="priority">The priority.</param>
232
                /// <param name="cancellationToken">The cancellation token.</param>
233
                /// <returns>
234
                /// The message id.
235
                /// </returns>
236
                /// <remarks>
237
                /// This overload is ideal when sending an email without using a template.
238
                /// This is a convenience method with simplified parameters.
239
                /// If you need more options, use the <see cref="IMail.SendAsync" /> method.
240
                /// </remarks>
241
                /// <exception cref="ArgumentOutOfRangeException">Too many recipients.</exception>
242
                /// <exception cref="Exception">Email exceeds the size limit.</exception>
243
                public static Task<string> SendAmpEmailToSingleRecipientAsync(
244
                        this IMail mailResource,
245
                        MailAddress to,
246
                        MailAddress from,
247
                        string subject,
248
                        string ampContent,
249
                        string htmlContent,
250
                        string textContent,
251
                        bool trackOpens = true,
252
                        bool trackClicks = true,
253
                        SubscriptionTrackingSettings subscriptionTracking = null,
254
                        MailAddress replyTo = null,
255
                        IEnumerable<Attachment> attachments = null,
256
                        IEnumerable<KeyValuePair<string, string>> headers = null,
257
                        IEnumerable<string> categories = null,
258
                        IEnumerable<KeyValuePair<string, string>> customArgs = null,
259
                        DateTime? sendAt = null,
260
                        string batchId = null,
261
                        UnsubscribeOptions unsubscribeOptions = null,
262
                        string ipPoolName = null,
263
                        MailSettings mailSettings = null,
264
                        MailPriority priority = MailPriority.Normal,
265
                        CancellationToken cancellationToken = default)
266
                {
267
                        var recipients = new[] { to };
×
268
                        return mailResource.SendAmpEmailToMultipleRecipientsAsync(recipients, from, subject, ampContent, htmlContent, textContent, trackOpens, trackClicks, subscriptionTracking, replyTo, attachments, headers, categories, customArgs, sendAt, batchId, unsubscribeOptions, ipPoolName, mailSettings, priority, cancellationToken);
×
269
                }
270

271
                /// <summary>
272
                /// Send the same email to multiple recipients without using a template (which means you must provide the subject, html content and text content).
273
                /// </summary>
274
                /// <param name="mailResource">The mail resource.</param>
275
                /// <param name="recipients">The recipients.</param>
276
                /// <param name="from">From.</param>
277
                /// <param name="subject">The subject.</param>
278
                /// <param name="htmlContent">Content of the HTML.</param>
279
                /// <param name="textContent">Content of the text.</param>
280
                /// <param name="trackOpens">if set to <c>true</c> [track opens].</param>
281
                /// <param name="trackClicks">if set to <c>true</c> [track clicks].</param>
282
                /// <param name="subscriptionTracking">The subscription tracking.</param>
283
                /// <param name="replyTo">The reply-to address.</param>
284
                /// <param name="attachments">The attachments.</param>
285
                /// <param name="headers">The headers.</param>
286
                /// <param name="categories">The categories.</param>
287
                /// <param name="customArgs">The custom arguments.</param>
288
                /// <param name="sendAt">The send at.</param>
289
                /// <param name="batchId">The batch identifier.</param>
290
                /// <param name="unsubscribeOptions">The unsubscribe options.</param>
291
                /// <param name="ipPoolName">Name of the ip pool.</param>
292
                /// <param name="mailSettings">The mail settings.</param>
293
                /// <param name="priority">The priority.</param>
294
                /// <param name="cancellationToken">The cancellation token.</param>
295
                /// <returns>
296
                /// The message id.
297
                /// </returns>
298
                /// <remarks>
299
                /// This is a convenience method with simplified parameters.
300
                /// If you need more options, use the <see cref="IMail.SendAsync" /> method.
301
                /// </remarks>
302
                /// <exception cref="ArgumentOutOfRangeException">Too many recipients.</exception>
303
                /// <exception cref="Exception">Email exceeds the size limit.</exception>
304
                public static Task<string> SendToMultipleRecipientsAsync(
305
                        this IMail mailResource,
306
                        IEnumerable<MailAddress> recipients,
307
                        MailAddress from,
308
                        string subject,
309
                        string htmlContent,
310
                        string textContent,
311
                        bool trackOpens = true,
312
                        bool trackClicks = true,
313
                        SubscriptionTrackingSettings subscriptionTracking = null,
314
                        MailAddress replyTo = null,
315
                        IEnumerable<Attachment> attachments = null,
316
                        IEnumerable<KeyValuePair<string, string>> headers = null,
317
                        IEnumerable<string> categories = null,
318
                        IEnumerable<KeyValuePair<string, string>> customArgs = null,
319
                        DateTime? sendAt = null,
320
                        string batchId = null,
321
                        UnsubscribeOptions unsubscribeOptions = null,
322
                        string ipPoolName = null,
323
                        MailSettings mailSettings = null,
324
                        MailPriority priority = MailPriority.Normal,
325
                        CancellationToken cancellationToken = default)
326
                {
327
                        var personalizations = new[]
3✔
328
                        {
3✔
329
                                new MailPersonalization
3✔
330
                                {
3✔
331
                                        To = recipients.ToArray()
3✔
332
                                }
3✔
333
                        };
3✔
334

335
                        var contents = new List<MailContent>();
3✔
336
                        if (!string.IsNullOrEmpty(textContent)) contents.Add(new MailContent("text/plain", textContent));
3✔
337
                        if (!string.IsNullOrEmpty(htmlContent)) contents.Add(new MailContent("text/html", htmlContent));
3✔
338

339
                        var trackingSettings = new TrackingSettings
3✔
340
                        {
3✔
341
                                ClickTracking = new ClickTrackingSettings
3✔
342
                                {
3✔
343
                                        EnabledInHtmlContent = trackClicks,
3✔
344
                                        EnabledInTextContent = trackClicks
3✔
345
                                },
3✔
346
                                OpenTracking = new OpenTrackingSettings { Enabled = trackOpens },
3✔
347
                                GoogleAnalytics = new GoogleAnalyticsSettings { Enabled = false },
3✔
348
                                SubscriptionTracking = subscriptionTracking
3✔
349
                        };
3✔
350

351
                        var replyToList = replyTo == null ? null : new[] { replyTo };
3✔
352

353
                        return mailResource.SendAsync(personalizations, subject, contents, from, replyToList, attachments, null, headers, categories, customArgs, sendAt, batchId, unsubscribeOptions, ipPoolName, mailSettings, trackingSettings, priority, cancellationToken);
3✔
354
                }
355

356
                /// <summary>
357
                /// Send the same email to multiple recipients using a legacy template.
358
                /// </summary>
359
                /// <param name="mailResource">The mail resource.</param>
360
                /// <param name="recipients">The recipients.</param>
361
                /// <param name="from">From.</param>
362
                /// <param name="subject">The subject.</param>
363
                /// <param name="htmlContent">Content of the HTML.</param>
364
                /// <param name="textContent">Content of the text.</param>
365
                /// <param name="templateId">The template identifier.</param>
366
                /// <param name="substitutions">Data to be merged in the content.</param>
367
                /// <param name="trackOpens">if set to <c>true</c> [track opens].</param>
368
                /// <param name="trackClicks">if set to <c>true</c> [track clicks].</param>
369
                /// <param name="subscriptionTracking">The subscription tracking.</param>
370
                /// <param name="replyTo">The reply-to address.</param>
371
                /// <param name="attachments">The attachments.</param>
372
                /// <param name="headers">The headers.</param>
373
                /// <param name="categories">The categories.</param>
374
                /// <param name="customArgs">The custom arguments.</param>
375
                /// <param name="sendAt">The send at.</param>
376
                /// <param name="batchId">The batch identifier.</param>
377
                /// <param name="unsubscribeOptions">The unsubscribe options.</param>
378
                /// <param name="ipPoolName">Name of the ip pool.</param>
379
                /// <param name="mailSettings">The mail settings.</param>
380
                /// <param name="priority">The priority.</param>
381
                /// <param name="cancellationToken">The cancellation token.</param>
382
                /// <returns>
383
                /// The message id.
384
                /// </returns>
385
                /// <remarks>
386
                /// This is a convenience method with simplified parameters.
387
                /// If you need more options, use the <see cref="IMail.SendAsync" /> method.
388
                /// </remarks>
389
                /// <exception cref="ArgumentOutOfRangeException">Too many recipients.</exception>
390
                /// <exception cref="Exception">Email exceeds the size limit.</exception>
391
                public static Task<string> SendToMultipleRecipientsAsync(
392
                        this IMail mailResource,
393
                        IEnumerable<MailAddress> recipients,
394
                        MailAddress from,
395
                        string subject,
396
                        string htmlContent,
397
                        string textContent,
398
                        string templateId,
399
                        IEnumerable<KeyValuePair<string, string>> substitutions = null,
400
                        bool trackOpens = true,
401
                        bool trackClicks = true,
402
                        SubscriptionTrackingSettings subscriptionTracking = null,
403
                        MailAddress replyTo = null,
404
                        IEnumerable<Attachment> attachments = null,
405
                        IEnumerable<KeyValuePair<string, string>> headers = null,
406
                        IEnumerable<string> categories = null,
407
                        IEnumerable<KeyValuePair<string, string>> customArgs = null,
408
                        DateTime? sendAt = null,
409
                        string batchId = null,
410
                        UnsubscribeOptions unsubscribeOptions = null,
411
                        string ipPoolName = null,
412
                        MailSettings mailSettings = null,
413
                        MailPriority priority = MailPriority.Normal,
414
                        CancellationToken cancellationToken = default)
415
                {
416
                        var personalizations = new[]
×
417
                        {
×
418
                                new MailPersonalization
×
419
                                {
×
420
                                        To = recipients.ToArray(),
×
421
                                        Substitutions = substitutions?.ToArray()
×
422
                                }
×
423
                        };
×
424

425
                        var contents = new List<MailContent>();
×
426
                        if (!string.IsNullOrEmpty(textContent)) contents.Add(new MailContent("text/plain", textContent));
×
427
                        if (!string.IsNullOrEmpty(htmlContent)) contents.Add(new MailContent("text/html", htmlContent));
×
428

429
                        var trackingSettings = new TrackingSettings
×
430
                        {
×
431
                                ClickTracking = new ClickTrackingSettings
×
432
                                {
×
433
                                        EnabledInHtmlContent = trackClicks,
×
434
                                        EnabledInTextContent = trackClicks
×
435
                                },
×
436
                                OpenTracking = new OpenTrackingSettings { Enabled = trackOpens },
×
437
                                GoogleAnalytics = new GoogleAnalyticsSettings { Enabled = false },
×
438
                                SubscriptionTracking = subscriptionTracking
×
439
                        };
×
440

441
                        var replyToList = replyTo == null ? null : new[] { replyTo };
×
442

443
                        return mailResource.SendAsync(personalizations, subject, contents, from, replyToList, attachments, templateId, headers, categories, customArgs, sendAt, batchId, unsubscribeOptions, ipPoolName, mailSettings, trackingSettings, priority, cancellationToken);
×
444
                }
445

446
                /// <summary>
447
                /// Send the same email to multiple recipients.
448
                /// </summary>
449
                /// <param name="mailResource">The mail resource.</param>
450
                /// <param name="recipients">The recipients.</param>
451
                /// <param name="from">From.</param>
452
                /// <param name="dynamicTemplateId">The identifier of the template.</param>
453
                /// <param name="dynamicData">The data to be merged in the content.</param>
454
                /// <param name="trackOpens">if set to <c>true</c> [track opens].</param>
455
                /// <param name="trackClicks">if set to <c>true</c> [track clicks].</param>
456
                /// <param name="subscriptionTracking">The subscription tracking.</param>
457
                /// <param name="replyTo">The reply-to address.</param>
458
                /// <param name="attachments">The attachments.</param>
459
                /// <param name="headers">The headers.</param>
460
                /// <param name="categories">The categories.</param>
461
                /// <param name="customArgs">The custom arguments.</param>
462
                /// <param name="sendAt">The send at.</param>
463
                /// <param name="batchId">The batch identifier.</param>
464
                /// <param name="unsubscribeOptions">The unsubscribe options.</param>
465
                /// <param name="ipPoolName">Name of the ip pool.</param>
466
                /// <param name="mailSettings">The mail settings.</param>
467
                /// <param name="priority">The priority.</param>
468
                /// <param name="cancellationToken">The cancellation token.</param>
469
                /// <returns>
470
                /// The message id.
471
                /// </returns>
472
                /// <remarks>
473
                /// This is a convenience method with simplified parameters.
474
                /// If you need more options, use the <see cref="IMail.SendAsync" /> method.
475
                /// </remarks>
476
                /// <exception cref="ArgumentOutOfRangeException">Too many recipients.</exception>
477
                /// <exception cref="Exception">Email exceeds the size limit.</exception>
478
                public static Task<string> SendToMultipleRecipientsAsync(
479
                        this IMail mailResource,
480
                        IEnumerable<MailAddress> recipients,
481
                        MailAddress from,
482
                        string dynamicTemplateId,
483
                        object dynamicData = null,
484
                        bool trackOpens = true,
485
                        bool trackClicks = true,
486
                        SubscriptionTrackingSettings subscriptionTracking = null,
487
                        MailAddress replyTo = null,
488
                        IEnumerable<Attachment> attachments = null,
489
                        IEnumerable<KeyValuePair<string, string>> headers = null,
490
                        IEnumerable<string> categories = null,
491
                        IEnumerable<KeyValuePair<string, string>> customArgs = null,
492
                        DateTime? sendAt = null,
493
                        string batchId = null,
494
                        UnsubscribeOptions unsubscribeOptions = null,
495
                        string ipPoolName = null,
496
                        MailSettings mailSettings = null,
497
                        MailPriority priority = MailPriority.Normal,
498
                        CancellationToken cancellationToken = default)
499
                {
500
                        if (!Template.IsDynamic(dynamicTemplateId))
×
501
                        {
502
                                throw new ArgumentException($"{dynamicTemplateId} is not a valid dynamic template identifier.", nameof(dynamicTemplateId));
×
503
                        }
504

505
                        var personalizations = new[]
×
506
                        {
×
507
                                new MailPersonalization
×
508
                                {
×
509
                                        To = recipients.ToArray(),
×
510
                                        DynamicData = dynamicData
×
511
                                }
×
512
                        };
×
513

514
                        var trackingSettings = new TrackingSettings
×
515
                        {
×
516
                                ClickTracking = new ClickTrackingSettings
×
517
                                {
×
518
                                        EnabledInHtmlContent = trackClicks,
×
519
                                        EnabledInTextContent = trackClicks
×
520
                                },
×
521
                                OpenTracking = new OpenTrackingSettings { Enabled = trackOpens },
×
522
                                GoogleAnalytics = new GoogleAnalyticsSettings { Enabled = false },
×
523
                                SubscriptionTracking = subscriptionTracking
×
524
                        };
×
525

526
                        var replyToList = replyTo == null ? null : new[] { replyTo };
×
527

528
                        return mailResource.SendAsync(personalizations, null, null, from, replyToList, attachments, dynamicTemplateId, headers, categories, customArgs, sendAt, batchId, unsubscribeOptions, ipPoolName, mailSettings, trackingSettings, priority, cancellationToken);
×
529
                }
530

531
                /// <summary>
532
                /// Send the same AMP email to multiple recipients.
533
                /// </summary>
534
                /// <param name="mailResource">The mail resource.</param>
535
                /// <param name="recipients">The recipients.</param>
536
                /// <param name="from">From.</param>
537
                /// <param name="subject">The subject.</param>
538
                /// <param name="ampContent">Content of the AMP.</param>
539
                /// <param name="htmlContent">Content of the HTML.</param>
540
                /// <param name="textContent">Content of the text.</param>
541
                /// <param name="trackOpens">if set to <c>true</c> [track opens].</param>
542
                /// <param name="trackClicks">if set to <c>true</c> [track clicks].</param>
543
                /// <param name="subscriptionTracking">The subscription tracking.</param>
544
                /// <param name="replyTo">The reply-to address.</param>
545
                /// <param name="attachments">The attachments.</param>
546
                /// <param name="headers">The headers.</param>
547
                /// <param name="categories">The categories.</param>
548
                /// <param name="customArgs">The custom arguments.</param>
549
                /// <param name="sendAt">The send at.</param>
550
                /// <param name="batchId">The batch identifier.</param>
551
                /// <param name="unsubscribeOptions">The unsubscribe options.</param>
552
                /// <param name="ipPoolName">Name of the ip pool.</param>
553
                /// <param name="mailSettings">The mail settings.</param>
554
                /// <param name="priority">The priority.</param>
555
                /// <param name="cancellationToken">The cancellation token.</param>
556
                /// <returns>
557
                /// The message id.
558
                /// </returns>
559
                /// <remarks>
560
                /// This is a convenience method with simplified parameters.
561
                /// If you need more options, use the <see cref="IMail.SendAsync" /> method.
562
                /// </remarks>
563
                /// <exception cref="ArgumentOutOfRangeException">Too many recipients.</exception>
564
                /// <exception cref="Exception">Email exceeds the size limit.</exception>
565
                public static Task<string> SendAmpEmailToMultipleRecipientsAsync(
566
                        this IMail mailResource,
567
                        IEnumerable<MailAddress> recipients,
568
                        MailAddress from,
569
                        string subject,
570
                        string ampContent,
571
                        string htmlContent,
572
                        string textContent,
573
                        bool trackOpens = true,
574
                        bool trackClicks = true,
575
                        SubscriptionTrackingSettings subscriptionTracking = null,
576
                        MailAddress replyTo = null,
577
                        IEnumerable<Attachment> attachments = null,
578
                        IEnumerable<KeyValuePair<string, string>> headers = null,
579
                        IEnumerable<string> categories = null,
580
                        IEnumerable<KeyValuePair<string, string>> customArgs = null,
581
                        DateTime? sendAt = null,
582
                        string batchId = null,
583
                        UnsubscribeOptions unsubscribeOptions = null,
584
                        string ipPoolName = null,
585
                        MailSettings mailSettings = null,
586
                        MailPriority priority = MailPriority.Normal,
587
                        CancellationToken cancellationToken = default)
588
                {
589
                        var personalizations = new[]
×
590
                        {
×
591
                                new MailPersonalization
×
592
                                {
×
593
                                        To = recipients.ToArray()
×
594
                                }
×
595
                        };
×
596

597
                        var contents = new List<MailContent>();
×
598
                        if (!string.IsNullOrEmpty(textContent)) contents.Add(new MailContent("text/plain", textContent));
×
599
                        if (!string.IsNullOrEmpty(ampContent)) contents.Add(new MailContent("text/x-amp-html", ampContent));
×
600
                        if (!string.IsNullOrEmpty(htmlContent)) contents.Add(new MailContent("text/html", htmlContent));
×
601

602
                        var trackingSettings = new TrackingSettings
×
603
                        {
×
604
                                ClickTracking = new ClickTrackingSettings
×
605
                                {
×
606
                                        EnabledInHtmlContent = trackClicks,
×
607
                                        EnabledInTextContent = trackClicks
×
608
                                },
×
609
                                OpenTracking = new OpenTrackingSettings { Enabled = trackOpens },
×
610
                                GoogleAnalytics = new GoogleAnalyticsSettings { Enabled = false },
×
611
                                SubscriptionTracking = subscriptionTracking
×
612
                        };
×
613

614
                        var replyToList = replyTo == null ? null : new[] { replyTo };
×
615

616
                        return mailResource.SendAsync(personalizations, subject, contents, from, replyToList, attachments, null, headers, categories, customArgs, sendAt, batchId, unsubscribeOptions, ipPoolName, mailSettings, trackingSettings, priority, cancellationToken);
×
617
                }
618

619
                /// <summary>
620
                /// Send an email to a single recipient without using a template (which means you must provide the subject, html content and text content).
621
                /// </summary>
622
                /// <param name="warmupEngine">The warmup engine.</param>
623
                /// <param name="to">To.</param>
624
                /// <param name="from">From.</param>
625
                /// <param name="subject">The subject.</param>
626
                /// <param name="htmlContent">Content of the HTML.</param>
627
                /// <param name="textContent">Content of the text.</param>
628
                /// <param name="trackOpens">if set to <c>true</c> [track opens].</param>
629
                /// <param name="trackClicks">if set to <c>true</c> [track clicks].</param>
630
                /// <param name="subscriptionTracking">The subscription tracking.</param>
631
                /// <param name="replyTo">The reply-to address.</param>
632
                /// <param name="attachments">The attachments.</param>
633
                /// <param name="headers">The headers.</param>
634
                /// <param name="categories">The categories.</param>
635
                /// <param name="customArgs">The custom arguments.</param>
636
                /// <param name="sendAt">The send at.</param>
637
                /// <param name="batchId">The batch identifier.</param>
638
                /// <param name="unsubscribeOptions">The unsubscribe options.</param>
639
                /// <param name="mailSettings">The mail settings.</param>
640
                /// <param name="priority">The priority.</param>
641
                /// <param name="cancellationToken">The cancellation token.</param>
642
                /// <returns>
643
                /// The result.
644
                /// </returns>
645
                /// <remarks>
646
                /// This overload is ideal when sending an email without using a template.
647
                /// This is a convenience method with simplified parameters.
648
                /// If you need more options, use the <see cref="IWarmupEngine.SendAsync" /> method.
649
                /// </remarks>
650
                /// <exception cref="ArgumentOutOfRangeException">Too many recipients.</exception>
651
                /// <exception cref="Exception">Email exceeds the size limit.</exception>
652
                public static Task<WarmupResult> SendToSingleRecipientAsync(
653
                        this IWarmupEngine warmupEngine,
654
                        MailAddress to,
655
                        MailAddress from,
656
                        string subject,
657
                        string htmlContent,
658
                        string textContent,
659
                        bool trackOpens = true,
660
                        bool trackClicks = true,
661
                        SubscriptionTrackingSettings subscriptionTracking = null,
662
                        MailAddress replyTo = null,
663
                        IEnumerable<Attachment> attachments = null,
664
                        IEnumerable<KeyValuePair<string, string>> headers = null,
665
                        IEnumerable<string> categories = null,
666
                        IEnumerable<KeyValuePair<string, string>> customArgs = null,
667
                        DateTime? sendAt = null,
668
                        string batchId = null,
669
                        UnsubscribeOptions unsubscribeOptions = null,
670
                        MailSettings mailSettings = null,
671
                        MailPriority priority = MailPriority.Normal,
672
                        CancellationToken cancellationToken = default)
673
                {
674
                        var recipients = new[] { to };
×
675
                        return warmupEngine.SendToMultipleRecipientsAsync(recipients, from, subject, htmlContent, textContent, trackOpens, trackClicks, subscriptionTracking, replyTo, attachments, headers, categories, customArgs, sendAt, batchId, unsubscribeOptions, mailSettings, priority, cancellationToken);
×
676
                }
677

678
                /// <summary>
679
                /// Send an email to a single recipient using a legacy template.
680
                /// </summary>
681
                /// <param name="warmupEngine">The warmup engine.</param>
682
                /// <param name="to">To.</param>
683
                /// <param name="from">From.</param>
684
                /// <param name="subject">The subject.</param>
685
                /// <param name="htmlContent">Content of the HTML.</param>
686
                /// <param name="textContent">Content of the text.</param>
687
                /// <param name="templateId">The template identifier.</param>
688
                /// <param name="substitutions">Data to be merged in the content.</param>
689
                /// <param name="trackOpens">if set to <c>true</c> [track opens].</param>
690
                /// <param name="trackClicks">if set to <c>true</c> [track clicks].</param>
691
                /// <param name="subscriptionTracking">The subscription tracking.</param>
692
                /// <param name="replyTo">The reply-to address.</param>
693
                /// <param name="attachments">The attachments.</param>
694
                /// <param name="headers">The headers.</param>
695
                /// <param name="categories">The categories.</param>
696
                /// <param name="customArgs">The custom arguments.</param>
697
                /// <param name="sendAt">The send at.</param>
698
                /// <param name="batchId">The batch identifier.</param>
699
                /// <param name="unsubscribeOptions">The unsubscribe options.</param>
700
                /// <param name="mailSettings">The mail settings.</param>
701
                /// <param name="priority">The priority.</param>
702
                /// <param name="cancellationToken">The cancellation token.</param>
703
                /// <returns>
704
                /// The result.
705
                /// </returns>
706
                /// <remarks>
707
                /// This is a convenience method with simplified parameters.
708
                /// If you need more options, use the <see cref="IWarmupEngine.SendAsync" /> method.
709
                /// </remarks>
710
                /// <exception cref="ArgumentOutOfRangeException">Too many recipients.</exception>
711
                /// <exception cref="Exception">Email exceeds the size limit.</exception>
712
                public static Task<WarmupResult> SendToSingleRecipientAsync(
713
                        this IWarmupEngine warmupEngine,
714
                        MailAddress to,
715
                        MailAddress from,
716
                        string subject,
717
                        string htmlContent,
718
                        string textContent,
719
                        string templateId,
720
                        IEnumerable<KeyValuePair<string, string>> substitutions = null,
721
                        bool trackOpens = true,
722
                        bool trackClicks = true,
723
                        SubscriptionTrackingSettings subscriptionTracking = null,
724
                        MailAddress replyTo = null,
725
                        IEnumerable<Attachment> attachments = null,
726
                        IEnumerable<KeyValuePair<string, string>> headers = null,
727
                        IEnumerable<string> categories = null,
728
                        IEnumerable<KeyValuePair<string, string>> customArgs = null,
729
                        DateTime? sendAt = null,
730
                        string batchId = null,
731
                        UnsubscribeOptions unsubscribeOptions = null,
732
                        MailSettings mailSettings = null,
733
                        MailPriority priority = MailPriority.Normal,
734
                        CancellationToken cancellationToken = default)
735
                {
736
                        var recipients = new[] { to };
1✔
737
                        return warmupEngine.SendToMultipleRecipientsAsync(recipients, from, subject, htmlContent, textContent, templateId, substitutions, trackOpens, trackClicks, subscriptionTracking, replyTo, attachments, headers, categories, customArgs, sendAt, batchId, unsubscribeOptions, mailSettings, priority, cancellationToken);
1✔
738
                }
739

740
                /// <summary>
741
                /// Send an email to a single recipient using a dynamic template.
742
                /// </summary>
743
                /// <param name="warmupEngine">The warmup engine.</param>
744
                /// <param name="to">To.</param>
745
                /// <param name="from">From.</param>
746
                /// <param name="dynamicTemplateId">The identifier of the template.</param>
747
                /// <param name="dynamicData">The data to be merged in the content.</param>
748
                /// <param name="trackOpens">if set to <c>true</c> [track opens].</param>
749
                /// <param name="trackClicks">if set to <c>true</c> [track clicks].</param>
750
                /// <param name="subscriptionTracking">The subscription tracking.</param>
751
                /// <param name="replyTo">The reply-to address.</param>
752
                /// <param name="attachments">The attachments.</param>
753
                /// <param name="headers">The headers.</param>
754
                /// <param name="categories">The categories.</param>
755
                /// <param name="customArgs">The custom arguments.</param>
756
                /// <param name="sendAt">The send at.</param>
757
                /// <param name="batchId">The batch identifier.</param>
758
                /// <param name="unsubscribeOptions">The unsubscribe options.</param>
759
                /// <param name="mailSettings">The mail settings.</param>
760
                /// <param name="priority">The priority.</param>
761
                /// <param name="cancellationToken">The cancellation token.</param>
762
                /// <returns>
763
                /// The result.
764
                /// </returns>
765
                /// <remarks>
766
                /// This is a convenience method with simplified parameters.
767
                /// If you need more options, use the <see cref="IWarmupEngine.SendAsync" /> method.
768
                /// </remarks>
769
                /// <exception cref="ArgumentOutOfRangeException">Too many recipients.</exception>
770
                /// <exception cref="Exception">Email exceeds the size limit.</exception>
771
                public static Task<WarmupResult> SendToSingleRecipientAsync(
772
                        this IWarmupEngine warmupEngine,
773
                        MailAddress to,
774
                        MailAddress from,
775
                        string dynamicTemplateId,
776
                        object dynamicData = null,
777
                        bool trackOpens = true,
778
                        bool trackClicks = true,
779
                        SubscriptionTrackingSettings subscriptionTracking = null,
780
                        MailAddress replyTo = null,
781
                        IEnumerable<Attachment> attachments = null,
782
                        IEnumerable<KeyValuePair<string, string>> headers = null,
783
                        IEnumerable<string> categories = null,
784
                        IEnumerable<KeyValuePair<string, string>> customArgs = null,
785
                        DateTime? sendAt = null,
786
                        string batchId = null,
787
                        UnsubscribeOptions unsubscribeOptions = null,
788
                        MailSettings mailSettings = null,
789
                        MailPriority priority = MailPriority.Normal,
790
                        CancellationToken cancellationToken = default)
791
                {
792
                        var recipients = new[] { to };
×
793
                        return warmupEngine.SendToMultipleRecipientsAsync(recipients, from, dynamicTemplateId, dynamicData, trackOpens, trackClicks, subscriptionTracking, replyTo, attachments, headers, categories, customArgs, sendAt, batchId, unsubscribeOptions, mailSettings, priority, cancellationToken);
×
794
                }
795

796
                /// <summary>
797
                /// Send the same email to multiple recipients without using a template (which means you must provide the subject, html content and text content).
798
                /// </summary>
799
                /// <param name="warmupEngine">The warmup engine.</param>
800
                /// <param name="recipients">The recipients.</param>
801
                /// <param name="from">From.</param>
802
                /// <param name="subject">The subject.</param>
803
                /// <param name="htmlContent">Content of the HTML.</param>
804
                /// <param name="textContent">Content of the text.</param>
805
                /// <param name="trackOpens">if set to <c>true</c> [track opens].</param>
806
                /// <param name="trackClicks">if set to <c>true</c> [track clicks].</param>
807
                /// <param name="subscriptionTracking">The subscription tracking.</param>
808
                /// <param name="replyTo">The reply-to address.</param>
809
                /// <param name="attachments">The attachments.</param>
810
                /// <param name="headers">The headers.</param>
811
                /// <param name="categories">The categories.</param>
812
                /// <param name="customArgs">The custom arguments.</param>
813
                /// <param name="sendAt">The send at.</param>
814
                /// <param name="batchId">The batch identifier.</param>
815
                /// <param name="unsubscribeOptions">The unsubscribe options.</param>
816
                /// <param name="mailSettings">The mail settings.</param>
817
                /// <param name="priority">The priority.</param>
818
                /// <param name="cancellationToken">The cancellation token.</param>
819
                /// <returns>
820
                /// The result.
821
                /// </returns>
822
                /// <remarks>
823
                /// This is a convenience method with simplified parameters.
824
                /// If you need more options, use the <see cref="IWarmupEngine.SendAsync" /> method.
825
                /// </remarks>
826
                /// <exception cref="ArgumentOutOfRangeException">Too many recipients.</exception>
827
                /// <exception cref="Exception">Email exceeds the size limit.</exception>
828
                public static Task<WarmupResult> SendToMultipleRecipientsAsync(
829
                        this IWarmupEngine warmupEngine,
830
                        IEnumerable<MailAddress> recipients,
831
                        MailAddress from,
832
                        string subject,
833
                        string htmlContent,
834
                        string textContent,
835
                        bool trackOpens = true,
836
                        bool trackClicks = true,
837
                        SubscriptionTrackingSettings subscriptionTracking = null,
838
                        MailAddress replyTo = null,
839
                        IEnumerable<Attachment> attachments = null,
840
                        IEnumerable<KeyValuePair<string, string>> headers = null,
841
                        IEnumerable<string> categories = null,
842
                        IEnumerable<KeyValuePair<string, string>> customArgs = null,
843
                        DateTime? sendAt = null,
844
                        string batchId = null,
845
                        UnsubscribeOptions unsubscribeOptions = null,
846
                        MailSettings mailSettings = null,
847
                        MailPriority priority = MailPriority.Normal,
848
                        CancellationToken cancellationToken = default)
849
                {
850
                        var personalizations = new[]
×
851
                        {
×
852
                                new MailPersonalization
×
853
                                {
×
854
                                        To = recipients.ToArray()
×
855
                                }
×
856
                        };
×
857

858
                        var contents = new List<MailContent>();
×
859
                        if (!string.IsNullOrEmpty(textContent)) contents.Add(new MailContent("text/plain", textContent));
×
860
                        if (!string.IsNullOrEmpty(htmlContent)) contents.Add(new MailContent("text/html", htmlContent));
×
861

862
                        var trackingSettings = new TrackingSettings
×
863
                        {
×
864
                                ClickTracking = new ClickTrackingSettings
×
865
                                {
×
866
                                        EnabledInHtmlContent = trackClicks,
×
867
                                        EnabledInTextContent = trackClicks
×
868
                                },
×
869
                                OpenTracking = new OpenTrackingSettings { Enabled = trackOpens },
×
870
                                GoogleAnalytics = new GoogleAnalyticsSettings { Enabled = false },
×
871
                                SubscriptionTracking = subscriptionTracking
×
872
                        };
×
873

874
                        var replyToList = replyTo == null ? null : new[] { replyTo };
×
875

876
                        return warmupEngine.SendAsync(personalizations, subject, contents, from, replyToList, attachments, null, headers, categories, customArgs, sendAt, batchId, unsubscribeOptions, mailSettings, trackingSettings, priority, cancellationToken);
×
877
                }
878

879
                /// <summary>
880
                /// Send the same email to multiple recipients using a legacy template.
881
                /// </summary>
882
                /// <param name="warmupEngine">The warmup engine.</param>
883
                /// <param name="recipients">The recipients.</param>
884
                /// <param name="from">From.</param>
885
                /// <param name="subject">The subject.</param>
886
                /// <param name="htmlContent">Content of the HTML.</param>
887
                /// <param name="textContent">Content of the text.</param>
888
                /// <param name="templateId">The template identifier.</param>
889
                /// <param name="substitutions">Data to be merged in the content.</param>
890
                /// <param name="trackOpens">if set to <c>true</c> [track opens].</param>
891
                /// <param name="trackClicks">if set to <c>true</c> [track clicks].</param>
892
                /// <param name="subscriptionTracking">The subscription tracking.</param>
893
                /// <param name="replyTo">The reply-to address.</param>
894
                /// <param name="attachments">The attachments.</param>
895
                /// <param name="headers">The headers.</param>
896
                /// <param name="categories">The categories.</param>
897
                /// <param name="customArgs">The custom arguments.</param>
898
                /// <param name="sendAt">The send at.</param>
899
                /// <param name="batchId">The batch identifier.</param>
900
                /// <param name="unsubscribeOptions">The unsubscribe options.</param>
901
                /// <param name="mailSettings">The mail settings.</param>
902
                /// <param name="priority">The priority.</param>
903
                /// <param name="cancellationToken">The cancellation token.</param>
904
                /// <returns>
905
                /// The result.
906
                /// </returns>
907
                /// <remarks>
908
                /// This is a convenience method with simplified parameters.
909
                /// If you need more options, use the <see cref="IWarmupEngine.SendAsync" /> method.
910
                /// </remarks>
911
                /// <exception cref="ArgumentOutOfRangeException">Too many recipients.</exception>
912
                /// <exception cref="Exception">Email exceeds the size limit.</exception>
913
                public static Task<WarmupResult> SendToMultipleRecipientsAsync(
914
                        this IWarmupEngine warmupEngine,
915
                        IEnumerable<MailAddress> recipients,
916
                        MailAddress from,
917
                        string subject,
918
                        string htmlContent,
919
                        string textContent,
920
                        string templateId,
921
                        IEnumerable<KeyValuePair<string, string>> substitutions = null,
922
                        bool trackOpens = true,
923
                        bool trackClicks = true,
924
                        SubscriptionTrackingSettings subscriptionTracking = null,
925
                        MailAddress replyTo = null,
926
                        IEnumerable<Attachment> attachments = null,
927
                        IEnumerable<KeyValuePair<string, string>> headers = null,
928
                        IEnumerable<string> categories = null,
929
                        IEnumerable<KeyValuePair<string, string>> customArgs = null,
930
                        DateTime? sendAt = null,
931
                        string batchId = null,
932
                        UnsubscribeOptions unsubscribeOptions = null,
933
                        MailSettings mailSettings = null,
934
                        MailPriority priority = MailPriority.Normal,
935
                        CancellationToken cancellationToken = default)
936
                {
937
                        var personalizations = new[]
1✔
938
                        {
1✔
939
                                new MailPersonalization
1✔
940
                                {
1✔
941
                                        To = recipients.ToArray(),
1✔
942
                                        Substitutions = substitutions?.ToArray()
1✔
943
                                }
1✔
944
                        };
1✔
945

946
                        var contents = new List<MailContent>();
1✔
947
                        if (!string.IsNullOrEmpty(textContent)) contents.Add(new MailContent("text/plain", textContent));
1✔
948
                        if (!string.IsNullOrEmpty(htmlContent)) contents.Add(new MailContent("text/html", htmlContent));
1✔
949

950
                        var trackingSettings = new TrackingSettings
1✔
951
                        {
1✔
952
                                ClickTracking = new ClickTrackingSettings
1✔
953
                                {
1✔
954
                                        EnabledInHtmlContent = trackClicks,
1✔
955
                                        EnabledInTextContent = trackClicks
1✔
956
                                },
1✔
957
                                OpenTracking = new OpenTrackingSettings { Enabled = trackOpens },
1✔
958
                                GoogleAnalytics = new GoogleAnalyticsSettings { Enabled = false },
1✔
959
                                SubscriptionTracking = subscriptionTracking
1✔
960
                        };
1✔
961

962
                        var replyToList = replyTo == null ? null : new[] { replyTo };
1✔
963

964
                        return warmupEngine.SendAsync(personalizations, subject, contents, from, replyToList, attachments, templateId, headers, categories, customArgs, sendAt, batchId, unsubscribeOptions, mailSettings, trackingSettings, priority, cancellationToken);
1✔
965
                }
966

967
                /// <summary>
968
                /// Send the same email to multiple recipients.
969
                /// </summary>
970
                /// <param name="warmupEngine">The warmup engine.</param>
971
                /// <param name="recipients">The recipients.</param>
972
                /// <param name="from">From.</param>
973
                /// <param name="dynamicTemplateId">The identifier of the template.</param>
974
                /// <param name="dynamicData">The data to be merged in the content.</param>
975
                /// <param name="trackOpens">if set to <c>true</c> [track opens].</param>
976
                /// <param name="trackClicks">if set to <c>true</c> [track clicks].</param>
977
                /// <param name="subscriptionTracking">The subscription tracking.</param>
978
                /// <param name="replyTo">The reply-to address.</param>
979
                /// <param name="attachments">The attachments.</param>
980
                /// <param name="headers">The headers.</param>
981
                /// <param name="categories">The categories.</param>
982
                /// <param name="customArgs">The custom arguments.</param>
983
                /// <param name="sendAt">The send at.</param>
984
                /// <param name="batchId">The batch identifier.</param>
985
                /// <param name="unsubscribeOptions">The unsubscribe options.</param>
986
                /// <param name="mailSettings">The mail settings.</param>
987
                /// <param name="priority">The priority.</param>
988
                /// <param name="cancellationToken">The cancellation token.</param>
989
                /// <returns>
990
                /// The result.
991
                /// </returns>
992
                /// <remarks>
993
                /// This is a convenience method with simplified parameters.
994
                /// If you need more options, use the <see cref="IWarmupEngine.SendAsync" /> method.
995
                /// </remarks>
996
                /// <exception cref="ArgumentOutOfRangeException">Too many recipients.</exception>
997
                /// <exception cref="Exception">Email exceeds the size limit.</exception>
998
                public static Task<WarmupResult> SendToMultipleRecipientsAsync(
999
                        this IWarmupEngine warmupEngine,
1000
                        IEnumerable<MailAddress> recipients,
1001
                        MailAddress from,
1002
                        string dynamicTemplateId,
1003
                        object dynamicData = null,
1004
                        bool trackOpens = true,
1005
                        bool trackClicks = true,
1006
                        SubscriptionTrackingSettings subscriptionTracking = null,
1007
                        MailAddress replyTo = null,
1008
                        IEnumerable<Attachment> attachments = null,
1009
                        IEnumerable<KeyValuePair<string, string>> headers = null,
1010
                        IEnumerable<string> categories = null,
1011
                        IEnumerable<KeyValuePair<string, string>> customArgs = null,
1012
                        DateTime? sendAt = null,
1013
                        string batchId = null,
1014
                        UnsubscribeOptions unsubscribeOptions = null,
1015
                        MailSettings mailSettings = null,
1016
                        MailPriority priority = MailPriority.Normal,
1017
                        CancellationToken cancellationToken = default)
1018
                {
1019
                        if (!Template.IsDynamic(dynamicTemplateId))
×
1020
                        {
1021
                                throw new ArgumentException($"{dynamicTemplateId} is not a valid dynamic template identifier.", nameof(dynamicTemplateId));
×
1022
                        }
1023

1024
                        var personalizations = new[]
×
1025
                        {
×
1026
                                new MailPersonalization
×
1027
                                {
×
1028
                                        To = recipients.ToArray(),
×
1029
                                        DynamicData = dynamicData
×
1030
                                }
×
1031
                        };
×
1032

1033
                        var trackingSettings = new TrackingSettings
×
1034
                        {
×
1035
                                ClickTracking = new ClickTrackingSettings
×
1036
                                {
×
1037
                                        EnabledInHtmlContent = trackClicks,
×
1038
                                        EnabledInTextContent = trackClicks
×
1039
                                },
×
1040
                                OpenTracking = new OpenTrackingSettings { Enabled = trackOpens },
×
1041
                                GoogleAnalytics = new GoogleAnalyticsSettings { Enabled = false },
×
1042
                                SubscriptionTracking = subscriptionTracking
×
1043
                        };
×
1044

1045
                        var replyToList = replyTo == null ? null : new[] { replyTo };
×
1046

1047
                        return warmupEngine.SendAsync(personalizations, null, null, from, replyToList, attachments, dynamicTemplateId, headers, categories, customArgs, sendAt, batchId, unsubscribeOptions, mailSettings, trackingSettings, priority, cancellationToken);
×
1048
                }
1049

1050
                /// <summary>
1051
                /// Get all of the details about the messages matching the criteria.
1052
                /// </summary>
1053
                /// <param name="emailActivities">The email activities resource.</param>
1054
                /// <param name="criteria">Filtering criteria.</param>
1055
                /// <param name="limit">Number of IP activity entries to return.</param>
1056
                /// <param name="cancellationToken">Cancellation token.</param>
1057
                /// <returns>
1058
                /// An array of <see cref="EmailMessageActivity" />.
1059
                /// </returns>
1060
                public static Task<EmailMessageActivity[]> SearchAsync(this IEmailActivities emailActivities, ISearchCriteria criteria, int limit = 20, CancellationToken cancellationToken = default)
1061
                {
1062
                        var filterCriteria = criteria == null ? Enumerable.Empty<ISearchCriteria>() : new[] { criteria };
2✔
1063
                        return emailActivities.SearchAsync(filterCriteria, limit, cancellationToken);
2✔
1064
                }
1065

1066
                /// <summary>
1067
                /// Get all of the details about the messages matching the criteria.
1068
                /// </summary>
1069
                /// <param name="emailActivities">The email activities resource.</param>
1070
                /// <param name="filterConditions">Filtering conditions.</param>
1071
                /// <param name="limit">Number of IP activity entries to return.</param>
1072
                /// <param name="cancellationToken">Cancellation token.</param>
1073
                /// <returns>
1074
                /// An array of <see cref="EmailMessageActivity" />.
1075
                /// </returns>
1076
                public static Task<EmailMessageActivity[]> SearchAsync(this IEmailActivities emailActivities, IEnumerable<ISearchCriteria> filterConditions, int limit = 20, CancellationToken cancellationToken = default)
1077
                {
1078
                        var filters = new List<KeyValuePair<SearchLogicalOperator, IEnumerable<ISearchCriteria>>>();
3✔
1079
                        if (filterConditions != null && filterConditions.Any()) filters.Add(new KeyValuePair<SearchLogicalOperator, IEnumerable<ISearchCriteria>>(SearchLogicalOperator.And, filterConditions));
6✔
1080
                        return emailActivities.SearchAsync(filters, limit, cancellationToken);
3✔
1081
                }
1082

1083
                /// <summary>
1084
                /// Get all of the details about the contacts matching the criteria.
1085
                /// </summary>
1086
                /// <param name="contacts">The contacts resource.</param>
1087
                /// <param name="criteria">Filtering criteria.</param>
1088
                /// <param name="cancellationToken">Cancellation token.</param>
1089
                /// <returns>
1090
                /// An array of <see cref="Contact" />.
1091
                /// </returns>
1092
                public static Task<Contact[]> SearchAsync(this IContacts contacts, ISearchCriteria criteria, CancellationToken cancellationToken = default)
1093
                {
1094
                        var filterCriteria = criteria == null ? Array.Empty<SearchCriteria>() : new[] { criteria };
×
1095
                        return contacts.SearchAsync(filterCriteria, cancellationToken);
×
1096
                }
1097

1098
                /// <summary>
1099
                /// Get all of the details about the contacts matching the criteria.
1100
                /// </summary>
1101
                /// <param name="contacts">The contacts resource.</param>
1102
                /// <param name="filterConditions">Filtering conditions.</param>
1103
                /// <param name="cancellationToken">Cancellation token.</param>
1104
                /// <returns>
1105
                /// An array of <see cref="Contact" />.
1106
                /// </returns>
1107
                public static Task<Contact[]> SearchAsync(this IContacts contacts, IEnumerable<ISearchCriteria> filterConditions, CancellationToken cancellationToken = default)
1108
                {
1109
                        var filters = new List<KeyValuePair<SearchLogicalOperator, IEnumerable<ISearchCriteria>>>();
×
1110
                        if (filterConditions != null && filterConditions.Any()) filters.Add(new KeyValuePair<SearchLogicalOperator, IEnumerable<ISearchCriteria>>(SearchLogicalOperator.And, filterConditions));
×
1111
                        return contacts.SearchAsync(filters, cancellationToken);
×
1112
                }
1113

1114
                /// <summary>
1115
                /// Get all of the details about the contacts matching the criteria.
1116
                /// </summary>
1117
                /// <param name="contacts">The contacts resource.</param>
1118
                /// <param name="filterConditions">Filtering conditions.</param>
1119
                /// <param name="cancellationToken">Cancellation token.</param>
1120
                /// <returns>
1121
                /// An array of <see cref="Contact" />.
1122
                /// </returns>
1123
                public static Task<Contact[]> SearchAsync(this IContacts contacts, IEnumerable<KeyValuePair<SearchLogicalOperator, IEnumerable<ISearchCriteria>>> filterConditions, CancellationToken cancellationToken = default)
1124
                {
1125
                        var query = Utils.ToQueryDslVersion1(filterConditions);
×
1126
                        return contacts.SearchAsync(query, cancellationToken);
×
1127
                }
1128

1129
                /// <summary>
1130
                /// Create a segment.
1131
                /// </summary>
1132
                /// <param name="segments">The segments resource.</param>
1133
                /// <param name="name">The name.</param>
1134
                /// <param name="criteria">The criteria.</param>
1135
                /// <param name="listId">The id of the list if this segment is a child of a list. This implies the query is rewritten as (${query_dsl}) AND CONTAINS(list_ids, ${parent_list_id}).</param>
1136
                /// <param name="cancellationToken">The cancellation token.</param>
1137
                /// <returns>
1138
                /// The <see cref="Segment" />.
1139
                /// </returns>
1140
                public static Task<Segment> CreateAsync(this ISegments segments, string name, ISearchCriteria criteria, string listId = null, CancellationToken cancellationToken = default)
1141
                {
1142
                        var filterCriteria = criteria != null ? new[] { criteria } : Array.Empty<ISearchCriteria>();
×
1143
                        return segments.CreateAsync(name, filterCriteria, listId, cancellationToken);
×
1144
                }
1145

1146
                /// <summary>
1147
                /// Create a segment.
1148
                /// </summary>
1149
                /// <param name="segments">The segments resource.</param>
1150
                /// <param name="name">The name.</param>
1151
                /// <param name="filterConditions">The enumeration of criteria.</param>
1152
                /// <param name="listId">The id of the list if this segment is a child of a list. This implies the query is rewritten as (${query_dsl}) AND CONTAINS(list_ids, ${parent_list_id}).</param>
1153
                /// <param name="cancellationToken">The cancellation token.</param>
1154
                /// <returns>
1155
                /// The <see cref="Segment" />.
1156
                /// </returns>
1157
                public static Task<Segment> CreateAsync(this ISegments segments, string name, IEnumerable<ISearchCriteria> filterConditions, string listId = null, CancellationToken cancellationToken = default)
1158
                {
1159
                        var filters = new List<KeyValuePair<SearchLogicalOperator, IEnumerable<ISearchCriteria>>>();
×
1160
                        if (filterConditions != null && filterConditions.Any()) filters.Add(new KeyValuePair<SearchLogicalOperator, IEnumerable<ISearchCriteria>>(SearchLogicalOperator.And, filterConditions));
×
1161
                        return segments.CreateAsync(name, filters, listId, cancellationToken);
×
1162
                }
1163

1164
                /// <summary>
1165
                /// Create a segment.
1166
                /// </summary>
1167
                /// <param name="segments">The segments resource.</param>
1168
                /// <param name="name">The name.</param>
1169
                /// <param name="filterConditions">The enumeration of criteria.</param>
1170
                /// <param name="listId">The id of the list if this segment is a child of a list. This implies the query is rewritten as (${query_dsl}) AND CONTAINS(list_ids, ${parent_list_id}).</param>
1171
                /// <param name="cancellationToken">The cancellation token.</param>
1172
                /// <returns>
1173
                /// The <see cref="Segment" />.
1174
                /// </returns>
1175
                public static Task<Segment> CreateAsync(this ISegments segments, string name, IEnumerable<KeyValuePair<SearchLogicalOperator, IEnumerable<ISearchCriteria>>> filterConditions, string listId = null, CancellationToken cancellationToken = default)
1176
                {
1177
                        var query = Utils.ToQueryDslVersion2(filterConditions);
×
1178
                        return segments.CreateAsync(name, query, listId, QueryLanguageVersion.Version2, cancellationToken);
×
1179
                }
1180

1181
                /// <summary>
1182
                /// Update a segment.
1183
                /// </summary>
1184
                /// <param name="segments">The segments resource.</param>
1185
                /// <param name="segmentId">The segment identifier.</param>
1186
                /// <param name="name">The name.</param>
1187
                /// <param name="criteria">The criteria.</param>
1188
                /// <param name="cancellationToken">The cancellation token.</param>
1189
                /// <returns>
1190
                /// The <see cref="Segment" />.
1191
                /// </returns>
1192
                public static Task<Segment> UpdateAsync(this ISegments segments, string segmentId, Parameter<string> name = default, Parameter<ISearchCriteria> criteria = default, CancellationToken cancellationToken = default)
1193
                {
1194
                        var filterCriteria = criteria.HasValue && criteria.Value != null ? new[] { criteria.Value } : Array.Empty<ISearchCriteria>();
×
1195
                        return segments.UpdateAsync(segmentId, name, filterCriteria, cancellationToken);
×
1196
                }
1197

1198
                /// <summary>
1199
                /// Update a segment.
1200
                /// </summary>
1201
                /// <param name="segments">The segments resource.</param>
1202
                /// <param name="segmentId">The segment identifier.</param>
1203
                /// <param name="name">The name.</param>
1204
                /// <param name="filterConditions">The enumeration of criteria.</param>
1205
                /// <param name="cancellationToken">The cancellation token.</param>
1206
                /// <returns>
1207
                /// The <see cref="Segment" />.
1208
                /// </returns>
1209
                public static Task<Segment> UpdateAsync(this ISegments segments, string segmentId, Parameter<string> name = default, Parameter<IEnumerable<ISearchCriteria>> filterConditions = default, CancellationToken cancellationToken = default)
1210
                {
1211
                        var filters = new List<KeyValuePair<SearchLogicalOperator, IEnumerable<ISearchCriteria>>>();
×
1212
                        if (filterConditions.HasValue && filterConditions.Value != null && filterConditions.Value.Any()) filters.Add(new KeyValuePair<SearchLogicalOperator, IEnumerable<ISearchCriteria>>(SearchLogicalOperator.And, filterConditions.Value));
×
1213
                        return segments.UpdateAsync(segmentId, name, filters, cancellationToken);
×
1214
                }
1215

1216
                /// <summary>
1217
                /// Update a segment.
1218
                /// </summary>
1219
                /// <param name="segments">The segments resource.</param>
1220
                /// <param name="segmentId">The segment identifier.</param>
1221
                /// <param name="name">The name.</param>
1222
                /// <param name="filterConditions">The enumeration of criteria.</param>
1223
                /// <param name="cancellationToken">The cancellation token.</param>
1224
                /// <returns>
1225
                /// The <see cref="Segment" />.
1226
                /// </returns>
1227
                public static Task<Segment> UpdateAsync(this ISegments segments, string segmentId, Parameter<string> name = default, Parameter<IEnumerable<KeyValuePair<SearchLogicalOperator, IEnumerable<ISearchCriteria>>>> filterConditions = default, CancellationToken cancellationToken = default)
1228
                {
1229
                        var query = filterConditions.HasValue ? (Parameter<string>)Utils.ToQueryDslVersion2(filterConditions.Value) : (Parameter<string>)default;
×
1230
                        return segments.UpdateAsync(segmentId, name, query, QueryLanguageVersion.Version2, cancellationToken);
×
1231
                }
1232

1233
                /// <summary>
1234
                /// Retrieve unassigned IP addresses.
1235
                /// </summary>
1236
                /// <param name="ipAddresses">The IP addresses resource.</param>
1237
                /// <param name="cancellationToken">Cancellation token.</param>
1238
                /// <returns>
1239
                /// An array of <see cref="IpAddress">Ip addresses</see>.
1240
                /// </returns>
1241
                public static async Task<IpAddress[]> GetUnassignedAsync(this IIpAddresses ipAddresses, CancellationToken cancellationToken = default)
1242
                {
1243
                        var unassignedIpAddresses = new List<IpAddress>();
1244
                        var currentOffset = 0;
1245

1246
                        while (true)
1247
                        {
1248
                                // Retrieve 500 ip addresses at a time (that's the maximum SendGrid allow us to retrieve at a time)
1249
                                var allIpAddresses = await ipAddresses.GetAllAsync(limit: Utils.MaxSendGridPagingLimit, offset: currentOffset, cancellationToken: cancellationToken).ConfigureAwait(false);
1250

1251
                                // Take the addresses that have not been added to a pool
1252
                                unassignedIpAddresses.AddRange(allIpAddresses.Where(ip => ip.Pools == null || !ip.Pools.Any()));
1253

1254
                                // Stop if there are no more addresses to fetch
1255
                                if (allIpAddresses.Length < Utils.MaxSendGridPagingLimit) break;
1256

1257
                                // Increase the offset so we retrieve the next set of 500 addresses
1258
                                currentOffset += Utils.MaxSendGridPagingLimit;
1259
                        }
1260

1261
                        return unassignedIpAddresses.ToArray();
1262
                }
1263

1264
                /// <summary>
1265
                /// Download the files generated by an export job and save them to files.
1266
                /// </summary>
1267
                /// <param name="contacts">The Contacts resource.</param>
1268
                /// <param name="jobId">The job identifier.</param>
1269
                /// <param name="destinationFolder">The folder where the files will be saved.</param>
1270
                /// <param name="decompress">Indicate if GZip compressed files should be automatically decompressed.</param>
1271
                /// <param name="cancellationToken">Cancellation token.</param>
1272
                /// <returns>
1273
                /// The async task.
1274
                /// </returns>
1275
                public static async Task DownloadExportFilesAsync(this IContacts contacts, string jobId, string destinationFolder, bool decompress = false, CancellationToken cancellationToken = default)
1276
                {
1277
                        var exportFiles = await contacts.DownloadExportFilesAsync(jobId, decompress, cancellationToken).ConfigureAwait(false);
1278

1279
                        foreach (var exportFile in exportFiles)
1280
                        {
1281
                                var destinationPath = Path.Combine(destinationFolder, exportFile.FileName);
1282
                                using (Stream output = File.OpenWrite(destinationPath))
1283
                                {
1284
#if NET5_0_OR_GREATER
1285
                                        await exportFile.Stream.CopyToAsync(output, cancellationToken).ConfigureAwait(false);
1286
#else
1287
                                        await exportFile.Stream.CopyToAsync(output).ConfigureAwait(false);
1288
#endif
1289
                                }
1290
                        }
1291
                }
1292

1293
                /// <summary>
1294
                /// Download the files generated by an export job as streams.
1295
                /// </summary>
1296
                /// <param name="contacts">The Contacts resource.</param>
1297
                /// <param name="jobId">The job identifier.</param>
1298
                /// <param name="decompress">Indicate if GZip compressed files should be automatically decompressed.</param>
1299
                /// <param name="cancellationToken">Cancellation token.</param>
1300
                /// <returns>
1301
                /// An array of <see cref="Stream"/>.
1302
                /// </returns>
1303
                public static async Task<(string FileName, Stream Stream)[]> DownloadExportFilesAsync(this IContacts contacts, string jobId, bool decompress = false, CancellationToken cancellationToken = default)
1304
                {
1305
                        var job = await contacts.GetExportJobAsync(jobId, cancellationToken).ConfigureAwait(false);
1306
                        return await contacts.DownloadExportFilesAsync(job, decompress, cancellationToken).ConfigureAwait(false);
1307
                }
1308

1309
                /// <summary>
1310
                /// Download the CSV and save it to a file.
1311
                /// </summary>
1312
                /// <param name="emailActivities">The Email Activities resource.</param>
1313
                /// <param name="downloadUUID">UUID used to locate the download CSV request entry. You can find this UUID in the email that is sent with the POST Request a CSV.</param>
1314
                /// <param name="destinationPath">The path and name of the CSV file.</param>
1315
                /// <param name="cancellationToken">Cancellation token.</param>
1316
                /// <returns>
1317
                /// The async task.
1318
                /// </returns>
1319
                public static async Task DownloadCsvAsync(this IEmailActivities emailActivities, string downloadUUID, string destinationPath, CancellationToken cancellationToken = default)
1320
                {
1321
                        using (var responseStream = await emailActivities.DownloadCsvAsync(downloadUUID, cancellationToken).ConfigureAwait(false))
1322
                        {
1323
                                using (Stream output = File.OpenWrite(destinationPath))
1324
                                {
1325
#if NET5_0_OR_GREATER
1326
                                        await responseStream.CopyToAsync(output, cancellationToken).ConfigureAwait(false);
1327
#else
1328
                                        await responseStream.CopyToAsync(output).ConfigureAwait(false);
1329
#endif
1330
                                }
1331
                        }
1332
                }
1333

1334
                /// <summary>
1335
                /// Add recipient address to the suppressions list for a given group.
1336
                /// If the group has been deleted, this request will add the address to the global suppression.
1337
                /// </summary>
1338
                /// <param name="suppressions">The Suppressions resource.</param>
1339
                /// <param name="groupId">ID of the suppression group.</param>
1340
                /// <param name="email">Email address to add to the suppression group.</param>
1341
                /// <param name="onBehalfOf">The user to impersonate.</param>
1342
                /// <param name="cancellationToken">The cancellation token.</param>
1343
                /// <returns>
1344
                /// The async task.
1345
                /// </returns>
1346
                public static Task AddAddressToUnsubscribeGroupAsync(this ISuppressions suppressions, long groupId, string email, string onBehalfOf = null, CancellationToken cancellationToken = default)
1347
                {
1348
                        return suppressions.AddAddressesToUnsubscribeGroupAsync(groupId, new[] { email }, onBehalfOf, cancellationToken);
1✔
1349
                }
1350

1351
                /// <summary>
1352
                /// Generate a new API Key for billing.
1353
                /// </summary>
1354
                /// <param name="apiKeys">The ApiKeys resource.</param>
1355
                /// <param name="name">The name.</param>
1356
                /// <param name="onBehalfOf">The user to impersonate.</param>
1357
                /// <param name="cancellationToken">Cancellation token.</param>
1358
                /// <returns>
1359
                /// The <see cref="ApiKey" />.
1360
                /// </returns>
1361
                public static Task<ApiKey> CreateWithBillingPermissionsAsync(this IApiKeys apiKeys, string name, string onBehalfOf = null, CancellationToken cancellationToken = default)
1362
                {
1363
                        var scopes = new[]
1✔
1364
                        {
1✔
1365
                                "billing.delete",
1✔
1366
                                "billing.read",
1✔
1367
                                "billing.update"
1✔
1368
                        };
1✔
1369

1370
                        return apiKeys.CreateAsync(name, scopes, onBehalfOf, cancellationToken);
1✔
1371
                }
1372

1373
                /// <summary>
1374
                /// Generate a new API Key with the same permissions that have been granted to you.
1375
                /// </summary>
1376
                /// <param name="apiKeys">The ApiKeys resource.</param>
1377
                /// <param name="name">The name.</param>
1378
                /// <param name="onBehalfOf">The user to impersonate.</param>
1379
                /// <param name="cancellationToken">Cancellation token.</param>
1380
                /// <returns>
1381
                /// The <see cref="ApiKey" />.
1382
                /// </returns>
1383
                /// <remarks>
1384
                /// If you specify an API Key when instanciating the <see cref="Client" />, the new API Key will inherit the permissions of that API Key.
1385
                /// If you specify a username and password when instanciating the <see cref="Client" />, the new API Key will inherit the permissions of that user.
1386
                /// </remarks>
1387
                public static async Task<ApiKey> CreateWithAllPermissionsAsync(this IApiKeys apiKeys, string name, string onBehalfOf = null, CancellationToken cancellationToken = default)
1388
                {
1389
                        var privateField = apiKeys.GetType().GetField("_client", BindingFlags.NonPublic | BindingFlags.Instance);
1390
                        if (privateField == null) throw new ArgumentException("Unable to find the HttpClient in the resource.", nameof(apiKeys));
1391
                        var client = (Pathoschild.Http.Client.IClient)privateField.GetValue(apiKeys);
1392

1393
                        var scopes = await client.GetCurrentScopes(true, cancellationToken).ConfigureAwait(false);
1394
                        var superApiKey = await apiKeys.CreateAsync(name, scopes, onBehalfOf, cancellationToken).ConfigureAwait(false);
1395
                        return superApiKey;
1396
                }
1397

1398
                /// <summary>
1399
                /// Generate a new API Key with the same "read" permissions that have ben granted to you.
1400
                /// </summary>
1401
                /// <param name="apiKeys">The ApiKeys resource.</param>
1402
                /// <param name="name">The name.</param>
1403
                /// <param name="onBehalfOf">The user to impersonate.</param>
1404
                /// <param name="cancellationToken">Cancellation token.</param>
1405
                /// <returns>
1406
                /// The <see cref="ApiKey" />.
1407
                /// </returns>
1408
                /// <remarks>
1409
                /// If you specify an API Key when instanciating the <see cref="Client" />, the new API Key will inherit the "read" permissions of that API Key.
1410
                /// If you specify a username and password when instanciating the <see cref="Client" />, the new API Key will inherit the "read" permissions of that user.
1411
                /// </remarks>
1412
                public static async Task<ApiKey> CreateWithReadOnlyPermissionsAsync(this IApiKeys apiKeys, string name, string onBehalfOf = null, CancellationToken cancellationToken = default)
1413
                {
1414
                        var privateField = apiKeys.GetType().GetField("_client", BindingFlags.NonPublic | BindingFlags.Instance);
1415
                        if (privateField == null) throw new ArgumentException("Unable to find the HttpClient in the resource.", nameof(apiKeys));
1416
                        var client = (Pathoschild.Http.Client.IClient)privateField.GetValue(apiKeys);
1417

1418
                        var scopes = await client.GetCurrentScopes(true, cancellationToken).ConfigureAwait(false);
1419
                        scopes = scopes.Where(s => s.EndsWith(".read", System.StringComparison.OrdinalIgnoreCase)).ToArray();
1420

1421
                        var readOnlyApiKey = await apiKeys.CreateAsync(name, scopes, onBehalfOf, cancellationToken).ConfigureAwait(false);
1422
                        return readOnlyApiKey;
1423
                }
1424

1425
                /// <summary>
1426
                /// Send a teammate invitation via email with the same "read" permissions that have been granted to you.
1427
                /// A teammate invite will expire after 7 days, but you may resend the invite at any time
1428
                /// to reset the expiration date.
1429
                /// </summary>
1430
                /// <param name="teammates">The teammates resource.</param>
1431
                /// <param name="email">The email address of the teammate.</param>
1432
                /// <param name="cancellationToken">The cancellation token.</param>
1433
                /// <returns>
1434
                /// The async task.
1435
                /// </returns>
1436
                /// <remarks>
1437
                /// Essentials, Legacy Lite, and Free Trial users may create up to one teammate per account.
1438
                /// There is not a teammate limit for Pro and higher plans.
1439
                /// </remarks>
1440
                public static async Task<TeammateInvitation> InviteTeammateWithReadOnlyPrivilegesAsync(this ITeammates teammates, string email, CancellationToken cancellationToken = default)
1441
                {
1442
                        var privateField = teammates.GetType().GetField("_client", BindingFlags.NonPublic | BindingFlags.Instance);
1443
                        if (privateField == null) throw new ArgumentException("Unable to find the HttpClient in the resource.", nameof(teammates));
1444
                        var client = (Pathoschild.Http.Client.IClient)privateField.GetValue(teammates);
1445

1446
                        var scopes = await client.GetCurrentScopes(true, cancellationToken).ConfigureAwait(true);
1447
                        scopes = scopes.Where(s => s.EndsWith(".read", StringComparison.OrdinalIgnoreCase)).ToArray();
1448

1449
                        return await teammates.InviteTeammateAsync(email, scopes, cancellationToken).ConfigureAwait(false);
1450
                }
1451

1452
                /// <summary>
1453
                /// Parses the signed events webhook asynchronously.
1454
                /// </summary>
1455
                /// <param name="parser">The webhook parser.</param>
1456
                /// <param name="stream">The stream.</param>
1457
                /// <param name="publicKey">Your public key. To obtain this value, see <see cref="StrongGrid.Resources.WebhookSettings.GetSignedEventsPublicKeyAsync"/>.</param>
1458
                /// <param name="signature">The signature.</param>
1459
                /// <param name="timestamp">The timestamp.</param>
1460
                /// <param name="cancellationToken">The cancellation token.</param>
1461
                /// <returns>An array of <see cref="Event">events</see>.</returns>
1462
                public static async Task<Event[]> ParseSignedEventsWebhookAsync(this IWebhookParser parser, Stream stream, string publicKey, string signature, string timestamp, CancellationToken cancellationToken = default)
1463
                {
1464
                        string requestBody;
1465
                        using (var streamReader = new StreamReader(stream))
1466
                        {
1467
#if NET7_0_OR_GREATER
1468
                                requestBody = await streamReader.ReadToEndAsync(cancellationToken).ConfigureAwait(false);
1469
#else
1470
                                requestBody = await streamReader.ReadToEndAsync().ConfigureAwait(false);
1471
#endif
1472
                        }
1473

1474
                        var webHookEvents = parser.ParseSignedEventsWebhook(requestBody, publicKey, signature, timestamp);
1475
                        return webHookEvents;
1476
                }
1477

1478
                /// <summary>
1479
                /// Parses the signed events webhook.
1480
                /// </summary>
1481
                /// <param name="parser">The webhook parser.</param>
1482
                /// <param name="requestBody">The content submitted by SendGrid's WebHook.</param>
1483
                /// <param name="publicKey">Your public key. To obtain this value, <see cref="StrongGrid.Resources.WebhookSettings.GetSignedEventsPublicKeyAsync"/>.</param>
1484
                /// <param name="signature">The signature.</param>
1485
                /// <param name="timestamp">The timestamp.</param>
1486
                /// <returns>An array of <see cref="Event">events</see>.</returns>
1487
                public static Event[] ParseSignedEventsWebhook(this IWebhookParser parser, string requestBody, string publicKey, string signature, string timestamp)
1488
                {
1489
                        if (string.IsNullOrEmpty(publicKey)) throw new ArgumentNullException(nameof(publicKey));
1✔
1490
                        if (string.IsNullOrEmpty(signature)) throw new ArgumentNullException(nameof(signature));
1✔
1491
                        if (string.IsNullOrEmpty(timestamp)) throw new ArgumentNullException(nameof(timestamp));
1✔
1492

1493
                        // Decode the base64 encoded values
1494
                        var signatureBytes = Convert.FromBase64String(signature);
1✔
1495
                        var publicKeyBytes = Convert.FromBase64String(publicKey);
1✔
1496

1497
                        // Must combine the timestamp and the payload
1498
                        var data = Encoding.UTF8.GetBytes(timestamp + requestBody);
1✔
1499

1500
                        /*
1501
                                The 'ECDsa.ImportSubjectPublicKeyInfo' method was introduced in .NET core 3.0
1502
                                and the DSASignatureFormat enum was introduced in .NET 5.0.
1503

1504
                                We can get rid of the 'ConvertECDSASignature' class and the Utils methods that
1505
                                convert public keys when we stop suporting .NET framework and .NET standard.
1506
                        */
1507

1508
#if NET5_0_OR_GREATER
1509
                        // Verify the signature
1510
                        var eCDsa = ECDsa.Create();
1✔
1511
                        eCDsa.ImportSubjectPublicKeyInfo(publicKeyBytes, out _);
1✔
1512
                        var verified = eCDsa.VerifyData(data, signatureBytes, HashAlgorithmName.SHA256, DSASignatureFormat.Rfc3279DerSequence);
1✔
1513
#elif NET48_OR_GREATER || NETSTANDARD2_1
1514
                        // Convert the signature and public key provided by SendGrid into formats usable by the ECDsa .net crypto class
1515
                        var sig = ConvertECDSASignature.LightweightConvertSignatureFromX9_62ToISO7816_8(256, signatureBytes);
1516
                        var (x, y) = Utils.GetXYFromSecp256r1PublicKey(publicKeyBytes);
1517

1518
                        // Verify the signature
1519
                        var eCDsa = ECDsa.Create();
1520
                        eCDsa.ImportParameters(new ECParameters
1521
                        {
1522
                                Curve = ECCurve.NamedCurves.nistP256, // aka secp256r1 aka prime256v1
1523
                                Q = new ECPoint
1524
                                {
1525
                                        X = x,
1526
                                        Y = y
1527
                                }
1528
                        });
1529
                        var verified = eCDsa.VerifyData(data, sig, HashAlgorithmName.SHA256);
1530
#else
1531
#error Unhandled TFM
1532
#endif
1533

1534
                        if (!verified)
1✔
1535
                        {
1536
                                throw new SecurityException("Webhook signature validation failed.");
×
1537
                        }
1538

1539
                        var webHookEvents = parser.ParseEventsWebhook(requestBody);
1✔
1540
                        return webHookEvents;
1✔
1541
                }
1542

1543
                /// <summary>
1544
                /// Get the current event webhook settings.
1545
                /// </summary>
1546
                /// <param name="webhookSettings">The webhook settings resource.</param>
1547
                /// <param name="onBehalfOf">The user to impersonate.</param>
1548
                /// <param name="cancellationToken">The cancellation token.</param>
1549
                /// <returns>
1550
                /// The <see cref="EventWebhookSettings" />.
1551
                /// </returns>
1552
                public static Task<EventWebhookSettings> GetEventWebhookSettingsAsync(this IWebhookSettings webhookSettings, string onBehalfOf = null, CancellationToken cancellationToken = default)
1553
                {
1554
                        return webhookSettings.GetEventWebhookSettingsAsync(null, onBehalfOf, cancellationToken);
1✔
1555
                }
1556

1557
                /// <summary>
1558
                /// Change the events settings.
1559
                /// </summary>
1560
                /// <param name="webhookSettings">The webhook settings resource.</param>
1561
                /// <param name="enabled">if set to <c>true</c> [enabled].</param>
1562
                /// <param name="url">The webhook endpoint url.</param>
1563
                /// <param name="bounce">if set to <c>true</c> [bounce].</param>
1564
                /// <param name="click">if set to <c>true</c> [click].</param>
1565
                /// <param name="deferred">if set to <c>true</c> [deferred].</param>
1566
                /// <param name="delivered">if set to <c>true</c> [delivered].</param>
1567
                /// <param name="dropped">if set to <c>true</c> [dropped].</param>
1568
                /// <param name="groupResubscribe">if set to <c>true</c> [groupResubscribe].</param>
1569
                /// <param name="groupUnsubscribe">if set to <c>true</c> [groupUnsubscribe].</param>
1570
                /// <param name="open">if set to <c>true</c> [open].</param>
1571
                /// <param name="processed">if set to <c>true</c> [processed].</param>
1572
                /// <param name="spamReport">if set to <c>true</c> [spamReport].</param>
1573
                /// <param name="unsubscribe">if set to <c>true</c> [unsubscribe].</param>
1574
                /// <param name="friendlyName">The friendly name.</param>
1575
                /// <param name="oauthClientId">The OAuth client ID that SendGrid will pass to your OAuth server or service provider to generate an OAuth access token. When passing data in this parameter, you must also specify the oauthTokenUrl.</param>
1576
                /// <param name="oauthClientSecret">The OAuth client secret that SendGrid will pass to your OAuth server or service provider to generate an OAuth access token. This secret is needed only once to create an access token. SendGrid will store the secret, allowing you to update your client ID and Token URL without passing the secret to SendGrid again. When passing data in this parameter, you must also specify the oauthClientId and oauthTokenUrl.</param>
1577
                /// <param name="oAuthTokenUrl">The URL where SendGrid will send the OAuth client ID and client secret to generate an OAuth access token. This should be your OAuth server or service provider. When passing data in this parameter, you must also specify the oauthClientId.</param>
1578
                /// <param name="onBehalfOf">The user to impersonate.</param>
1579
                /// <param name="cancellationToken">The cancellation token.</param>
1580
                /// <returns>
1581
                /// The <see cref="EventWebhookSettings" />.
1582
                /// </returns>
1583
                public static Task<EventWebhookSettings> UpdateEventWebhookSettingsAsync(
1584
                        this IWebhookSettings webhookSettings,
1585
                        bool enabled,
1586
                        string url,
1587
                        bool bounce = default,
1588
                        bool click = default,
1589
                        bool deferred = default,
1590
                        bool delivered = default,
1591
                        bool dropped = default,
1592
                        bool groupResubscribe = default,
1593
                        bool groupUnsubscribe = default,
1594
                        bool open = default,
1595
                        bool processed = default,
1596
                        bool spamReport = default,
1597
                        bool unsubscribe = default,
1598
                        string friendlyName = null,
1599
                        string oauthClientId = null,
1600
                        string oauthClientSecret = null,
1601
                        string oAuthTokenUrl = null,
1602
                        string onBehalfOf = null,
1603
                        CancellationToken cancellationToken = default)
1604
                {
1605
                        return webhookSettings.UpdateEventWebhookSettingsAsync(null, enabled, url, bounce, click, deferred, delivered, dropped, groupResubscribe, groupUnsubscribe, open, processed, spamReport, unsubscribe, friendlyName, oauthClientId, oauthClientSecret, oAuthTokenUrl, onBehalfOf, cancellationToken);
1✔
1606
                }
1607

1608
                /// <summary>
1609
                /// Sends a fake event notification post to the provided URL.
1610
                /// </summary>
1611
                /// <param name="webhookSettings">The webhook settings resource.</param>
1612
                /// <param name="url">The URL where you would like the test notification to be sent.</param>
1613
                /// <param name="oAuthClientId">The client ID Twilio SendGrid sends to your OAuth server or service provider to generate an OAuth access token. When passing data in this parameter, you must also specify oauThokenUrl.</param>
1614
                /// <param name="oAuthClientSecret">This value is needed only once to create an access token. SendGrid will store this secret, allowing you to update your Client ID and Token URL without passing the secret to SendGrid again. When passing data in this field, you must also specify oAuthClientId and oAuthTokenUrl.</param>
1615
                /// <param name="oAuthTokenUrl">The URL where Twilio SendGrid sends the Client ID and Client Secret to generate an access token. This should be your OAuth server or service provider. When passing data in this parameter, you must also include oAuthClientId.</param>
1616
                /// <param name="onBehalfOf">The user to impersonate.</param>
1617
                /// <param name="cancellationToken">The cancellation token.</param>
1618
                /// <returns>
1619
                /// The async task.
1620
                /// </returns>
1621
                public static Task SendEventTestAsync(this IWebhookSettings webhookSettings, string url, string oAuthClientId = null, string oAuthClientSecret = null, string oAuthTokenUrl = null, string onBehalfOf = null, CancellationToken cancellationToken = default)
1622
                {
1623
                        return webhookSettings.SendEventTestAsync(null, url, oAuthClientId, oAuthClientSecret, oAuthTokenUrl, onBehalfOf, cancellationToken);
1✔
1624
                }
1625

1626
                /// <summary>
1627
                /// Enable or disable signature verification for a single Event Webhook.
1628
                /// </summary>
1629
                /// <param name="webhookSettings">The webhook settings resource.</param>
1630
                /// <param name="id">The ID of the Event Webhook you want to update.</param>
1631
                /// <param name="enabled">Indicates if the signature verification should be enbladle or not.</param>
1632
                /// <param name="onBehalfOf">The user to impersonate.</param>
1633
                /// <param name="cancellationToken">The cancellation token.</param>
1634
                /// <returns>The async task.</returns>
1635
                public static Task ToggleEventWebhookSignatureVerificationAsync(this IWebhookSettings webhookSettings, string id, bool enabled, string onBehalfOf = null, CancellationToken cancellationToken = default)
1636
                {
1637
                        return webhookSettings.ToggleEventWebhookSignatureVerificationAsync(null, enabled, onBehalfOf, cancellationToken);
×
1638
                }
1639

1640
                /// <summary>
1641
                /// Get the signed events public key.
1642
                /// </summary>
1643
                /// <param name="webhookSettings">The webhook settings resource.</param>
1644
                /// <param name="onBehalfOf">The user to impersonate.</param>
1645
                /// <param name="cancellationToken">The cancellation token.</param>
1646
                /// <returns>
1647
                /// The public key.
1648
                /// </returns>
1649
                public static Task<string> GetSignedEventsPublicKeyAsync(this IWebhookSettings webhookSettings, string onBehalfOf = null, CancellationToken cancellationToken = default)
1650
                {
1651
                        return webhookSettings.GetSignedEventsPublicKeyAsync(null, onBehalfOf, cancellationToken);
×
1652
                }
1653
        }
1654
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc