Skip to content

Commit cae5b09

Browse files
authored
Final touches before release of Email Validation API (#27)
* Final touches before release of Email Validation API
1 parent 76d442f commit cae5b09

File tree

9 files changed

+465
-338
lines changed

9 files changed

+465
-338
lines changed

README.md

Lines changed: 34 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Send simple emails or leverage the full capabilities of [SendGrid's V3 API](http
2727
Use the SPM string to easily include the dependendency in your `Package.swift` file
2828

2929
```swift
30-
.package(url: "https://github.com/vapor-community/sendgrid-kit.git", from: "3.0.0"),
30+
.package(url: "https://github.com/vapor-community/sendgrid-kit.git", from: "3.1.0"),
3131
```
3232

3333
and add it to your target's dependencies:
@@ -61,9 +61,25 @@ let email = SendGridEmail(...)
6161
try await sendGridClient.send(email: email)
6262
```
6363

64+
### Error handling
65+
66+
If the request to the API failed for any reason a `SendGridError` is thrown, which has an `errors` property that contains an array of errors returned by the API.
67+
68+
Simply ensure you catch errors thrown like any other throwing function.
69+
70+
```swift
71+
import SendGridKit
72+
73+
do {
74+
try await sendGridClient.send(email: email)
75+
} catch let error as SendGridError {
76+
print(error)
77+
}
78+
```
79+
6480
### Email Validation API
6581

66-
SendGridKit supports SendGrid's Email Validation API, which helps verify the validity of email addresses before sending:
82+
SendGridKit supports SendGrid's [Email Address Validation API](https://www.twilio.com/docs/sendgrid/ui/managing-contacts/email-address-validation), which provides detailed information on the validity of email addresses.
6783

6884
```swift
6985
import SendGridKit
@@ -78,18 +94,18 @@ do {
7894
let validationResponse = try await sendGridClient.validateEmail(validationRequest)
7995

8096
// Check if the email is valid
81-
if validationResponse.result.verdict == .valid {
82-
print("Email is valid with score: \(validationResponse.result.score)")
97+
if validationResponse.result?.verdict == .valid {
98+
print("Email is valid with score: \(validationResponse.result?.score)")
8399
} else {
84-
print("Email is invalid: \(validationResponse.result.reason ?? "Unknown reason")")
100+
print("Email is invalid")
85101
}
86102

87103
// Access detailed validation information
88-
if validationResponse.checks.disposable {
89-
print("Warning: This is a disposable email address")
104+
if validationResponse.result?.checks?.domain?.isSuspectedDisposableAddress ?? true {
105+
print("Warning: This is probably a disposable email address")
90106
}
91107

92-
if validationResponse.checks.roleAccount {
108+
if validationResponse.result?.checks?.localPart?.isSuspectedRoleAddress {
93109
print("Note: This is a role-based email address")
94110
}
95111
} catch {
@@ -99,7 +115,7 @@ do {
99115

100116
#### Bulk Email Validation API
101117

102-
For validating multiple email addresses at once, SendGridKit provides access to SendGrid's Bulk Email Validation API. This requires uploading a CSV file with email addresses:
118+
For validating multiple email addresses at once, SendGridKit provides access to SendGrid's Bulk Email Address Validation API. This requires uploading a CSV file with email addresses:
103119

104120
```swift
105121
import SendGridKit
@@ -110,55 +126,37 @@ let sendGridClient = SendGridEmailValidationClient(httpClient: .shared, apiKey:
110126
do {
111127
// Step 1: Create a CSV file with email addresses
112128
let csvContent = """
113-
emails
114-
user1@example.com
115-
user2@example.com
116-
user3@example.com
117-
"""
129+
emails
130+
user1@example.com
131+
user2@example.com
132+
user3@example.com
133+
"""
118134
guard let csvData = csvContent.data(using: .utf8) else {
119135
throw SomeError.invalidCSV
120136
}
121137

122138
// Step 2: Upload the CSV file
123-
let (uploadSuccess, jobId) = try await sendGridClient.uploadBulkValidationFile(
139+
let fileUpload = try await sendGridClient.uploadBulkEmailValidationFile(
124140
fileData: csvData,
125141
fileType: .csv
126142
)
127143

128-
if !uploadSuccess {
144+
guard fileUpload.succeeded, let jobID = fileUpload.jobID else {
129145
throw SomeError.uploadError
130146
}
131147

132148
// Step 4: Check job status (poll until completed)
133-
var jobStatusResponse = try await sendGridClient.checkBulkValidationStatus(jobId: jobId)
134-
var jobStatus = jobStatusResponse.result
149+
var jobStatus = try await sendGridClient.checkBulkEmailValidationJob(by: jobID)
135150

136151
while jobStatus.status != .done {
137152
print("Job \(jobStatus.id) status: \(jobStatus.status) - \(jobStatus.segmentsProcessed)/\(jobStatus.segments) segments processed")
138153

139154
// Wait before checking again (implement your own backoff strategy)
140155
try? await Task.sleep(nanoseconds: 5_000_000_000) // 5 seconds
141156

142-
jobStatusResponse = try await sendGridClient.checkBulkValidationStatus(jobId: jobId)
143-
jobStatus = jobStatusResponse..result
157+
jobStatus = try await sendGridClient.checkBulkEmailValidationJob(by: jobID)
144158
}
145159
} catch {
146160
print("Bulk validation failed: \(error)")
147161
}
148162
```
149-
150-
### Error handling
151-
152-
If the request to the API failed for any reason a `SendGridError` is thrown, which has an `errors` property that contains an array of errors returned by the API.
153-
154-
Simply ensure you catch errors thrown like any other throwing function.
155-
156-
```swift
157-
import SendGridKit
158-
159-
do {
160-
try await sendGridClient.send(email: email)
161-
} catch let error as SendGridError {
162-
print(error)
163-
}
164-
```

Sources/SendGridKit/Models/BulkEmailValidation.swift

Lines changed: 58 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,113 @@
11
import struct Foundation.Date
22

33
/// A request to get an upload URL for bulk email validation.
4-
public struct BulkEmailValidationUploadURLRequest: Encodable {
5-
/// The file type that will be uploaded.
4+
public struct BulkEmailValidationUploadURLRequest: Codable {
5+
/// The format of the file you wish to upload.
66
public let fileType: FileType
77

8-
/// Initialize a new `BulkEmailValidationUploadURLRequest`
8+
/// Initialize a new ``BulkEmailValidationUploadURLRequest``
99
///
1010
/// - Parameter fileType: The file type that will be uploaded (CSV or ZIP)
1111
public init(fileType: FileType) {
1212
self.fileType = fileType
1313
}
1414

15-
/// The type of file that will be uploaded for bulk validation.
15+
/// The format of the file you wish to upload.
1616
public enum FileType: String, Codable, Sendable {
1717
/// A CSV file containing email addresses.
1818
case csv
1919
/// A ZIP file containing a CSV file with email addresses.
2020
case zip
2121
}
2222

23-
/// CodingKeys for mapping JSON fields to struct properties
2423
private enum CodingKeys: String, CodingKey {
2524
case fileType = "file_type"
2625
}
2726
}
2827

2928
/// The response containing upload details for bulk email validation.
30-
struct BulkEmailValidationUploadURLResponse: Decodable, Sendable {
31-
/// The unique identifier for the validation job.
32-
let jobId: String
29+
struct BulkEmailValidationUploadURLResponse: Codable, Sendable {
30+
/// The unique ID of the Bulk Email Address Validation Job.
31+
let jobID: String?
3332

34-
/// The URI where the file should be uploaded.
35-
let uploadUri: String
33+
/// The URI to use for the request to upload your list of email addresses.
34+
let uploadURI: String?
3635

37-
/// Headers that should be included in the upload request.
38-
let uploadHeaders: [UploadHeader]
36+
/// Array containing headers and header values.
37+
let uploadHeaders: [UploadHeader]?
3938

4039
/// Header for the upload request.
4140
struct UploadHeader: Codable, Sendable {
42-
/// The name of the header.
43-
let header: String
41+
/// The name of the header that must be included in the upload request.
42+
let header: String?
4443

45-
/// The value of the header.
46-
let value: String
44+
/// The value of the header that must be included in the upload request.
45+
let value: String?
4746
}
4847

49-
/// CodingKeys for mapping JSON fields to struct properties
5048
private enum CodingKeys: String, CodingKey {
51-
case jobId = "job_id"
52-
case uploadUri = "upload_uri"
49+
case jobID = "job_id"
50+
case uploadURI = "upload_uri"
5351
case uploadHeaders = "upload_headers"
5452
}
5553
}
5654

57-
/// The response from initiating a bulk email validation job after upload.
58-
public struct BulkEmailValidationJob: Decodable, Sendable {
59-
/// The response structure containing the job creation status.
60-
public let response: Response
55+
/// A response that returns a specific Bulk Email Validation Job.
56+
///
57+
/// You can use this endpoint to check on the progress of a Job.
58+
public struct BulkEmailValidationJob: Codable, Sendable {
59+
/// A response that returns a specific Bulk Email Validation Job.
60+
public let response: Response?
6161

62-
public struct Response: Decodable, Sendable {
63-
public let value: Value
62+
/// A response that returns a specific Bulk Email Validation Job.
63+
public struct Response: Codable, Sendable {
64+
public let value: Value?
6465

65-
public struct Value: Decodable, Sendable {
66-
public let result: Result
66+
public struct Value: Codable, Sendable {
67+
/// The status of a specific Bulk Email Validation Job.
68+
public let result: Result?
6769

68-
/// The status result of a bulk email validation job.
69-
public struct Result: Decodable, Sendable {
70-
/// The unique identifier for the validation job.
71-
public let id: String
70+
/// The status of a specific Bulk Email Validation Job.
71+
public struct Result: Codable, Sendable {
72+
/// The unique ID of the Bulk Email Address Validation Job.
73+
public let id: String?
7274

73-
/// Status of the validation job (e.g., "Queued", "Processing", "Completed").
74-
public let status: BulkEmailValidationJobStatus
75+
/// The status of the Bulk Email Address Validation Job.
76+
public let status: BulkEmailValidationJobStatus?
7577

7678
/// The total number of segments in the Bulk Email Address Validation Job.
77-
/// There are 1,500 email addresses per segment. The value is 0 until the Job status is Processing.
79+
///
80+
/// There are 1,500 email addresses per segment.
81+
/// The value is 0 until the Job status is ``BulkEmailValidationJobStatus/processing``.
7882
public let segments: Int?
7983

8084
/// The number of segments processed at the time of the request.
85+
///
8186
/// 100 segments process in parallel at a time.
8287
public let segmentsProcessed: Int?
8388

8489
/// Boolean indicating whether the results CSV file is available for download.
8590
public let isDownloadAvailable: Bool?
8691

87-
/// The ISO8601 timestamp when the Job was created.
88-
/// This is the time at which the upload request was sent to the upload_uri.
89-
public let startedAt: Date
92+
/// The date when the Job was created.
93+
///
94+
/// This is the time at which the upload request was sent to the `upload_uri`.
95+
public let startedAt: Date?
9096

91-
/// The ISO8601 timestamp when the Job was finished.
97+
/// The date when the Job was finished.
9298
public let finishedAt: Date?
9399

94100
/// Array containing error messages related to the Bulk Email Address Validation Job.
101+
///
95102
/// Array is empty if no errors ocurred.
96103
public let errors: [BulkValidationError]?
97104

98-
public struct BulkValidationError: Decodable, Sendable {
105+
/// Error message related to the Bulk Email Address Validation Job.
106+
public struct BulkValidationError: Codable, Sendable {
99107
/// Description of the error encountered during execution of the Bulk Email Address Validation Job.
100-
public let message: String
108+
public let message: String?
101109
}
102110

103-
/// CodingKeys for mapping JSON fields to struct properties
104111
private enum CodingKeys: String, CodingKey {
105112
case id, status, segments, errors
106113
case segmentsProcessed = "segments_processed"
@@ -113,7 +120,7 @@ public struct BulkEmailValidationJob: Decodable, Sendable {
113120
}
114121
}
115122

116-
/// BulkEmailValidationJobStatus
123+
/// The status of the Bulk Email Address Validation Job.
117124
public enum BulkEmailValidationJobStatus: String, Codable, Sendable {
118125
case initiated = "Initiated"
119126
case queued = "Queued"
@@ -123,23 +130,23 @@ public enum BulkEmailValidationJobStatus: String, Codable, Sendable {
123130
case error = "Error"
124131
}
125132

126-
/// The result of a bulk email validation operation.
133+
/// A response containing a list of all of a user's Bulk Email Validation Jobs.
127134
public struct BulkEmailValidationJobsResponse: Codable, Sendable {
128-
/// The response structure containing the validation results.
129-
public let result: [Result]
135+
/// The result of the response, containing an array of all of the user's Bulk Email Validation Jobs.
136+
public let result: [Result]?
130137

131-
/// Nested response structure containing the results value.
138+
/// A user's Bulk Email Validation Job.
132139
public struct Result: Codable, Sendable {
133140
/// The unique ID of the Bulk Email Address Validation Job.
134-
public let id: String
141+
public let id: String?
135142

136143
/// The status of the Bulk Email Address Validation Job.
137-
public let status: BulkEmailValidationJobStatus
144+
public let status: BulkEmailValidationJobStatus?
138145

139-
/// The ISO8601 timestamp when the Job was created. This is the time at which the upload request was sent to the upload_uri.
140-
public let startedAt: Date
146+
/// The date when the Job was created. This is the time at which the upload request was sent to the `upload_uri`.
147+
public let startedAt: Date?
141148

142-
/// The ISO8601 timestamp when the Job was finished.
149+
/// The date when the Job was finished.
143150
public let finishedAt: Date?
144151

145152
private enum CodingKeys: String, CodingKey {

0 commit comments

Comments
 (0)