Skip to content

Commit 2082bbe

Browse files
Make the Personalization’s dynamicTemplateData generic (#17)
* Make the Personalization’s dynamicTemplateData generic This should allow support for nested Codable structures, and better type support when encoded to JSON. Fixes #7 * Add some `init`s and tests --------- Co-authored-by: Francesco Paolo Severino <[email protected]> Co-authored-by: Francesco Paolo Severino <[email protected]>
1 parent c00b291 commit 2082bbe

File tree

6 files changed

+101
-8
lines changed

6 files changed

+101
-8
lines changed

Sources/SendGridKit/Models/MailSettings.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ public struct MailSettings: Codable, Sendable {
5050
public struct Setting: Codable, Sendable {
5151
/// Indicates if this setting is enabled.
5252
public var enable: Bool
53+
54+
public init(enable: Bool) {
55+
self.enable = enable
56+
}
5357
}
5458

5559
public struct Footer: Codable, Sendable {

Sources/SendGridKit/Models/Personalization.swift

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Foundation
22

3-
public struct Personalization: Codable, Sendable {
3+
public struct Personalization<DynamicTemplateData: Codable & Sendable>: Codable, Sendable {
44
/// An array of recipients.
55
///
66
/// > Important: Each object within this array may contain the name, but must always contain the email, of a recipient.
@@ -26,7 +26,7 @@ public struct Personalization: Codable, Sendable {
2626
public var substitutions: [String: String]?
2727

2828
/// A collection of key/value pairs following the pattern `"key":"value"` to substitute handlebar template data.
29-
public var dynamicTemplateData: [String: String]?
29+
public var dynamicTemplateData: DynamicTemplateData?
3030

3131
/// Values that are specific to this personalization that will be carried along with the email and its activity data.
3232
public var customArgs: [String: String]?
@@ -43,7 +43,7 @@ public struct Personalization: Codable, Sendable {
4343
subject: String? = nil,
4444
headers: [String: String]? = nil,
4545
substitutions: [String: String]? = nil,
46-
dynamicTemplateData: [String: String]? = nil,
46+
dynamicTemplateData: DynamicTemplateData? = nil,
4747
customArgs: [String: String]? = nil,
4848
sendAt: Date? = nil
4949
) {
@@ -70,3 +70,27 @@ public struct Personalization: Codable, Sendable {
7070
case sendAt = "send_at"
7171
}
7272
}
73+
74+
public extension Personalization where DynamicTemplateData == [String: String] {
75+
init(
76+
to: [EmailAddress]? = nil,
77+
cc: [EmailAddress]? = nil,
78+
bcc: [EmailAddress]? = nil,
79+
subject: String? = nil,
80+
headers: [String: String]? = nil,
81+
substitutions: [String: String]? = nil,
82+
dynamicTemplateData: [String: String]? = nil,
83+
customArgs: [String: String]? = nil,
84+
sendAt: Date? = nil
85+
) {
86+
self.to = to
87+
self.cc = cc
88+
self.bcc = bcc
89+
self.subject = subject
90+
self.headers = headers
91+
self.substitutions = substitutions
92+
self.dynamicTemplateData = dynamicTemplateData
93+
self.customArgs = customArgs
94+
self.sendAt = sendAt
95+
}
96+
}

Sources/SendGridKit/Models/SendGridEmail.swift

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import Foundation
22

3-
public struct SendGridEmail: Codable, Sendable {
3+
public struct SendGridEmail<DynamicTemplateData: Codable & Sendable>: Codable, Sendable {
44
/// An array of messages and their metadata.
55
///
66
/// Each object within `personalizations` can be thought of as an envelope -
77
/// it defines who should receive an individual message and how that message should be handled.
8-
public var personalizations: [Personalization]
8+
public var personalizations: [Personalization<DynamicTemplateData>]
99

1010
public var from: EmailAddress
1111

@@ -79,7 +79,7 @@ public struct SendGridEmail: Codable, Sendable {
7979
public var trackingSettings: TrackingSettings?
8080

8181
public init(
82-
personalizations: [Personalization],
82+
personalizations: [Personalization<DynamicTemplateData>],
8383
from: EmailAddress,
8484
replyTo: EmailAddress? = nil,
8585
replyToList: [EmailAddress]? = nil,
@@ -136,3 +136,43 @@ public struct SendGridEmail: Codable, Sendable {
136136
case trackingSettings = "tracking_settings"
137137
}
138138
}
139+
140+
public extension SendGridEmail where DynamicTemplateData == [String: String] {
141+
init(
142+
personalizations: [Personalization<[String: String]>],
143+
from: EmailAddress,
144+
replyTo: EmailAddress? = nil,
145+
replyToList: [EmailAddress]? = nil,
146+
subject: String? = nil,
147+
content: [EmailContent]? = nil,
148+
attachments: [EmailAttachment]? = nil,
149+
templateID: String? = nil,
150+
headers: [String: String]? = nil,
151+
categories: [String]? = nil,
152+
customArgs: [String: String]? = nil,
153+
sendAt: Date? = nil,
154+
batchID: String? = nil,
155+
asm: AdvancedSuppressionManager? = nil,
156+
ipPoolName: String? = nil,
157+
mailSettings: MailSettings? = nil,
158+
trackingSettings: TrackingSettings? = nil
159+
) {
160+
self.personalizations = personalizations
161+
self.from = from
162+
self.replyTo = replyTo
163+
self.replyToList = replyToList
164+
self.subject = subject
165+
self.content = content
166+
self.attachments = attachments
167+
self.templateID = templateID
168+
self.headers = headers
169+
self.categories = categories
170+
self.customArgs = customArgs
171+
self.sendAt = sendAt
172+
self.batchID = batchID
173+
self.asm = asm
174+
self.ipPoolName = ipPoolName
175+
self.mailSettings = mailSettings
176+
self.trackingSettings = trackingSettings
177+
}
178+
}

Sources/SendGridKit/Models/TrackingSettings.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ public struct ClickTracking: Codable, Sendable {
4444

4545
/// Indicates if this setting should be included in the text/plain portion of your email.
4646
public var enableText: Bool
47+
48+
public init(enable: Bool, enableText: Bool) {
49+
self.enable = enable
50+
self.enableText = enableText
51+
}
4752

4853
private enum CodingKeys: String, CodingKey {
4954
case enable

Sources/SendGridKit/SendGridClient.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public struct SendGridClient: Sendable {
3333
self.apiURL = forEU ? "https://api.eu.sendgrid.com/v3/mail/send" : "https://api.sendgrid.com/v3/mail/send"
3434
}
3535

36-
public func send(email: SendGridEmail) async throws {
36+
public func send<DynamicTemplateData: Codable & Sendable>(email: SendGridEmail<DynamicTemplateData>) async throws {
3737
var headers = HTTPHeaders()
3838
headers.add(name: "Authorization", value: "Bearer \(apiKey)")
3939
headers.add(name: "Content-Type", value: "application/json")

Tests/SendGridKitTests/SendGridTestsKit.swift

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Testing
22
import AsyncHTTPClient
3-
@testable import SendGridKit
3+
import SendGridKit
44

55
struct SendGridKitTests {
66
var client: SendGridClient
@@ -74,4 +74,24 @@ struct SendGridKitTests {
7474
true
7575
}
7676
}
77+
78+
@Test func dynamicTemplateData() async throws {
79+
struct DynamicTemplateData: Codable, Sendable {
80+
let text: String
81+
let integer: Int
82+
let double: Double
83+
}
84+
let dynamicTemplateData = DynamicTemplateData(text: "Hello, World!", integer: 42, double: 3.14)
85+
86+
// TODO: Replace the addresses with real email addresses
87+
let personalization = Personalization(to: [EmailAddress("TO-ADDRESS")], subject: "Test Email", dynamicTemplateData: dynamicTemplateData)
88+
let email = SendGridEmail(personalizations: [personalization], from: EmailAddress("FROM-ADDRESS"), content: [EmailContent("Hello, World!")])
89+
90+
try await withKnownIssue {
91+
try await client.send(email: email)
92+
} when: {
93+
// TODO: Replace with `false` when you have a valid API key
94+
true
95+
}
96+
}
7797
}

0 commit comments

Comments
 (0)