Skip to content

Commit c26a042

Browse files
committed
Refactor package structure and add strict concurrency settings; implement findContact method in IndiePitcher and update tests for improved readability and functionality.
1 parent adfcbb2 commit c26a042

File tree

4 files changed

+84
-42
lines changed

4 files changed

+84
-42
lines changed

Package.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ let package = Package(
1111
.library(
1212
name: "IndiePitcherSwift",
1313
targets: ["IndiePitcherSwift"]
14-
),
14+
)
1515
],
1616
dependencies: [
1717
.package(url: "https://github.com/swift-server/async-http-client.git", from: "1.20.0"),
@@ -22,11 +22,13 @@ let package = Package(
2222
// Targets can depend on other targets in this package and products from dependencies.
2323
.target(
2424
name: "IndiePitcherSwift",
25-
dependencies: [.product(name: "AsyncHTTPClient", package: "async-http-client"),]
25+
dependencies: [.product(name: "AsyncHTTPClient", package: "async-http-client")],
26+
swiftSettings: [.enableExperimentalFeature("StrictConcurrency=complete")]
2627
),
2728
.testTarget(
2829
name: "IndiePitcherSwiftTests",
29-
dependencies: ["IndiePitcherSwift", "Nimble"]
30+
dependencies: ["IndiePitcherSwift", "Nimble"],
31+
swiftSettings: [.enableExperimentalFeature("StrictConcurrency=complete")]
3032
),
3133
]
3234
)

Sources/IndiePitcherSwift/IndiePitcherSwift.swift

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import AsyncHTTPClient
2-
import Foundation
32
import NIO
43
import NIOCore
54
import NIOFoundationCompat
65
import NIOHTTP1
76

