Skip to content

Commit

Permalink
Make the Personalization’s dynamicTemplateData generic (#17)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
3 people authored Sep 21, 2024
1 parent c00b291 commit 2082bbe
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 8 deletions.
4 changes: 4 additions & 0 deletions Sources/SendGridKit/Models/MailSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ public struct MailSettings: Codable, Sendable {
public struct Setting: Codable, Sendable {
/// Indicates if this setting is enabled.
public var enable: Bool

public init(enable: Bool) {
self.enable = enable
}
}

public struct Footer: Codable, Sendable {
Expand Down
30 changes: 27 additions & 3 deletions Sources/SendGridKit/Models/Personalization.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

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

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

/// Values that are specific to this personalization that will be carried along with the email and its activity data.
public var customArgs: [String: String]?
Expand All @@ -43,7 +43,7 @@ public struct Personalization: Codable, Sendable {
subject: String? = nil,
headers: [String: String]? = nil,
substitutions: [String: String]? = nil,
dynamicTemplateData: [String: String]? = nil,
dynamicTemplateData: DynamicTemplateData? = nil,
customArgs: [String: String]? = nil,
sendAt: Date? = nil
) {
Expand All @@ -70,3 +70,27 @@ public struct Personalization: Codable, Sendable {
case sendAt = "send_at"
}
}

public extension Personalization where DynamicTemplateData == [String: String] {
init(
to: [EmailAddress]? = nil,
cc: [EmailAddress]? = nil,
bcc: [EmailAddress]? = nil,
subject: String? = nil,
headers: [String: String]? = nil,
substitutions: [String: String]? = nil,
dynamicTemplateData: [String: String]? = nil,
customArgs: [String: String]? = nil,
sendAt: Date? = nil
) {
self.to = to
self.cc = cc
self.bcc = bcc
self.subject = subject
self.headers = headers
self.substitutions = substitutions
self.dynamicTemplateData = dynamicTemplateData
self.customArgs = customArgs
self.sendAt = sendAt
}
}
46 changes: 43 additions & 3 deletions Sources/SendGridKit/Models/SendGridEmail.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import Foundation

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

public var from: EmailAddress

Expand Down Expand Up @@ -79,7 +79,7 @@ public struct SendGridEmail: Codable, Sendable {
public var trackingSettings: TrackingSettings?

public init(
personalizations: [Personalization],
personalizations: [Personalization<DynamicTemplateData>],
from: EmailAddress,
replyTo: EmailAddress? = nil,
replyToList: [EmailAddress]? = nil,
Expand Down Expand Up @@ -136,3 +136,43 @@ public struct SendGridEmail: Codable, Sendable {
case trackingSettings = "tracking_settings"
}
}

public extension SendGridEmail where DynamicTemplateData == [String: String] {
init(
personalizations: [Personalization<[String: String]>],
from: EmailAddress,
replyTo: EmailAddress? = nil,
replyToList: [EmailAddress]? = nil,
subject: String? = nil,
content: [EmailContent]? = nil,
attachments: [EmailAttachment]? = nil,
templateID: String? = nil,
headers: [String: String]? = nil,
categories: [String]? = nil,
customArgs: [String: String]? = nil,
sendAt: Date? = nil,
batchID: String? = nil,
asm: AdvancedSuppressionManager? = nil,
ipPoolName: String? = nil,
mailSettings: MailSettings? = nil,
trackingSettings: TrackingSettings? = nil
) {
self.personalizations = personalizations
self.from = from
self.replyTo = replyTo
self.replyToList = replyToList
self.subject = subject
self.content = content
self.attachments = attachments
self.templateID = templateID
self.headers = headers
self.categories = categories
self.customArgs = customArgs
self.sendAt = sendAt
self.batchID = batchID
self.asm = asm
self.ipPoolName = ipPoolName
self.mailSettings = mailSettings
self.trackingSettings = trackingSettings
}
}
5 changes: 5 additions & 0 deletions Sources/SendGridKit/Models/TrackingSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ public struct ClickTracking: Codable, Sendable {

/// Indicates if this setting should be included in the text/plain portion of your email.
public var enableText: Bool

public init(enable: Bool, enableText: Bool) {
self.enable = enable
self.enableText = enableText
}

private enum CodingKeys: String, CodingKey {
case enable
Expand Down
2 changes: 1 addition & 1 deletion Sources/SendGridKit/SendGridClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public struct SendGridClient: Sendable {
self.apiURL = forEU ? "https://api.eu.sendgrid.com/v3/mail/send" : "https://api.sendgrid.com/v3/mail/send"
}

public func send(email: SendGridEmail) async throws {
public func send<DynamicTemplateData: Codable & Sendable>(email: SendGridEmail<DynamicTemplateData>) async throws {
var headers = HTTPHeaders()
headers.add(name: "Authorization", value: "Bearer \(apiKey)")
headers.add(name: "Content-Type", value: "application/json")
Expand Down
22 changes: 21 additions & 1 deletion Tests/SendGridKitTests/SendGridTestsKit.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Testing
import AsyncHTTPClient
@testable import SendGridKit
import SendGridKit

struct SendGridKitTests {
var client: SendGridClient
Expand Down Expand Up @@ -74,4 +74,24 @@ struct SendGridKitTests {
true
}
}

@Test func dynamicTemplateData() async throws {
struct DynamicTemplateData: Codable, Sendable {
let text: String
let integer: Int
let double: Double
}
let dynamicTemplateData = DynamicTemplateData(text: "Hello, World!", integer: 42, double: 3.14)

// TODO: Replace the addresses with real email addresses
let personalization = Personalization(to: [EmailAddress("TO-ADDRESS")], subject: "Test Email", dynamicTemplateData: dynamicTemplateData)
let email = SendGridEmail(personalizations: [personalization], from: EmailAddress("FROM-ADDRESS"), content: [EmailContent("Hello, World!")])

try await withKnownIssue {
try await client.send(email: email)
} when: {
// TODO: Replace with `false` when you have a valid API key
true
}
}
}

0 comments on commit 2082bbe

Please sign in to comment.