Skip to content

Commit

Permalink
feat: poc: Telemetry architecture
Browse files Browse the repository at this point in the history
  • Loading branch information
fabriziodemaria committed Nov 5, 2024
1 parent 58c3a71 commit f26082e
Show file tree
Hide file tree
Showing 10 changed files with 74 additions and 6 deletions.
4 changes: 3 additions & 1 deletion Sources/Confidence/Apply/FlagApplierWithRetries.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ final class FlagApplierWithRetries: FlagApplier {
private let cacheDataInteractor: CacheDataActor
private let metadata: ConfidenceMetadata
private let debugLogger: DebugLogger?
private let telemetry = Telemetry.shared

init(
httpClient: HttpClient,
Expand Down Expand Up @@ -139,7 +140,8 @@ final class FlagApplierWithRetries: FlagApplier {
request: ApplyFlagsRequest
) async -> ApplyFlagResult {
do {
return try await httpClient.post(path: ":apply", data: request)
let header = TelemetryPayload(staleAccess: telemetry.getStaleAccessAndReset())
return try await httpClient.post(path: ":apply", data: request, header: header)
} catch {
return .failure(handleError(error: error))
}
Expand Down
3 changes: 2 additions & 1 deletion Sources/Confidence/Confidence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ public class Confidence: ConfidenceEventSender {
return self.cache.evaluate(
flagName: key,
defaultValue: defaultValue,
context: getContext(),
// TMP - TESTING (force a different context, causing STALE)
context: ["test":ConfidenceValue(null: ())],

Check failure on line 157 in Sources/Confidence/Confidence.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Colon Spacing Violation: Colons should be next to the identifier when specifying a type and next to the key in dictionary literals (colon)
flagApplier: flagApplier
)
}
Expand Down
1 change: 1 addition & 0 deletions Sources/Confidence/FlagEvaluation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ extension FlagResolution {
var resolveReason: ResolveReason = .match
if self.context != context {
resolveReason = .stale
Telemetry.shared.incrementStaleAccess()
}
return Evaluation(
value: pathValue,
Expand Down
1 change: 1 addition & 0 deletions Sources/Confidence/Http/HttpClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ typealias HttpClientResult<T> = Result<HttpClientResponse<T>, Error>

internal protocol HttpClient {
func post<T: Decodable>(path: String, data: Encodable) async throws -> HttpClientResult<T>
func post<T: Decodable>(path: String, data: Encodable, header: Encodable) async throws -> HttpClientResult<T>
}

struct HttpClientResponse<T> {
Expand Down
24 changes: 23 additions & 1 deletion Sources/Confidence/Http/NetworkClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,22 @@ final class NetworkClient: HttpClient {
self.timeoutIntervalForRequests = timeoutIntervalForRequests
}

func post<T>(path: String, data: any Encodable, header: any Encodable) async throws -> HttpClientResult<T> where T : Decodable {

Check failure on line 32 in Sources/Confidence/Http/NetworkClient.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Colon Spacing Violation: Colons should be next to the identifier when specifying a type and next to the key in dictionary literals (colon)
let request = try buildRequest(path: path, data: data, header: header)
return try await post(request: request)
}

public func post<T: Decodable>(
path: String,
data: Encodable
) async throws -> HttpClientResult<T> {
let request = try buildRequest(path: path, data: data)
return try await post(request: request)
}

private func post<T: Decodable>(
request: URLRequest
) async throws -> HttpClientResult<T> {
let requestResult = await perform(request: request, retry: self.retry)
if let error = requestResult.error {
return .failure(error)
Expand Down Expand Up @@ -96,7 +107,7 @@ extension NetworkClient {
return URL(string: "\(normalisedBase)\(normalisedPath)")
}

private func buildRequest(path: String, data: Encodable) throws -> URLRequest {
private func buildRequest(path: String, data: Encodable, header: Encodable? = nil) throws -> URLRequest {
guard let url = constructURL(base: baseUrl, path: path) else {
throw ConfidenceError.internalError(message: "Could not create service url")
}
Expand All @@ -107,9 +118,20 @@ extension NetworkClient {
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")


let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601

if let header = header {
let jsonHeaderData = try encoder.encode(header)

if let headerJsonString = String(data: jsonHeaderData, encoding: .utf8) {
request.addValue(headerJsonString, forHTTPHeaderField: "Confidence-Metadata")
}
}
// TMP - TESTING
print(">> \(request.allHTTPHeaderFields)")

let jsonData = try encoder.encode(data)
request.httpBody = jsonData

Expand Down
4 changes: 3 additions & 1 deletion Sources/Confidence/RemoteConfidenceClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public class RemoteConfidenceClient: ConfidenceClient {
private var httpClient: HttpClient
private var baseUrl: String
private let debugLogger: DebugLogger?
private let telemetry = Telemetry.shared

init(
options: ConfidenceClientOptions,
Expand Down Expand Up @@ -44,9 +45,10 @@ public class RemoteConfidenceClient: ConfidenceClient {
sendTime: timeString,
sdk: Sdk(id: metadata.name, version: metadata.version)
)
let header = TelemetryPayload(staleAccess: telemetry.getStaleAccessAndReset())
do {
let result: HttpClientResult<PublishEventResponse> =
try await self.httpClient.post(path: ":publish", data: request)
try await self.httpClient.post(path: ":publish", data: request, header: header)
switch result {
case .success(let successData):
let status = successData.response.statusCode
Expand Down
5 changes: 3 additions & 2 deletions Sources/Confidence/RemoteResolveConfidenceClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ class RemoteConfidenceResolveClient: ConfidenceResolveClient {
private let targetingKey = "targeting_key"
private var options: ConfidenceClientOptions
private let metadata: ConfidenceMetadata
private let telemetry = Telemetry.shared

private var httpClient: HttpClient
private var applyOnResolve: Bool
Expand Down Expand Up @@ -33,10 +34,10 @@ class RemoteConfidenceResolveClient: ConfidenceResolveClient {
apply: applyOnResolve,
sdk: Sdk(id: metadata.name, version: metadata.version)
)

let header = TelemetryPayload(staleAccess: telemetry.getStaleAccessAndReset())
do {
let result: HttpClientResult<ResolveFlagsResponse> =
try await self.httpClient.post(path: ":resolve", data: request)
try await self.httpClient.post(path: ":resolve", data: request, header: header)
switch result {
case .success(let successData):
guard successData.response.status == .ok else {
Expand Down
29 changes: 29 additions & 0 deletions Sources/Confidence/Telemetry/TelemetryManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Foundation

protocol TelemetryManager {
func incrementStaleAccess()
func getStaleAccessAndReset() -> Int
}

class Telemetry: TelemetryManager {
private let queue = DispatchQueue(label: "com.confidence.telemetry_manager")
private var staleAccessCounter = 0;

Check failure on line 10 in Sources/Confidence/Telemetry/TelemetryManager.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Trailing Semicolon Violation: Lines should not have trailing semicolons (trailing_semicolon)

public init() {}

static public let shared: TelemetryManager = Telemetry()

public func incrementStaleAccess() {
queue.sync {
staleAccessCounter += 1
}
}

public func getStaleAccessAndReset() -> Int {
return queue.sync {
let currentCounter = staleAccessCounter
staleAccessCounter = 0;

Check failure on line 25 in Sources/Confidence/Telemetry/TelemetryManager.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Trailing Semicolon Violation: Lines should not have trailing semicolons (trailing_semicolon)
return currentCounter
}
}
}
5 changes: 5 additions & 0 deletions Sources/Confidence/Telemetry/TelemetryPayload.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Foundation

struct TelemetryPayload: Encodable {
var staleAccess = 0
}
4 changes: 4 additions & 0 deletions Tests/ConfidenceTests/Helpers/HttpClientMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ final class HttpClientMock: HttpClient {
try handlePost(path: path, data: data)
}

func post<T>(path: String, data: any Encodable, header: any Encodable) async throws -> HttpClientResult<T> where T : Decodable {

Check failure on line 26 in Tests/ConfidenceTests/Helpers/HttpClientMock.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Colon Spacing Violation: Colons should be next to the identifier when specifying a type and next to the key in dictionary literals (colon)
try handlePost(path: path, data: data)
}

private func handlePost<T>(
path: String, data: Encodable
) throws -> HttpClientResult<T> where T: Decodable {
Expand Down

0 comments on commit f26082e

Please sign in to comment.