diff --git a/.spi.yml b/.spi.yml index 8a3fa63..b5ab714 100644 --- a/.spi.yml +++ b/.spi.yml @@ -1,5 +1,4 @@ version: 1 builder: configs: - - documentation_targets: [SendGridKit] - swift_version: 6.0 \ No newline at end of file + - documentation_targets: [SendGridKit] \ No newline at end of file diff --git a/.swift-format b/.swift-format new file mode 100644 index 0000000..47901d1 --- /dev/null +++ b/.swift-format @@ -0,0 +1,70 @@ +{ + "fileScopedDeclarationPrivacy": { + "accessLevel": "private" + }, + "indentation": { + "spaces": 4 + }, + "indentConditionalCompilationBlocks": true, + "indentSwitchCaseLabels": false, + "lineBreakAroundMultilineExpressionChainComponents": false, + "lineBreakBeforeControlFlowKeywords": false, + "lineBreakBeforeEachArgument": false, + "lineBreakBeforeEachGenericRequirement": false, + "lineLength": 140, + "maximumBlankLines": 1, + "multiElementCollectionTrailingCommas": true, + "noAssignmentInExpressions": { + "allowedFunctions": [ + "XCTAssertNoThrow" + ] + }, + "prioritizeKeepingFunctionOutputTogether": false, + "respectsExistingLineBreaks": true, + "rules": { + "AllPublicDeclarationsHaveDocumentation": false, + "AlwaysUseLiteralForEmptyCollectionInit": false, + "AlwaysUseLowerCamelCase": true, + "AmbiguousTrailingClosureOverload": true, + "BeginDocumentationCommentWithOneLineSummary": false, + "DoNotUseSemicolons": true, + "DontRepeatTypeInStaticProperties": true, + "FileScopedDeclarationPrivacy": true, + "FullyIndirectEnum": true, + "GroupNumericLiterals": true, + "IdentifiersMustBeASCII": true, + "NeverForceUnwrap": false, + "NeverUseForceTry": false, + "NeverUseImplicitlyUnwrappedOptionals": false, + "NoAccessLevelOnExtensionDeclaration": true, + "NoAssignmentInExpressions": true, + "NoBlockComments": true, + "NoCasesWithOnlyFallthrough": true, + "NoEmptyTrailingClosureParentheses": true, + "NoLabelsInCasePatterns": true, + "NoLeadingUnderscores": false, + "NoParensAroundConditions": true, + "NoPlaygroundLiterals": true, + "NoVoidReturnOnFunctionSignature": true, + "OmitExplicitReturns": false, + "OneCasePerLine": true, + "OneVariableDeclarationPerLine": true, + "OnlyOneTrailingClosureArgument": true, + "OrderedImports": true, + "ReplaceForEachWithForLoop": true, + "ReturnVoidInsteadOfEmptyTuple": true, + "TypeNamesShouldBeCapitalized": true, + "UseEarlyExits": false, + "UseExplicitNilCheckInConditions": true, + "UseLetInEveryBoundCaseVariable": true, + "UseShorthandTypeNames": true, + "UseSingleLinePropertyGetter": true, + "UseSynthesizedInitializer": true, + "UseTripleSlashForDocumentationComments": true, + "UseWhereClausesInForLoops": false, + "ValidateDocumentationComments": false + }, + "spacesAroundRangeFormationOperators": false, + "tabWidth": 8, + "version": 1 +} \ No newline at end of file diff --git a/README.md b/README.md index 2ba597c..8719c41 100644 --- a/README.md +++ b/README.md @@ -18,22 +18,22 @@
-📧 SendGridKit is a Swift package used to communicate with the SendGrid API for Server Side Swift Apps. +📧 SendGridKit is a Swift package that helps you communicate with the SendGrid API in your Server Side Swift applications. -Send simple emails, or leverage the full capabilities of [SendGrid's V3 API](https://www.twilio.com/docs/sendgrid/api-reference/mail-send/mail-send). +Send simple emails or leverage the full capabilities of [SendGrid's V3 API](https://www.twilio.com/docs/sendgrid/api-reference/mail-send/mail-send). ### Getting Started Use the SPM string to easily include the dependendency in your `Package.swift` file ```swift -.package(url: "https://github.com/vapor-community/sendgrid-kit.git", from: "3.0.0-rc.1") +.package(url: "https://github.com/vapor-community/sendgrid-kit.git", from: "3.0.0"), ``` and add it to your target's dependencies: ```swift -.product(name: "SendGridKit", package: "sendgrid-kit") +.product(name: "SendGridKit", package: "sendgrid-kit"), ``` ## Overview @@ -41,6 +41,9 @@ and add it to your target's dependencies: Register the config and the provider. ```swift +import AsyncHTTPClient +import SendGridKit + let httpClient = HTTPClient(...) let sendGridClient = SendGridClient(httpClient: httpClient, apiKey: "YOUR_API_KEY") ``` @@ -65,6 +68,8 @@ If the request to the API failed for any reason a `SendGridError` is thrown, whi Simply ensure you catch errors thrown like any other throwing function. ```swift +import SendGridKit + do { try await sendGridClient.send(email: email) } catch let error as SendGridError { diff --git a/Sources/SendGridKit/Models/AdvancedSuppressionManager.swift b/Sources/SendGridKit/Models/AdvancedSuppressionManager.swift index 5a213db..cd0b381 100644 --- a/Sources/SendGridKit/Models/AdvancedSuppressionManager.swift +++ b/Sources/SendGridKit/Models/AdvancedSuppressionManager.swift @@ -1,5 +1,6 @@ import Foundation +/// An object allowing you to specify how to handle unsubscribes. public struct AdvancedSuppressionManager: Codable, Sendable { /// The unsubscribe group to associate with this email. /// diff --git a/Sources/SendGridKit/Models/EmailAddress.swift b/Sources/SendGridKit/Models/EmailAddress.swift index 439e7f7..ef70e30 100644 --- a/Sources/SendGridKit/Models/EmailAddress.swift +++ b/Sources/SendGridKit/Models/EmailAddress.swift @@ -1,5 +1,6 @@ import Foundation +/// An email address to use in an email to be sent via the SendGrid API. public struct EmailAddress: Codable, Sendable { /// The email address of the person to whom you are sending an email. public var email: String diff --git a/Sources/SendGridKit/Models/EmailAttachment.swift b/Sources/SendGridKit/Models/EmailAttachment.swift index c2c8027..58a7d8f 100644 --- a/Sources/SendGridKit/Models/EmailAttachment.swift +++ b/Sources/SendGridKit/Models/EmailAttachment.swift @@ -1,5 +1,6 @@ import Foundation +/// An attachment to include in an email. public struct EmailAttachment: Codable, Sendable { /// The Base64 encoded content of the attachment. public var content: String @@ -19,8 +20,12 @@ public struct EmailAttachment: Codable, Sendable { /// such as opening or downloading the file. public var disposition: Disposition? + /// The disposition of the attachment. public enum Disposition: String, Codable, Sendable { + /// The attachment is displayed automatically within the message. case inline + + /// The attached file requires some action to be taken before it is displayed. case attachment } diff --git a/Sources/SendGridKit/Models/EmailContent.swift b/Sources/SendGridKit/Models/EmailContent.swift index c0d5f41..0d2df9d 100644 --- a/Sources/SendGridKit/Models/EmailContent.swift +++ b/Sources/SendGridKit/Models/EmailContent.swift @@ -1,5 +1,6 @@ import Foundation +/// The content of an email. public struct EmailContent: Codable, Sendable { /// The MIME type of the content you are including in your email. /// diff --git a/Sources/SendGridKit/Models/MailSettings.swift b/Sources/SendGridKit/Models/MailSettings.swift index 7233d48..597da2f 100644 --- a/Sources/SendGridKit/Models/MailSettings.swift +++ b/Sources/SendGridKit/Models/MailSettings.swift @@ -1,5 +1,6 @@ import Foundation +/// Advanced settings to control how you would like to handle the email. public struct MailSettings: Codable, Sendable { /// Allows you to bypass all unsubscribe groups and suppressions to ensure that the email is delivered to every single recipient. /// @@ -47,32 +48,44 @@ public struct MailSettings: Codable, Sendable { } } -public struct Setting: Codable, Sendable { - /// Indicates if this setting is enabled. - public var enable: Bool +extension MailSettings { + /// Indicates if a setting of ``MailSettings`` is enabled. + public struct Setting: Codable, Sendable, ExpressibleByBooleanLiteral { + /// Indicates if this setting is enabled. + public var enable: Bool - public init(enable: Bool) { - self.enable = enable + public init(enable: Bool) { + self.enable = enable + } + + public init(booleanLiteral value: Bool) { + self.init(enable: value) + } } -} -public struct Footer: Codable, Sendable { - /// Indicates if this setting is enabled. - public var enable: Bool + /// The default footer that you would like included on every email. + public struct Footer: Codable, Sendable, ExpressibleByBooleanLiteral { + /// Indicates if this setting is enabled. + public var enable: Bool + + /// The plain text content of your footer. + public var text: String? - /// The plain text content of your footer. - public var text: String? + /// The HTML content of your footer. + public var html: String? - /// The HTML content of your footer. - public var html: String? + public init( + enable: Bool, + text: String? = nil, + html: String? = nil + ) { + self.enable = enable + self.text = text + self.html = html + } - public init( - enable: Bool, - text: String? = nil, - html: String? = nil - ) { - self.enable = enable - self.text = text - self.html = html + public init(booleanLiteral value: Bool) { + self.init(enable: value) + } } } diff --git a/Sources/SendGridKit/Models/Personalization.swift b/Sources/SendGridKit/Models/Personalization.swift index 66abe9e..11aceaa 100644 --- a/Sources/SendGridKit/Models/Personalization.swift +++ b/Sources/SendGridKit/Models/Personalization.swift @@ -1,5 +1,6 @@ import Foundation +/// An object containing the personalization of an email. public struct Personalization: Codable, Sendable { /// An array of recipients. /// diff --git a/Sources/SendGridKit/Models/SendGridEmail.swift b/Sources/SendGridKit/Models/SendGridEmail.swift index aff8657..e96f907 100644 --- a/Sources/SendGridKit/Models/SendGridEmail.swift +++ b/Sources/SendGridKit/Models/SendGridEmail.swift @@ -1,5 +1,6 @@ import Foundation +/// An email to be sent using the SendGrid API. public struct SendGridEmail: Codable, Sendable { /// An array of messages and their metadata. /// @@ -78,6 +79,7 @@ public struct SendGridEmail: Codable, S /// Settings to determine how you would like to track the metrics of how your recipients interact with your email. public var trackingSettings: TrackingSettings? + /// Initialize a new ``SendGridEmail`` using a custom dynamic template data type. public init( personalizations: [Personalization], from: EmailAddress, @@ -138,6 +140,7 @@ public struct SendGridEmail: Codable, S } extension SendGridEmail where DynamicTemplateData == [String: String] { + /// Initialize a new ``SendGridEmail`` using a dictionary of `String` as dynamic template data. public init( personalizations: [Personalization<[String: String]>], from: EmailAddress, diff --git a/Sources/SendGridKit/Models/SendGridError.swift b/Sources/SendGridKit/Models/SendGridError.swift index 64a1fdf..fad77fd 100644 --- a/Sources/SendGridKit/Models/SendGridError.swift +++ b/Sources/SendGridKit/Models/SendGridError.swift @@ -1,19 +1,22 @@ import Foundation +/// An error response from the SendGrid API. public struct SendGridError: Error, Decodable, Sendable { - public var errors: [SendGridErrorResponse]? + /// The errors returned by the SendGrid API. + public var errors: [Description]? /// When applicable, this property value will be an error ID. public var id: String? -} -public struct SendGridErrorResponse: Decodable, Sendable { - /// An error message. - public var message: String? + /// The description of the ``SendGridError``. + public struct Description: Decodable, Sendable { + /// An error message. + public var message: String? - /// When applicable, this property value will be the field that generated the error. - public var field: String? + /// When applicable, this property value will be the field that generated the error. + public var field: String? - /// When applicable, this property value will be helper text or a link to documentation to help you troubleshoot the error. - public var help: String? + /// When applicable, this property value will be helper text or a link to documentation to help you troubleshoot the error. + public var help: String? + } } diff --git a/Sources/SendGridKit/Models/TrackingSettings.swift b/Sources/SendGridKit/Models/TrackingSettings.swift index 7cc1537..2f60022 100644 --- a/Sources/SendGridKit/Models/TrackingSettings.swift +++ b/Sources/SendGridKit/Models/TrackingSettings.swift @@ -1,5 +1,6 @@ import Foundation +/// Settings to enable tracking for your email. public struct TrackingSettings: Codable, Sendable { /// Allows you to track whether a recipient clicked a link in your email. public var clickTracking: ClickTracking? @@ -38,130 +39,142 @@ public struct TrackingSettings: Codable, Sendable { } } -public struct ClickTracking: Codable, Sendable { - /// Indicates if this setting is enabled. - public var enable: Bool - - /// 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 +extension TrackingSettings { + /// Allows you to track whether a recipient clicked a link in your email. + public struct ClickTracking: Codable, Sendable { + /// Indicates if this setting is enabled. + public var enable: Bool + + /// 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 + case enableText = "enable_text" + } } - private enum CodingKeys: String, CodingKey { - case enable - case enableText = "enable_text" - } -} - -public struct OpenTracking: Codable, Sendable { - /// Indicates if this setting is enabled. - public var enable: Bool - - /// Allows you to specify a substitution tag that you can insert in the body of your email at a location that you desire. + /// Allows you to track if the email was opened by including a single transparent pixel image in the body of the message content. /// - /// > Note: This tag will be replaced by the open tracking pixel. - public var substitutionTag: String? - - public init( - enable: Bool, - substitutionTag: String? = nil - ) { - self.enable = enable - self.substitutionTag = substitutionTag + /// When the message is opened, a request for the pixel is sent. + /// That request indicates to Twilio SendGrid that the message was opened. + public struct OpenTracking: Codable, Sendable { + /// Indicates if this setting is enabled. + public var enable: Bool + + /// Allows you to specify a substitution tag that you can insert in the body of your email at a location that you desire. + /// + /// > Note: This tag will be replaced by the open tracking pixel. + public var substitutionTag: String? + + public init( + enable: Bool, + substitutionTag: String? = nil + ) { + self.enable = enable + self.substitutionTag = substitutionTag + } + + private enum CodingKeys: String, CodingKey { + case enable + case substitutionTag = "substitution_tag" + } } - private enum CodingKeys: String, CodingKey { - case enable - case substitutionTag = "substitution_tag" - } -} - -public struct SubscriptionTracking: Codable, Sendable { - /// Indicates if this setting is enabled. - public var enable: Bool - - /// Text to be appended to the email, with the subscription tracking link. - /// - /// > Tip: You may control where the link is by using the tag `<% %>`. - public var text: String? - - /// HTML to be appended to the email, with the subscription tracking link. - /// - /// > Tip: You may control where the link is by using the tag `<% %>`. - public var html: String? - - /// A tag that will be replaced with the unsubscribe URL. - /// - /// For example: `[unsubscribe_url]`. + /// Allows you to insert a subscription management link at the bottom of the text and HTML bodies of your email. /// - /// If this parameter is used, it will override both the ``SubscriptionTracking/text`` and ``SubscriptionTracking/html`` parameters. - /// The URL of the link will be placed at the substitution tag’s location, with no additional formatting. - public var substitutionTag: String? - - public init( - enable: Bool, - text: String? = nil, - html: String? = nil, - substitutionTag: String? = nil - ) { - self.enable = enable - self.text = text - self.html = html - self.substitutionTag = substitutionTag - } - - private enum CodingKeys: String, CodingKey { - case enable - case text - case html - case substitutionTag = "substitution_tag" - } -} - -public struct GoogleAnalytics: Codable, Sendable { - /// Indicates if this setting is enabled. - public var enable: Bool - - /// Name of the referrer source. (e.g. Google, SomeDomain.com, or Marketing Email) - public var utmSource: String? - - /// Name of the marketing medium. (e.g. Email) - public var utmMedium: String? - - /// Used to identify any paid keywords. - public var utmTerm: String? - - /// Used to differentiate your campaign from advertisements. - public var utmContent: String? - - /// The name of the campaign. - public var utmCampaign: String? - - public init( - enable: Bool, - utmSource: String? = nil, - utmMedium: String? = nil, - utmTerm: String? = nil, - utmContent: String? = nil, - utmCampaign: String? = nil - ) { - self.enable = enable - self.utmSource = utmSource - self.utmMedium = utmMedium - self.utmTerm = utmTerm - self.utmContent = utmContent - self.utmCampaign = utmCampaign + /// If you would like to specify the location of the link within your email, + /// you may use the ``TrackingSettings/SubscriptionTracking/substitutionTag`` property. + public struct SubscriptionTracking: Codable, Sendable { + /// Indicates if this setting is enabled. + public var enable: Bool + + /// Text to be appended to the email, with the subscription tracking link. + /// + /// > Tip: You may control where the link is by using the tag `<% %>`. + public var text: String? + + /// HTML to be appended to the email, with the subscription tracking link. + /// + /// > Tip: You may control where the link is by using the tag `<% %>`. + public var html: String? + + /// A tag that will be replaced with the unsubscribe URL. + /// + /// For example: `[unsubscribe_url]`. + /// + /// If this parameter is used, it will override both the ``SubscriptionTracking/text`` and ``SubscriptionTracking/html`` parameters. + /// The URL of the link will be placed at the substitution tag’s location, with no additional formatting. + public var substitutionTag: String? + + public init( + enable: Bool, + text: String? = nil, + html: String? = nil, + substitutionTag: String? = nil + ) { + self.enable = enable + self.text = text + self.html = html + self.substitutionTag = substitutionTag + } + + private enum CodingKeys: String, CodingKey { + case enable + case text + case html + case substitutionTag = "substitution_tag" + } } - private enum CodingKeys: String, CodingKey { - case enable - case utmSource = "utm_source" - case utmMedium = "utm_medium" - case utmTerm = "utm_term" - case utmContent = "utm_content" - case utmCampaign = "utm_campaign" + /// Allows you to enable tracking provided by Google Analytics. + public struct GoogleAnalytics: Codable, Sendable { + /// Indicates if this setting is enabled. + public var enable: Bool + + /// Name of the referrer source. (e.g. Google, SomeDomain.com, or Marketing Email) + public var utmSource: String? + + /// Name of the marketing medium. (e.g. Email) + public var utmMedium: String? + + /// Used to identify any paid keywords. + public var utmTerm: String? + + /// Used to differentiate your campaign from advertisements. + public var utmContent: String? + + /// The name of the campaign. + public var utmCampaign: String? + + public init( + enable: Bool, + utmSource: String? = nil, + utmMedium: String? = nil, + utmTerm: String? = nil, + utmContent: String? = nil, + utmCampaign: String? = nil + ) { + self.enable = enable + self.utmSource = utmSource + self.utmMedium = utmMedium + self.utmTerm = utmTerm + self.utmContent = utmContent + self.utmCampaign = utmCampaign + } + + private enum CodingKeys: String, CodingKey { + case enable + case utmSource = "utm_source" + case utmMedium = "utm_medium" + case utmTerm = "utm_term" + case utmContent = "utm_content" + case utmCampaign = "utm_campaign" + } } } diff --git a/Sources/SendGridKit/SendGridClient.swift b/Sources/SendGridKit/SendGridClient.swift index 3161664..5e4f599 100644 --- a/Sources/SendGridKit/SendGridClient.swift +++ b/Sources/SendGridKit/SendGridClient.swift @@ -4,10 +4,11 @@ import NIO import NIOFoundationCompat import NIOHTTP1 +/// A client for sending emails using the SendGrid API. public struct SendGridClient: Sendable { - let apiURL: String - let httpClient: HTTPClient - let apiKey: String + private let apiURL: String + private let httpClient: HTTPClient + private let apiKey: String private let encoder: JSONEncoder = { let encoder = JSONEncoder() @@ -33,6 +34,9 @@ public struct SendGridClient: Sendable { self.apiURL = forEU ? "https://api.eu.sendgrid.com/v3/mail/send" : "https://api.sendgrid.com/v3/mail/send" } + /// Send an email using the SendGrid API. + /// + /// - Parameter email: The ``SendGridEmail`` to send. public func send(email: SendGridEmail) async throws { var headers = HTTPHeaders() headers.add(name: "Authorization", value: "Bearer \(self.apiKey)") @@ -49,6 +53,6 @@ public struct SendGridClient: Sendable { if (200...299).contains(response.status.code) { return } // `JSONDecoder` will handle empty body by throwing decoding error - throw try await decoder.decode(SendGridError.self, from: response.body.collect(upTo: 1024 * 1024)) + throw try await self.decoder.decode(SendGridError.self, from: response.body.collect(upTo: 1024 * 1024)) } } diff --git a/Sources/SendGridKit/SendGridKit.docc/Extensions/SendGridClient.md b/Sources/SendGridKit/SendGridKit.docc/Extensions/SendGridClient.md new file mode 100644 index 0000000..0360428 --- /dev/null +++ b/Sources/SendGridKit/SendGridKit.docc/Extensions/SendGridClient.md @@ -0,0 +1,42 @@ +# ``SendGridKit/SendGridClient`` + +## Overview + +Register the config and the provider. + +```swift +import AsyncHTTPClient +import SendGridKit + +let httpClient = HTTPClient(...) +let sendGridClient = SendGridClient(httpClient: httpClient, apiKey: "YOUR_API_KEY") +``` + +### Using the API + +You can use all of the available parameters here to build your ``SendGridEmail``. + +Usage in a route closure would be as followed: + +```swift +import SendGridKit + +let email = SendGridEmail(...) +try await sendGridClient.send(email: email) +``` + +### Error handling + +If the request to the API failed for any reason a ``SendGridError`` is thrown, which has an ``SendGridError/errors`` property that contains an array of errors returned by the API. + +Simply ensure you catch errors thrown like any other throwing function. + +```swift +import SendGridKit + +do { + try await sendGridClient.send(email: email) +} catch let error as SendGridError { + print(error) +} +``` diff --git a/Sources/SendGridKit/SendGridKit.docc/SendGridKit.md b/Sources/SendGridKit/SendGridKit.docc/SendGridKit.md index 3be4880..9d7fc2b 100644 --- a/Sources/SendGridKit/SendGridKit.docc/SendGridKit.md +++ b/Sources/SendGridKit/SendGridKit.docc/SendGridKit.md @@ -1,42 +1,12 @@ # ``SendGridKit`` -📧 SendGridKit is a Swift package used to communicate with the SendGrid API for Server Side Swift Apps. +A Swift on Server SDK for the SendGrid API ## Overview -Register the config and the provider. +`SendGridKit` is a Swift package that helps you communicate with the SendGrid API in your Server Side Swift applications. -```swift -let httpClient = HTTPClient(...) -let sendGridClient = SendGridClient(httpClient: httpClient, apiKey: "YOUR_API_KEY") -``` - -### Using the API - -You can use all of the available parameters here to build your ``SendGridEmail``. - -Usage in a route closure would be as followed: - -```swift -import SendGridKit - -let email = SendGridEmail(...) -try await sendGridClient.send(email: email) -``` - -### Error handling - -If the request to the API failed for any reason a ``SendGridError`` is thrown, which has an ``SendGridError/errors`` property that contains an array of errors returned by the API. - -Simply ensure you catch errors thrown like any other throwing function. - -```swift -do { - try await sendGridClient.send(email: email) -} catch let error as SendGridError { - print(error) -} -``` +Send simple emails or leverage the full capabilities of [SendGrid's V3 API](https://www.twilio.com/docs/sendgrid/api-reference/mail-send/mail-send). ## Topics @@ -47,6 +17,13 @@ do { - ``Personalization`` - ``EmailAddress`` - ``EmailContent`` +- ``EmailAttachment`` + +### Advanced + +- ``MailSettings`` +- ``TrackingSettings`` +- ``AdvancedSuppressionManager`` ### Errors diff --git a/Tests/SendGridKitTests/SendGridTestsKit.swift b/Tests/SendGridKitTests/SendGridKitTests.swift similarity index 77% rename from Tests/SendGridKitTests/SendGridTestsKit.swift rename to Tests/SendGridKitTests/SendGridKitTests.swift index 954a1f1..6982a0a 100644 --- a/Tests/SendGridKitTests/SendGridTestsKit.swift +++ b/Tests/SendGridKitTests/SendGridKitTests.swift @@ -2,15 +2,19 @@ import AsyncHTTPClient import SendGridKit import Testing +@Suite("SendGridKit Tests") struct SendGridKitTests { var client: SendGridClient + // TODO: Replace with `false` when you have a valid API key + let credentialsAreInvalid = true init() { // TODO: Replace with a valid API key to test client = SendGridClient(httpClient: HTTPClient.shared, apiKey: "YOUR-API-KEY") } - @Test func sendEmail() async throws { + @Test("Send Email") + func sendEmail() async throws { // TODO: Replace to address with the email address you'd like to recieve your test email let emailAddress = EmailAddress("TO-ADDRESS") // TODO: Replace from address with the email address associated with your verified Sender Identity @@ -27,25 +31,24 @@ struct SendGridKitTests { let emailContent = EmailContent("This email was sent using SendGridKit!") - let setting = Setting(enable: true) let mailSettings = MailSettings( - bypassListManagement: setting, - bypassSpamManagement: setting, - bypassBounceManagement: setting, - footer: Footer(enable: true, text: "footer", html: "footer"), - sandboxMode: setting + bypassListManagement: true, + bypassSpamManagement: true, + bypassBounceManagement: true, + footer: false, + sandboxMode: true ) let trackingSettings = TrackingSettings( - clickTracking: ClickTracking(enable: true, enableText: true), - openTracking: OpenTracking(enable: true, substitutionTag: "open_tracking"), - subscriptionTracking: SubscriptionTracking( + clickTracking: .init(enable: true, enableText: true), + openTracking: .init(enable: true, substitutionTag: "open_tracking"), + subscriptionTracking: .init( enable: true, text: "sub_text", html: "sub_html", substitutionTag: "sub_tag" ), - ganalytics: GoogleAnalytics( + ganalytics: .init( enable: true, utmSource: "utm_source", utmMedium: "utm_medium", @@ -70,12 +73,12 @@ struct SendGridKitTests { try await withKnownIssue { try await client.send(email: email) } when: { - // TODO: Replace with `false` when you have a valid API key - true + credentialsAreInvalid } } - @Test func dynamicTemplateData() async throws { + @Test("DynamicTemplateData") + func dynamicTemplateData() async throws { struct DynamicTemplateData: Codable, Sendable { let text: String let integer: Int @@ -96,8 +99,7 @@ struct SendGridKitTests { try await withKnownIssue { try await client.send(email: email) } when: { - // TODO: Replace with `false` when you have a valid API key - true + credentialsAreInvalid } } }