From 2082bbe452b0f8fa6974c07ed178fb1c713a241d Mon Sep 17 00:00:00 2001 From: Tony Arnold Date: Sat, 21 Sep 2024 19:41:48 +1000 Subject: [PATCH] =?UTF-8?q?Make=20the=20Personalization=E2=80=99s=20dynami?= =?UTF-8?q?cTemplateData=20generic=20(#17)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 <96546612+fpseverino@users.noreply.github.com> Co-authored-by: Francesco Paolo Severino --- Sources/SendGridKit/Models/MailSettings.swift | 4 ++ .../SendGridKit/Models/Personalization.swift | 30 ++++++++++-- .../SendGridKit/Models/SendGridEmail.swift | 46 +++++++++++++++++-- .../SendGridKit/Models/TrackingSettings.swift | 5 ++ Sources/SendGridKit/SendGridClient.swift | 2 +- Tests/SendGridKitTests/SendGridTestsKit.swift | 22 ++++++++- 6 files changed, 101 insertions(+), 8 deletions(-) diff --git a/Sources/SendGridKit/Models/MailSettings.swift b/Sources/SendGridKit/Models/MailSettings.swift index 35b7df7..de1463c 100644 --- a/Sources/SendGridKit/Models/MailSettings.swift +++ b/Sources/SendGridKit/Models/MailSettings.swift @@ -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 { diff --git a/Sources/SendGridKit/Models/Personalization.swift b/Sources/SendGridKit/Models/Personalization.swift index 7a5d315..ede38bf 100644 --- a/Sources/SendGridKit/Models/Personalization.swift +++ b/Sources/SendGridKit/Models/Personalization.swift @@ -1,6 +1,6 @@ import Foundation -public struct Personalization: Codable, Sendable { +public struct Personalization: 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. @@ -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]? @@ -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 ) { @@ -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 + } +} diff --git a/Sources/SendGridKit/Models/SendGridEmail.swift b/Sources/SendGridKit/Models/SendGridEmail.swift index bdc57d6..897fcbd 100644 --- a/Sources/SendGridKit/Models/SendGridEmail.swift +++ b/Sources/SendGridKit/Models/SendGridEmail.swift @@ -1,11 +1,11 @@ import Foundation -public struct SendGridEmail: Codable, Sendable { +public struct SendGridEmail: 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] public var from: EmailAddress @@ -79,7 +79,7 @@ public struct SendGridEmail: Codable, Sendable { public var trackingSettings: TrackingSettings? public init( - personalizations: [Personalization], + personalizations: [Personalization], from: EmailAddress, replyTo: EmailAddress? = nil, replyToList: [EmailAddress]? = nil, @@ -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 + } +} diff --git a/Sources/SendGridKit/Models/TrackingSettings.swift b/Sources/SendGridKit/Models/TrackingSettings.swift index 1d06306..4a1bb34 100644 --- a/Sources/SendGridKit/Models/TrackingSettings.swift +++ b/Sources/SendGridKit/Models/TrackingSettings.swift @@ -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 diff --git a/Sources/SendGridKit/SendGridClient.swift b/Sources/SendGridKit/SendGridClient.swift index 21a8a2c..9d395ac 100644 --- a/Sources/SendGridKit/SendGridClient.swift +++ b/Sources/SendGridKit/SendGridClient.swift @@ -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(email: SendGridEmail) async throws { var headers = HTTPHeaders() headers.add(name: "Authorization", value: "Bearer \(apiKey)") headers.add(name: "Content-Type", value: "application/json") diff --git a/Tests/SendGridKitTests/SendGridTestsKit.swift b/Tests/SendGridKitTests/SendGridTestsKit.swift index 972fa94..3c23cdc 100644 --- a/Tests/SendGridKitTests/SendGridTestsKit.swift +++ b/Tests/SendGridKitTests/SendGridTestsKit.swift @@ -1,6 +1,6 @@ import Testing import AsyncHTTPClient -@testable import SendGridKit +import SendGridKit struct SendGridKitTests { var client: SendGridClient @@ -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 + } + } }