7+
#if canImport(FoundationEssentials)
8+
import FoundationEssentials
9+
#else
10+
import Foundation
11+
#endif
12+
813
extension HTTPClientResponse {
914
var isOk: Bool {
1015
status.code >= 200 && status.code < 300
@@ -25,7 +30,10 @@ public struct IndiePitcher: Sendable {
2530
/// - client: Vapor's client instance to use to perform network requests. Uses the shared client by default.
2631
/// - apiKey: Your project's secret key.
2732
/// - baseURL: The base URL for the API. Defaults to "https://api.indiepitcher.com/v1".
28-
public init(client: HTTPClient = .shared, apiKey: String, baseURL: String = "https://api.indiepitcher.com/v1") {
33+
public init(
34+
client: HTTPClient = .shared, apiKey: String,
35+
baseURL: String = "https://api.indiepitcher.com/v1"
36+
) {
2937
self.client = client
3038
self.apiKey = apiKey
3139
self.baseURL = baseURL
@@ -56,8 +64,7 @@ public struct IndiePitcher: Sendable {
5664
baseURL + path
5765
}
5866

59-
private func post<T: Codable>(path: String, body: Codable) async throws -> T
60-
{
67+
private func post<T: Codable>(path: String, body: Codable) async throws -> T {
6168

6269
var headers = commonHeaders
6370
headers.add(name: "Content-Type", value: "application/json")
@@ -146,6 +153,17 @@ public struct IndiePitcher: Sendable {
146153
try await post(path: "/contacts/create", body: contact)
147154
}
148155

156+
/// Searches for a contact with the specified email address.
157+
///
158+
/// - Parameter email: The email address of the contact to find.
159+
/// - Returns: A `DataResponse` containing the contact information if found.
160+
/// - Throws: An error if the contact cannot be found or if there's an issue with the request.
161+
public func findContact(email: String) async throws -> DataResponse<Contact> {
162+
let encodedEmail =
163+
email.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? email
164+
return try await get(path: "/contacts/find?email=\(encodedEmail)")
165+
}
166+
149167
/// Add miultiple contacts (up to 100) using a single API call to avoid being rate limited. Payloads with `updateIfExists` is set to `true` will be updated if a contact with given email already exists.
150168
/// - Parameter contacts: Contact properties
151169
/// - Returns: A generic empty response.

Sources/IndiePitcherSwift/dtos.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import Foundation
1+
#if canImport(FoundationEssentials)
2+
import FoundationEssentials
3+
#else
4+
import Foundation
5+
#endif
26

37
/// The format of the email body
48
public enum EmailBodyFormat: String, Codable, Sendable {
Lines changed: 52 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,91 @@
1-
import XCTest
2-
@testable import IndiePitcherSwift
31
import AsyncHTTPClient
42
import Nimble
3+
import XCTest
4+
5+
@testable import IndiePitcherSwift
56

67
final class IndiePitcherSwiftTests: XCTestCase {
7-
8+
89
var indiePitcher: IndiePitcher!
9-
10+
1011
override func setUp() async throws {
1112
indiePitcher = IndiePitcher(apiKey: IP_SECRET_API_KEY)
1213
}
13-
14+
1415
override func tearDown() async throws {
1516
// work around API rate limiting
1617
try await Task.sleep(for: .seconds(1))
1718
}
18-
19+
1920
func testThrowRequestError() async {
2021
let indiePitcherx = IndiePitcher(apiKey: "fake")
21-
await expect({try await indiePitcherx.listMailingLists()})
22+
await expect({ try await indiePitcherx.listMailingLists() })
2223
.to(throwError(IndiePitcherRequestError(statusCode: 401, reason: "Unauthorized")))
2324
}
24-
25+
2526
func testSendTransactionalEmailMarkdown() async throws {
26-
27-
try await indiePitcher.sendEmail(data: .init(to: "[email protected]",
28-
subject: "Test email from IP Swift SDK unit tests",
29-
body: "This is a test body that supports **markdown**.",
30-
bodyFormat: .markdown))
27+
28+
try await indiePitcher.sendEmail(
29+
data: .init(
30+
31+
subject: "Test email from IP Swift SDK unit tests",
32+
body: "This is a test body that supports **markdown**.",
33+
bodyFormat: .markdown))
3134
}
32-
35+
3336
func testGetMailingLists() async throws {
3437
let listsResponse = try await indiePitcher.listMailingLists()
3538
expect(listsResponse.metadata.total) == 3
3639
expect(listsResponse.data.count) == 3
3740
}
38-
41+
3942
func testSendMarketingEmailToList() async throws {
40-
try await indiePitcher.sendEmailToMailingList(data: .init(subject: "Test marketing email from IP Swift SDK unit tests",
41-
body: "This is a test body of a marketing email that supports **markdown**.",
42-
bodyFormat: .markdown,
43-
list: "integration-tests"))
43+
try await indiePitcher.sendEmailToMailingList(
44+
data: .init(
45+
subject: "Test marketing email from IP Swift SDK unit tests",
46+
body: "This is a test body of a marketing email that supports **markdown**.",
47+
bodyFormat: .markdown,
48+
list: "integration-tests"))
4449
}
45-
50+
4651
func testSendEmailToContact() async throws {
47-
try await indiePitcher.sendEmailToContact(data: .init(contactEmail: "[email protected]",
48-
subject: "Test personalized contact email from IP Swift SDK unit tests",
49-
body: "This is a test body of a personalized transactional email that supports **markdown**.",
50-
bodyFormat: .markdown,
51-
list: "integration-tests",
52-
delaySeconds: 60))
52+
try await indiePitcher.sendEmailToContact(
53+
data: .init(
54+
contactEmail: "[email protected]",
55+
subject: "Test personalized contact email from IP Swift SDK unit tests",
56+
body:
57+
"This is a test body of a personalized transactional email that supports **markdown**.",
58+
bodyFormat: .markdown,
59+
list: "integration-tests",
60+
delaySeconds: 60))
5361
}
54-
62+
5563
func testContactManagement() async throws {
5664
let email = "[email protected]"
57-
try await indiePitcher.addContact(contact: .init(email: email,
58-
subscribedToLists: ["test_list_1", "test_list_2"]))
65+
try await indiePitcher.addContact(
66+
contact: .init(
67+
email: email,
68+
subscribedToLists: ["test_list_1", "test_list_2"]))
69+
70+
let contact = try await indiePitcher.findContact(email: email).data
71+
expect(contact.email) == email
72+
expect(contact.subscribedToLists) == ["test_list_1", "test_list_2"]
5973

6074
try await indiePitcher.deleteContact(email: email)
6175
}
62-
76+
6377
func testAddMultipleContacts() async throws {
64-
65-
try await indiePitcher.addContacts(contacts: [.init(email: "[email protected]"), .init(email: "[email protected]")])
78+
79+
try await indiePitcher.addContacts(contacts: [
80+
.init(email: "[email protected]"), .init(email: "[email protected]"),
81+
])
6682
try await indiePitcher.deleteContact(email: "[email protected]")
6783
try await indiePitcher.deleteContact(email: "[email protected]")
6884
}
69-
85+
7086
func testCreatePortalSession() async throws {
71-
_ = try await indiePitcher.createMailingListsPortalSession(contactEmail: "[email protected]", returnURL: .init(string: "https://indiepitcher.com")!)
87+
_ = try await indiePitcher.createMailingListsPortalSession(
88+
contactEmail: "[email protected]",
89+
returnURL: .init(string: "https://indiepitcher.com")!)
7290
}
7391
}

0 commit comments

Comments
 (0)