diff --git a/Sources/Confidence/Confidence.swift b/Sources/Confidence/Confidence.swift index 4d63e6cd..29a83436 100644 --- a/Sources/Confidence/Confidence.swift +++ b/Sources/Confidence/Confidence.swift @@ -273,7 +273,8 @@ public class Confidence: ConfidenceEventSender { storage: storage, context: context, parent: self, - debugLogger: debugLogger) + debugLogger: debugLogger + ) } private func withLock(callback: @escaping (Confidence) -> Void) { @@ -297,6 +298,7 @@ extension Confidence { // Can be configured internal var region: ConfidenceRegion = .global internal var initialContext: ConfidenceStruct = [:] + internal var timeout: Double = 10 // Injectable for testing internal var flagApplier: FlagApplier? @@ -356,6 +358,14 @@ extension Confidence { return self } + /** + Set the timeout for the network request, defaulting to 10 seconds. + */ + public func withTimeout(timeout: Double) -> Builder { + self.timeout = timeout + return self + } + /** Build the Confidence instance. */ @@ -368,7 +378,8 @@ extension Confidence { } let options = ConfidenceClientOptions( credentials: ConfidenceClientCredentials.clientSecret(secret: clientSecret), - region: region) + region: region, + timeoutIntervalForRequest: timeout) let metadata = ConfidenceMetadata( name: sdkId, version: "0.2.4") // x-release-please-version @@ -377,7 +388,10 @@ extension Confidence { metadata: metadata, debugLogger: debugLogger ) - let httpClient = NetworkClient(baseUrl: BaseUrlMapper.from(region: options.region)) + let httpClient = NetworkClient( + baseUrl: BaseUrlMapper.from(region: options.region), + timeoutIntervalForRequests: options.timeoutIntervalForRequest + ) let flagApplier = flagApplier ?? FlagApplierWithRetries( httpClient: httpClient, storage: DefaultStorage(filePath: "confidence.flags.apply"), diff --git a/Sources/Confidence/ConfidenceClientOptions.swift b/Sources/Confidence/ConfidenceClientOptions.swift index 7ddb17ee..fe64d3bb 100644 --- a/Sources/Confidence/ConfidenceClientOptions.swift +++ b/Sources/Confidence/ConfidenceClientOptions.swift @@ -4,15 +4,18 @@ struct ConfidenceClientOptions { public var credentials: ConfidenceClientCredentials public var region: ConfidenceRegion public var initializationStrategy: InitializationStrategy + public var timeoutIntervalForRequest: Double public init( credentials: ConfidenceClientCredentials, region: ConfidenceRegion? = nil, - initializationStrategy: InitializationStrategy = .fetchAndActivate + initializationStrategy: InitializationStrategy = .fetchAndActivate, + timeoutIntervalForRequest: Double ) { self.credentials = credentials self.region = region ?? .global self.initializationStrategy = initializationStrategy + self.timeoutIntervalForRequest = timeoutIntervalForRequest } } diff --git a/Sources/Confidence/Http/NetworkClient.swift b/Sources/Confidence/Http/NetworkClient.swift index b9898a6f..0ce563e7 100644 --- a/Sources/Confidence/Http/NetworkClient.swift +++ b/Sources/Confidence/Http/NetworkClient.swift @@ -5,12 +5,14 @@ final class NetworkClient: HttpClient { private let retry: Retry private let session: URLSession private let baseUrl: String + private var timeoutIntervalForRequests: Double public init( session: URLSession? = nil, baseUrl: String, defaultHeaders: [String: String] = [:], - retry: Retry = .none + retry: Retry = .none, + timeoutIntervalForRequests: Double ) { self.session = session @@ -24,6 +26,7 @@ final class NetworkClient: HttpClient { self.headers = defaultHeaders self.retry = retry self.baseUrl = baseUrl + self.timeoutIntervalForRequests = timeoutIntervalForRequests } public func post( diff --git a/Sources/Confidence/RemoteConfidenceClient.swift b/Sources/Confidence/RemoteConfidenceClient.swift index 67573571..7666f6ff 100644 --- a/Sources/Confidence/RemoteConfidenceClient.swift +++ b/Sources/Confidence/RemoteConfidenceClient.swift @@ -23,7 +23,11 @@ public class RemoteConfidenceClient: ConfidenceClient { case .usa: self.baseUrl = "https://events.us.confidence.dev/v1/events" } - self.httpClient = NetworkClient(session: session, baseUrl: baseUrl) + self.httpClient = NetworkClient( + session: session, + baseUrl: baseUrl, + timeoutIntervalForRequests: options.timeoutIntervalForRequest + ) self.metadata = metadata self.debugLogger = debugLogger } diff --git a/Sources/Confidence/RemoteResolveConfidenceClient.swift b/Sources/Confidence/RemoteResolveConfidenceClient.swift index b2f168cd..479dead8 100644 --- a/Sources/Confidence/RemoteResolveConfidenceClient.swift +++ b/Sources/Confidence/RemoteResolveConfidenceClient.swift @@ -19,7 +19,8 @@ class RemoteConfidenceResolveClient: ConfidenceResolveClient { self.metadata = metadata self.httpClient = NetworkClient( session: session, - baseUrl: BaseUrlMapper.from(region: options.region)) + baseUrl: BaseUrlMapper.from(region: options.region), + timeoutIntervalForRequests: options.timeoutIntervalForRequest) } // MARK: Resolver diff --git a/Tests/ConfidenceTests/ConfidenceContextTests.swift b/Tests/ConfidenceTests/ConfidenceContextTests.swift index c41447cc..155c952d 100644 --- a/Tests/ConfidenceTests/ConfidenceContextTests.swift +++ b/Tests/ConfidenceTests/ConfidenceContextTests.swift @@ -6,7 +6,7 @@ final class ConfidenceContextTests: XCTestCase { func testWithContext() { let client = RemoteConfidenceResolveClient( options: ConfidenceClientOptions( - credentials: ConfidenceClientCredentials.clientSecret(secret: "")), + credentials: ConfidenceClientCredentials.clientSecret(secret: ""), timeoutIntervalForRequest: 10), session: MockedClientURLProtocol.mockedSession(), metadata: ConfidenceMetadata(name: "", version: "")) @@ -33,7 +33,7 @@ final class ConfidenceContextTests: XCTestCase { func testWithContextUpdateParent() { let client = RemoteConfidenceResolveClient( options: ConfidenceClientOptions( - credentials: ConfidenceClientCredentials.clientSecret(secret: "")), + credentials: ConfidenceClientCredentials.clientSecret(secret: ""), timeoutIntervalForRequest: 10), session: MockedClientURLProtocol.mockedSession(), metadata: ConfidenceMetadata(name: "", version: "")) @@ -65,7 +65,7 @@ final class ConfidenceContextTests: XCTestCase { func testUpdateLocalContext() { let client = RemoteConfidenceResolveClient( options: ConfidenceClientOptions( - credentials: ConfidenceClientCredentials.clientSecret(secret: "")), + credentials: ConfidenceClientCredentials.clientSecret(secret: ""), timeoutIntervalForRequest: 10), session: MockedClientURLProtocol.mockedSession(), metadata: ConfidenceMetadata(name: "", version: "")) @@ -92,7 +92,7 @@ final class ConfidenceContextTests: XCTestCase { func testUpdateLocalContextWithoutOverride() { let client = RemoteConfidenceResolveClient( options: ConfidenceClientOptions( - credentials: ConfidenceClientCredentials.clientSecret(secret: "")), + credentials: ConfidenceClientCredentials.clientSecret(secret: ""), timeoutIntervalForRequest: 10), session: MockedClientURLProtocol.mockedSession(), metadata: ConfidenceMetadata(name: "", version: "")) @@ -123,7 +123,7 @@ final class ConfidenceContextTests: XCTestCase { func testUpdateParentContextWithOverride() { let client = RemoteConfidenceResolveClient( options: ConfidenceClientOptions( - credentials: ConfidenceClientCredentials.clientSecret(secret: "")), + credentials: ConfidenceClientCredentials.clientSecret(secret: ""), timeoutIntervalForRequest: 10), session: MockedClientURLProtocol.mockedSession(), metadata: ConfidenceMetadata(name: "", version: "")) @@ -154,7 +154,7 @@ final class ConfidenceContextTests: XCTestCase { func testRemoveContextEntry() { let client = RemoteConfidenceResolveClient( options: ConfidenceClientOptions( - credentials: ConfidenceClientCredentials.clientSecret(secret: "")), + credentials: ConfidenceClientCredentials.clientSecret(secret: ""), timeoutIntervalForRequest: 10), session: MockedClientURLProtocol.mockedSession(), metadata: ConfidenceMetadata(name: "", version: "")) @@ -179,7 +179,7 @@ final class ConfidenceContextTests: XCTestCase { func testRemoveContextEntryFromParent() { let client = RemoteConfidenceResolveClient( options: ConfidenceClientOptions( - credentials: ConfidenceClientCredentials.clientSecret(secret: "")), + credentials: ConfidenceClientCredentials.clientSecret(secret: ""), timeoutIntervalForRequest: 10), session: MockedClientURLProtocol.mockedSession(), metadata: ConfidenceMetadata(name: "", version: "")) @@ -207,7 +207,7 @@ final class ConfidenceContextTests: XCTestCase { func testRemoveContextEntryFromParentAndChild() { let client = RemoteConfidenceResolveClient( options: ConfidenceClientOptions( - credentials: ConfidenceClientCredentials.clientSecret(secret: "")), + credentials: ConfidenceClientCredentials.clientSecret(secret: ""), timeoutIntervalForRequest: 10), session: MockedClientURLProtocol.mockedSession(), metadata: ConfidenceMetadata(name: "", version: "")) @@ -238,7 +238,7 @@ final class ConfidenceContextTests: XCTestCase { func testRemoveContextEntryFromParentAndChildThenUpdate() { let client = RemoteConfidenceResolveClient( options: ConfidenceClientOptions( - credentials: ConfidenceClientCredentials.clientSecret(secret: "")), + credentials: ConfidenceClientCredentials.clientSecret(secret: ""), timeoutIntervalForRequest: 10), session: MockedClientURLProtocol.mockedSession(), metadata: ConfidenceMetadata(name: "", version: "")) @@ -271,7 +271,7 @@ final class ConfidenceContextTests: XCTestCase { func testVisitorId() { let client = RemoteConfidenceResolveClient( options: ConfidenceClientOptions( - credentials: ConfidenceClientCredentials.clientSecret(secret: "")), + credentials: ConfidenceClientCredentials.clientSecret(secret: ""), timeoutIntervalForRequest: 10), session: MockedClientURLProtocol.mockedSession(), metadata: ConfidenceMetadata(name: "", version: "")) diff --git a/Tests/ConfidenceTests/EventSenderEngineTest.swift b/Tests/ConfidenceTests/EventSenderEngineTest.swift index 7db33b67..6be4d436 100644 --- a/Tests/ConfidenceTests/EventSenderEngineTest.swift +++ b/Tests/ConfidenceTests/EventSenderEngineTest.swift @@ -110,7 +110,10 @@ final class EventSenderEngineTest: XCTestCase { func testRemoveEventsFromStorageOnBadRequest() throws { MockedClientURLProtocol.mockedOperation = .badRequest let badRequestUploader = RemoteConfidenceClient( - options: ConfidenceClientOptions(credentials: ConfidenceClientCredentials.clientSecret(secret: "")), + options: ConfidenceClientOptions( + credentials: ConfidenceClientCredentials.clientSecret(secret: ""), + timeoutIntervalForRequest: 10 + ), session: MockedClientURLProtocol.mockedSession(), metadata: ConfidenceMetadata(name: "", version: "")) @@ -135,7 +138,10 @@ final class EventSenderEngineTest: XCTestCase { func testKeepEventsInStorageForRetry() throws { MockedClientURLProtocol.mockedOperation = .needRetryLater let retryLaterUploader = RemoteConfidenceClient( - options: ConfidenceClientOptions(credentials: ConfidenceClientCredentials.clientSecret(secret: "")), + options: ConfidenceClientOptions( + credentials: ConfidenceClientCredentials.clientSecret(secret: ""), + timeoutIntervalForRequest: 10 + ), session: MockedClientURLProtocol.mockedSession(), metadata: ConfidenceMetadata(name: "", version: "")) diff --git a/Tests/ConfidenceTests/FlagApplierWithRetriesTest.swift b/Tests/ConfidenceTests/FlagApplierWithRetriesTest.swift index 5f781e5f..6ff64d34 100644 --- a/Tests/ConfidenceTests/FlagApplierWithRetriesTest.swift +++ b/Tests/ConfidenceTests/FlagApplierWithRetriesTest.swift @@ -8,7 +8,10 @@ import XCTest @available(macOS 13.0, iOS 16.0, *) class FlagApplierWithRetriesTest: XCTestCase { - private let options = ConfidenceClientOptions(credentials: .clientSecret(secret: "test")) + private let options = ConfidenceClientOptions( + credentials: .clientSecret(secret: "test"), + timeoutIntervalForRequest: 10 + ) private var storage = StorageMock() private var httpClient = HttpClientMock() private let metadata = ConfidenceMetadata(name: "test-provider-name", version: "0.0.0.") diff --git a/Tests/ConfidenceTests/RemoteConfidenceClientTests.swift b/Tests/ConfidenceTests/RemoteConfidenceClientTests.swift index 2d62d03b..bac83b1e 100644 --- a/Tests/ConfidenceTests/RemoteConfidenceClientTests.swift +++ b/Tests/ConfidenceTests/RemoteConfidenceClientTests.swift @@ -12,7 +12,7 @@ class RemoteConfidenceClientTest: XCTestCase { func testUploadDoesntThrow() async throws { let client = RemoteConfidenceClient( options: ConfidenceClientOptions( - credentials: ConfidenceClientCredentials.clientSecret(secret: "")), + credentials: ConfidenceClientCredentials.clientSecret(secret: ""), timeoutIntervalForRequest: 10), session: MockedClientURLProtocol.mockedSession(), metadata: ConfidenceMetadata(name: "", version: "")) @@ -29,7 +29,7 @@ class RemoteConfidenceClientTest: XCTestCase { func testUploadEmptyEventsDoesntThrow() async throws { let client = RemoteConfidenceClient( options: ConfidenceClientOptions( - credentials: ConfidenceClientCredentials.clientSecret(secret: "")), + credentials: ConfidenceClientCredentials.clientSecret(secret: ""), timeoutIntervalForRequest: 10), session: MockedClientURLProtocol.mockedSession(), metadata: ConfidenceMetadata(name: "", version: "")) @@ -41,7 +41,7 @@ class RemoteConfidenceClientTest: XCTestCase { MockedClientURLProtocol.mockedOperation = .firstEventFails let client = RemoteConfidenceClient( options: ConfidenceClientOptions( - credentials: ConfidenceClientCredentials.clientSecret(secret: "")), + credentials: ConfidenceClientCredentials.clientSecret(secret: ""), timeoutIntervalForRequest: 10), session: MockedClientURLProtocol.mockedSession(), metadata: ConfidenceMetadata(name: "", version: "")) @@ -59,7 +59,7 @@ class RemoteConfidenceClientTest: XCTestCase { MockedClientURLProtocol.mockedOperation = .malformedResponse let client = RemoteConfidenceClient( options: ConfidenceClientOptions( - credentials: ConfidenceClientCredentials.clientSecret(secret: "")), + credentials: ConfidenceClientCredentials.clientSecret(secret: ""), timeoutIntervalForRequest: 10), session: MockedClientURLProtocol.mockedSession(), metadata: ConfidenceMetadata(name: "", version: "")) diff --git a/Tests/ConfidenceTests/RemoteResolveConfidenceClientTest.swift b/Tests/ConfidenceTests/RemoteResolveConfidenceClientTest.swift index 16519b27..c2d310ae 100644 --- a/Tests/ConfidenceTests/RemoteResolveConfidenceClientTest.swift +++ b/Tests/ConfidenceTests/RemoteResolveConfidenceClientTest.swift @@ -26,7 +26,7 @@ class RemoteResolveConfidenceClientTest: XCTestCase { let session = MockedResolveClientURLProtocol.mockedSession(flags: flags) let client = RemoteConfidenceResolveClient( - options: .init(credentials: .clientSecret(secret: "test")), + options: .init(credentials: .clientSecret(secret: "test"), timeoutIntervalForRequest: 10), session: session, applyOnResolve: true, metadata: ConfidenceMetadata(name: "", version: "") diff --git a/api/Confidence_public_api.json b/api/Confidence_public_api.json index 98200bc9..be9aaed5 100644 --- a/api/Confidence_public_api.json +++ b/api/Confidence_public_api.json @@ -79,6 +79,10 @@ "name": "withRegion(region:)", "declaration": "public func withRegion(region: ConfidenceRegion) -> Builder" }, + { + "name": "withTimeout(timeout:)", + "declaration": "public func withTimeout(timeout: Double) -> Builder" + }, { "name": "build()", "declaration": "public func build() -> Confidence"