diff --git a/Sources/Common/Http/HttpClient.swift b/Sources/Common/Http/HttpClient.swift index f206f761..9b2f23f3 100644 --- a/Sources/Common/Http/HttpClient.swift +++ b/Sources/Common/Http/HttpClient.swift @@ -34,14 +34,12 @@ public enum HttpClientError: Error { } extension HTTPURLResponse { - public func mapStatusToError(error: HttpError?, flag: String = "unknown") -> Error { + public func mapStatusToError(error: HttpError?) -> Error { let defaultError = ConfidenceError.internalError( message: "General error: \(error?.message ?? "Unknown error")") switch self.status { - case .notFound: - return ConfidenceError.badRequest(message: flag) // TODO - case .badRequest: + case .notFound, .badRequest: return ConfidenceError.badRequest(message: error?.message ?? "") default: return defaultError diff --git a/Sources/Common/Http/NetworkClient.swift b/Sources/Common/Http/NetworkClient.swift index 4ebedd93..801891af 100644 --- a/Sources/Common/Http/NetworkClient.swift +++ b/Sources/Common/Http/NetworkClient.swift @@ -130,7 +130,6 @@ extension NetworkClient { let decoder = JSONDecoder() decoder.dateDecodingStrategy = .iso8601 if response.response.status == .ok { - // TODO: Inspect and log errors response.decodedData = try decoder.decode(T.self, from: responseData) } else { do { diff --git a/Sources/Common/Sdk.swift b/Sources/Common/Sdk.swift new file mode 100644 index 00000000..c33bc291 --- /dev/null +++ b/Sources/Common/Sdk.swift @@ -0,0 +1,11 @@ +import Foundation + +public struct Sdk: Codable { + public init(id: String?, version: String?) { + self.id = id ?? "SDK_ID_SWIFT_PROVIDER" + self.version = version ?? "unknown" + } + + var id: String + var version: String +} diff --git a/Sources/Confidence/Confidence.swift b/Sources/Confidence/Confidence.swift index 256ceab0..c6089a16 100644 --- a/Sources/Confidence/Confidence.swift +++ b/Sources/Confidence/Confidence.swift @@ -28,11 +28,13 @@ public class Confidence: ConfidenceEventSender { self.parent = parent } - // TODO: Implement actual event uploading to the backend public func send(definition: String, payload: ConfidenceStruct) { print("Sending: \"\(definition)\".\nMessage: \(payload)\nContext: \(context)") Task { - try? await client.send(definition: definition, payload: payload) + // TODO: This will be called inside the EventSenderEngine once implemented + try? await client.upload(batch: [ + ConfidenceClientEvent(definition: definition, payload: payload) + ]) } } @@ -105,6 +107,7 @@ extension Confidence { client: RemoteConfidenceClient( options: ConfidenceClientOptions( credentials: ConfidenceClientCredentials.clientSecret(secret: clientSecret), + timeout: timeout, region: region), metadata: ConfidenceMetadata( name: "SDK_ID_SWIFT_CONFIDENCE", diff --git a/Sources/Confidence/ConfidenceClient/ConfidenceClient.swift b/Sources/Confidence/ConfidenceClient/ConfidenceClient.swift index 726313a5..adac7fbb 100644 --- a/Sources/Confidence/ConfidenceClient/ConfidenceClient.swift +++ b/Sources/Confidence/ConfidenceClient/ConfidenceClient.swift @@ -1,5 +1,10 @@ import Foundation public protocol ConfidenceClient { - func send(definition: String, payload: ConfidenceStruct) async throws + func upload(batch: [ConfidenceClientEvent]) async throws +} + +public struct ConfidenceClientEvent { + var definition: String + var payload: ConfidenceStruct } diff --git a/Sources/Confidence/ConfidenceClient/RemoteConfidenceClient.swift b/Sources/Confidence/ConfidenceClient/RemoteConfidenceClient.swift index 20306d48..67f6a16f 100644 --- a/Sources/Confidence/ConfidenceClient/RemoteConfidenceClient.swift +++ b/Sources/Confidence/ConfidenceClient/RemoteConfidenceClient.swift @@ -26,16 +26,16 @@ public class RemoteConfidenceClient: ConfidenceClient { self.metadata = metadata } - public func send(definition: String, payload: ConfidenceStruct) async throws { + public func upload(batch: [ConfidenceClientEvent]) async throws { let timeString = Date.backport.nowISOString let request = PublishEventRequest( - events: [ + events: batch.map { event in Event( - eventDefinition: "eventDefinitions/\(definition)", - payload: payload, + eventDefinition: "eventDefinitions/\(event.definition)", + payload: event.payload, eventTime: timeString ) - ], + }, clientSecret: options.credentials.getSecret(), sendTime: timeString, sdk: Sdk(id: metadata.name, version: metadata.version) @@ -99,14 +99,3 @@ struct EventError: Decodable { case unknown } } - - -struct Sdk: Encodable { - init(id: String?, version: String?) { - self.id = id ?? "SDK_ID_SWIFT_PROVIDER" - self.version = version ?? "unknown" - } - - var id: String - var version: String -} diff --git a/Sources/Confidence/ConfidenceMetadata.swift b/Sources/Confidence/ConfidenceMetadata.swift index 6e492544..8a51792a 100644 --- a/Sources/Confidence/ConfidenceMetadata.swift +++ b/Sources/Confidence/ConfidenceMetadata.swift @@ -1,6 +1,6 @@ import Foundation -public struct ConfidenceMetadata { - public var name: String? = "SDK_ID_SWIFT_PROVIDER" - public var version: String? +struct ConfidenceMetadata { + public var name: String + public var version: String } diff --git a/Sources/ConfidenceProvider/Apply/FlagApplierWithRetries.swift b/Sources/ConfidenceProvider/Apply/FlagApplierWithRetries.swift index 71cf3d75..cd450c80 100644 --- a/Sources/ConfidenceProvider/Apply/FlagApplierWithRetries.swift +++ b/Sources/ConfidenceProvider/Apply/FlagApplierWithRetries.swift @@ -1,6 +1,6 @@ import Foundation -import Confidence import Common +import Confidence import OpenFeature import os diff --git a/Sources/ConfidenceProvider/Cache/DefaultStorage.swift b/Sources/ConfidenceProvider/Cache/DefaultStorage.swift index 7fc41a1c..5c5c87f0 100644 --- a/Sources/ConfidenceProvider/Cache/DefaultStorage.swift +++ b/Sources/ConfidenceProvider/Cache/DefaultStorage.swift @@ -1,6 +1,6 @@ import Foundation -import Confidence import Common +import Confidence public class DefaultStorage: Storage { private let storageQueue = DispatchQueue(label: "com.confidence.storage") diff --git a/Sources/ConfidenceProvider/Cache/InMemoryProviderCache.swift b/Sources/ConfidenceProvider/Cache/InMemoryProviderCache.swift index bd71204a..1573e5f3 100644 --- a/Sources/ConfidenceProvider/Cache/InMemoryProviderCache.swift +++ b/Sources/ConfidenceProvider/Cache/InMemoryProviderCache.swift @@ -1,8 +1,8 @@ import Foundation import Combine +import Common import Confidence import OpenFeature -import Common import os public class InMemoryProviderCache: ProviderCache { diff --git a/Sources/ConfidenceProvider/ConfidenceClient/LocalStorageResolver.swift b/Sources/ConfidenceProvider/ConfidenceClient/LocalStorageResolver.swift index 42d8df06..c3fb28aa 100644 --- a/Sources/ConfidenceProvider/ConfidenceClient/LocalStorageResolver.swift +++ b/Sources/ConfidenceProvider/ConfidenceClient/LocalStorageResolver.swift @@ -1,7 +1,7 @@ import Foundation +import Common import Confidence import OpenFeature -import Common public class LocalStorageResolver: Resolver { private var cache: ProviderCache diff --git a/Sources/ConfidenceProvider/ConfidenceClient/RemoteConfidenceClient.swift b/Sources/ConfidenceProvider/ConfidenceClient/RemoteResolveConfidenceClient.swift similarity index 89% rename from Sources/ConfidenceProvider/ConfidenceClient/RemoteConfidenceClient.swift rename to Sources/ConfidenceProvider/ConfidenceClient/RemoteResolveConfidenceClient.swift index dda1b637..3d5cc3d1 100644 --- a/Sources/ConfidenceProvider/ConfidenceClient/RemoteConfidenceClient.swift +++ b/Sources/ConfidenceProvider/ConfidenceClient/RemoteResolveConfidenceClient.swift @@ -1,9 +1,9 @@ import Foundation +import Common import Confidence import OpenFeature -import Common -public class RemoteConfidenceClient: ConfidenceResolveClient { +public class RemoteConfidenceResolveClient: ConfidenceResolveClient { private let targetingKey = "targeting_key" private let flagApplier: FlagApplier private var options: ConfidenceClientOptions @@ -11,7 +11,6 @@ public class RemoteConfidenceClient: ConfidenceResolveClient { private var httpClient: HttpClient private var applyOnResolve: Bool - private var baseUrl: String init( options: ConfidenceClientOptions, @@ -24,15 +23,9 @@ public class RemoteConfidenceClient: ConfidenceResolveClient { self.flagApplier = flagApplier self.applyOnResolve = applyOnResolve self.metadata = metadata - switch options.region { - case .global: - self.baseUrl = "https://resolver.confidence.dev/v1/flags" - case .europe: - self.baseUrl = "https://resolver.eu.confidence.dev/v1/flags" - case .usa: - self.baseUrl = "https://resolver.us.confidence.dev/v1/flags" - } - self.httpClient = NetworkClient(session: session, baseUrl: baseUrl) + self.httpClient = NetworkClient( + session: session, + baseUrl: BaseUrlMapper.from(region: options.region)) } // MARK: Resolver @@ -172,16 +165,6 @@ struct ApplyFlagsRequest: Codable { struct ApplyFlagsResponse: Codable { } -struct Sdk: Codable { - init(id: String?, version: String?) { - self.id = id ?? "SDK_ID_SWIFT_PROVIDER" - self.version = version ?? "unknown" - } - - var id: String - var version: String -} - private func displayName(resolvedFlag: ResolvedFlag) throws -> String { let flagNameComponents = resolvedFlag.flag.components(separatedBy: "/") if flagNameComponents.count <= 1 || flagNameComponents[0] != "flags" { diff --git a/Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift b/Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift index 754dc4e5..07960fce 100644 --- a/Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift +++ b/Sources/ConfidenceProvider/ConfidenceFeatureProvider.swift @@ -1,8 +1,8 @@ import Foundation -import Confidence -import OpenFeature import Combine import Common +import Confidence +import OpenFeature import os /// The implementation of the Confidence Feature Provider. This implementation allows to pre-cache evaluations. @@ -28,7 +28,7 @@ public class ConfidenceFeatureProvider: FeatureProvider { /// Should not be called externally, use `ConfidenceFeatureProvider.Builder`or init with `Confidence` instead. init( metadata: ProviderMetadata, - client: RemoteConfidenceClient, + client: RemoteConfidenceResolveClient, cache: ProviderCache, storage: Storage, overrides: [String: LocalOverride] = [:], @@ -58,21 +58,12 @@ public class ConfidenceFeatureProvider: FeatureProvider { self.cache = InMemoryProviderCache.from(storage: DefaultStorage.resolverFlagsCache()) self.storage = DefaultStorage.resolverFlagsCache() self.resolver = LocalStorageResolver(cache: cache) - let baseUrl: String - switch options.region { - case .global: - baseUrl = "https://resolver.confidence.dev/v1/flags" - case .europe: - baseUrl = "https://resolver.eu.confidence.dev/v1/flags" - case .usa: - baseUrl = "https://resolver.us.confidence.dev/v1/flags" - } self.flagApplier = FlagApplierWithRetries( - httpClient: NetworkClient(baseUrl: baseUrl), + httpClient: NetworkClient(baseUrl: BaseUrlMapper.from(region: options.region)), storage: DefaultStorage.applierFlagsCache(), options: options, metadata: metadata) - self.client = RemoteConfidenceClient( + self.client = RemoteConfidenceResolveClient( options: options, applyOnResolve: false, flagApplier: flagApplier, @@ -618,19 +609,10 @@ extension ConfidenceFeatureProvider { /// Creates the `ConfidenceFeatureProvider` according to the settings specified in the builder. public func build() -> ConfidenceFeatureProvider { - let baseUrl: String - switch options.region { - case .global: - baseUrl = "https://resolver.confidence.dev/v1/flags" - case .europe: - baseUrl = "https://resolver.eu.confidence.dev/v1/flags" - case .usa: - baseUrl = "https://resolver.us.confidence.dev/v1/flags" - } let flagApplier = flagApplier ?? FlagApplierWithRetries( - httpClient: NetworkClient(baseUrl: baseUrl), + httpClient: NetworkClient(baseUrl: BaseUrlMapper.from(region: options.region)), storage: DefaultStorage.applierFlagsCache(), options: options, metadata: metadata @@ -638,7 +620,7 @@ extension ConfidenceFeatureProvider { let cache = cache ?? InMemoryProviderCache.from(storage: storage) - let client = RemoteConfidenceClient( + let client = RemoteConfidenceResolveClient( options: options, session: self.session, applyOnResolve: false, diff --git a/Sources/ConfidenceProvider/Utils/File.swift b/Sources/ConfidenceProvider/Utils/File.swift new file mode 100644 index 00000000..1f2e332d --- /dev/null +++ b/Sources/ConfidenceProvider/Utils/File.swift @@ -0,0 +1,15 @@ +import Foundation +import Confidence + +public enum BaseUrlMapper { + static func from(region: ConfidenceRegion) -> String { + switch region { + case .global: + "https://resolver.confidence.dev/v1/flags" + case .europe: + "https://resolver.eu.confidence.dev/v1/flags" + case .usa: + "https://resolver.us.confidence.dev/v1/flags" + } + } +} diff --git a/Sources/ConfidenceProvider/Utils/HttpStatusCode+Error.swift b/Sources/ConfidenceProvider/Utils/HttpStatusCode+Error.swift index c78f28b7..bfec2a2c 100644 --- a/Sources/ConfidenceProvider/Utils/HttpStatusCode+Error.swift +++ b/Sources/ConfidenceProvider/Utils/HttpStatusCode+Error.swift @@ -1,7 +1,7 @@ import Foundation +import Common import Confidence import OpenFeature -import Common extension HTTPURLResponse { func mapStatusToError(error: HttpError?, flag: String = "unknown") -> Error { diff --git a/Tests/ConfidenceProviderTests/Helpers/AlwaysFailCache.swift b/Tests/ConfidenceProviderTests/Helpers/AlwaysFailCache.swift index 1d520831..ac51f6e6 100644 --- a/Tests/ConfidenceProviderTests/Helpers/AlwaysFailCache.swift +++ b/Tests/ConfidenceProviderTests/Helpers/AlwaysFailCache.swift @@ -1,7 +1,7 @@ import Foundation +import Common import Confidence import OpenFeature -import Common @testable import ConfidenceProvider diff --git a/Tests/ConfidenceProviderTests/Helpers/ClientMock.swift b/Tests/ConfidenceProviderTests/Helpers/ClientMock.swift index 6fdecd3d..8a4d653b 100644 --- a/Tests/ConfidenceProviderTests/Helpers/ClientMock.swift +++ b/Tests/ConfidenceProviderTests/Helpers/ClientMock.swift @@ -1,6 +1,6 @@ import Foundation -import Confidence import Common +import Confidence import OpenFeature @testable import ConfidenceProvider diff --git a/Tests/ConfidenceProviderTests/Helpers/HttpClientMock.swift b/Tests/ConfidenceProviderTests/Helpers/HttpClientMock.swift index 7e80bc99..8d9377cf 100644 --- a/Tests/ConfidenceProviderTests/Helpers/HttpClientMock.swift +++ b/Tests/ConfidenceProviderTests/Helpers/HttpClientMock.swift @@ -1,6 +1,6 @@ import Foundation -import Confidence import Common +import Confidence import XCTest final class HttpClientMock: HttpClient { diff --git a/Tests/ConfidenceProviderTests/Helpers/MockedConfidenceClientURLProtocol.swift b/Tests/ConfidenceProviderTests/Helpers/MockedConfidenceClientURLProtocol.swift index 3a8bcc76..eb620702 100644 --- a/Tests/ConfidenceProviderTests/Helpers/MockedConfidenceClientURLProtocol.swift +++ b/Tests/ConfidenceProviderTests/Helpers/MockedConfidenceClientURLProtocol.swift @@ -1,6 +1,6 @@ import Foundation -import Confidence import Common +import Confidence import OpenFeature @testable import ConfidenceProvider diff --git a/Tests/ConfidenceProviderTests/LocalStorageResolverTest.swift b/Tests/ConfidenceProviderTests/LocalStorageResolverTest.swift index 0174f93a..af9f27ef 100644 --- a/Tests/ConfidenceProviderTests/LocalStorageResolverTest.swift +++ b/Tests/ConfidenceProviderTests/LocalStorageResolverTest.swift @@ -1,7 +1,7 @@ import Foundation +import Common import Confidence import OpenFeature -import Common import XCTest @testable import ConfidenceProvider diff --git a/Tests/ConfidenceProviderTests/RemoteConfidenceClientTest.swift b/Tests/ConfidenceProviderTests/RemoteResolveConfidenceClientTest.swift similarity index 94% rename from Tests/ConfidenceProviderTests/RemoteConfidenceClientTest.swift rename to Tests/ConfidenceProviderTests/RemoteResolveConfidenceClientTest.swift index 6f9616e9..035f28bc 100644 --- a/Tests/ConfidenceProviderTests/RemoteConfidenceClientTest.swift +++ b/Tests/ConfidenceProviderTests/RemoteResolveConfidenceClientTest.swift @@ -4,7 +4,7 @@ import XCTest @testable import ConfidenceProvider -class RemoteConfidenceClientTest: XCTestCase { +class RemoteResolveConfidenceClientTest: XCTestCase { var flags: [String: MockedConfidenceClientURLProtocol.TestFlag] = [:] let resolvedFlag1 = MockedConfidenceClientURLProtocol.ResolvedTestFlag( variant: "control", value: .structure(["size": .integer(3)])) @@ -26,7 +26,7 @@ class RemoteConfidenceClientTest: XCTestCase { let session = MockedConfidenceClientURLProtocol.mockedSession(flags: flags) let flagApplier = FlagApplierMock() - let client = RemoteConfidenceClient( + let client = RemoteConfidenceResolveClient( options: .init(credentials: .clientSecret(secret: "test")), session: session, applyOnResolve: true, diff --git a/Tests/ConfidenceTests/Helpers/ConfidenceClientMock.swift b/Tests/ConfidenceTests/Helpers/ConfidenceClientMock.swift index 7ff5644a..e77de270 100644 --- a/Tests/ConfidenceTests/Helpers/ConfidenceClientMock.swift +++ b/Tests/ConfidenceTests/Helpers/ConfidenceClientMock.swift @@ -3,7 +3,7 @@ import Foundation @testable import Confidence class ConfidenceClientMock: ConfidenceClient { - func send(definition: String, payload: ConfidenceStruct) async throws { + func upload(batch: [ConfidenceClientEvent]) async throws { // NO-OP } }