Skip to content

Commit

Permalink
feat: Split OFREP and GO Feature Flag provider (#7)
Browse files Browse the repository at this point in the history
Signed-off-by: Thomas Poignant <[email protected]>
  • Loading branch information
thomaspoignant authored Jul 7, 2024
1 parent 560ce65 commit 9615bfd
Show file tree
Hide file tree
Showing 19 changed files with 1,070 additions and 791 deletions.
20 changes: 18 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,40 @@ let package = Package(
products: [
.library(
name: "GOFeatureFlag",
targets: ["GOFeatureFlag"])
targets: ["GOFeatureFlag"]),
.library(
name: "OFREP",
targets: ["OFREP"])
],
dependencies: [
.package(url: "https://github.com/open-feature/swift-sdk.git", from: "0.1.0")
],
targets: [
.target(
name: "GOFeatureFlag",
name: "OFREP",
dependencies: [
.product(name: "OpenFeature", package: "swift-sdk")
],
plugins:[]
),
.target(
name: "GOFeatureFlag",
dependencies: [
"OFREP"
],
plugins:[]
),
.testTarget(
name: "GOFeatureFlagTests",
dependencies: [
"GOFeatureFlag"
]
),
.testTarget(
name: "OFREPTests",
dependencies: [
"OFREP"
]
)
]
)
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import Foundation
import OFREP

public struct GoFeatureFlagProviderOptions {
public let endpoint: String
public var pollInterval: TimeInterval
public var networkService: NetworkingService?
public var apiKey: String?

public init(
endpoint: String,
pollInterval: TimeInterval = 30,
apiKey: String?,
networkService: NetworkingService? = URLSession.shared) {
self.endpoint = endpoint
self.pollInterval = pollInterval
self.apiKey = apiKey
self.networkService = networkService
}
}
60 changes: 60 additions & 0 deletions Sources/GOFeatureFlag/provider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import Foundation
import OFREP
import OpenFeature
import Combine

struct Metadata: ProviderMetadata {
var name: String? = "GO Feature Flag provider"
}

public final class GoFeatureFlagProvider: FeatureProvider {
public var hooks: [any OpenFeature.Hook] = []
public var metadata: ProviderMetadata = Metadata()
private let ofrepProvider: OfrepProvider

public init(options: GoFeatureFlagProviderOptions){
var headers: [String:String] = [:]
if let apiKey = options.apiKey {
headers["Authorization"] = "Bearer \(apiKey)"
}
let ofrepOptions = OfrepProviderOptions(
endpoint: options.endpoint,
pollInterval: options.pollInterval,
headers: headers,
networkService: options.networkService
)
self.ofrepProvider = OfrepProvider(options: ofrepOptions)
}

public func initialize(initialContext: (any OpenFeature.EvaluationContext)?) {
self.ofrepProvider.initialize(initialContext: initialContext)
}

public func onContextSet(oldContext: (any OpenFeature.EvaluationContext)?, newContext: any OpenFeature.EvaluationContext) {
self.ofrepProvider.onContextSet(oldContext: oldContext, newContext: newContext)
}

public func getBooleanEvaluation(key: String, defaultValue: Bool, context: (any OpenFeature.EvaluationContext)?) throws -> OpenFeature.ProviderEvaluation<Bool> {
return try self.ofrepProvider.getBooleanEvaluation(key: key, defaultValue: defaultValue, context: context)
}

public func getStringEvaluation(key: String, defaultValue: String, context: (any OpenFeature.EvaluationContext)?) throws -> OpenFeature.ProviderEvaluation<String> {
return try self.ofrepProvider.getStringEvaluation(key: key, defaultValue: defaultValue, context: context)
}

public func getIntegerEvaluation(key: String, defaultValue: Int64, context: (any OpenFeature.EvaluationContext)?) throws -> OpenFeature.ProviderEvaluation<Int64> {
return try self.ofrepProvider.getIntegerEvaluation(key: key, defaultValue: defaultValue, context: context)
}

public func getDoubleEvaluation(key: String, defaultValue: Double, context: (any OpenFeature.EvaluationContext)?) throws -> OpenFeature.ProviderEvaluation<Double> {
return try self.ofrepProvider.getDoubleEvaluation(key: key, defaultValue: defaultValue, context: context)
}

public func getObjectEvaluation(key: String, defaultValue: OpenFeature.Value, context: (any OpenFeature.EvaluationContext)?) throws -> OpenFeature.ProviderEvaluation<OpenFeature.Value> {
return try self.ofrepProvider.getObjectEvaluation(key: key, defaultValue: defaultValue, context: context)
}

public func observe() -> AnyPublisher<OpenFeature.ProviderEvent, Never> {
return self.ofrepProvider.observe()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import OpenFeature
class OfrepAPI {
private let networkingService: NetworkingService
private var etag: String = ""
private let options: GoFeatureFlagProviderOptions
private let options: OfrepProviderOptions

init(networkingService: NetworkingService, options: GoFeatureFlagProviderOptions) {
init(networkingService: NetworkingService, options: OfrepProviderOptions) {
self.networkingService = networkingService
self.options = options
}
Expand All @@ -29,6 +29,13 @@ class OfrepAPI {
forHTTPHeaderField: "Content-Type"
)

// adding headers from options
if let headers = self.options.headers {
for (key, value) in headers {
request.setValue(key, forHTTPHeaderField: value)
}
}

if etag != "" {
request.setValue(etag, forHTTPHeaderField: "If-None-Match")
}
Expand Down
File renamed without changes.
19 changes: 19 additions & 0 deletions Sources/OFREP/model/options.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Foundation

public struct OfrepProviderOptions {
public let endpoint: String
public var pollInterval: TimeInterval
public var headers: [String:String]?
public var networkService: NetworkingService?

public init(
endpoint: String,
pollInterval: TimeInterval = 30,
headers: [String:String] = [:],
networkService: NetworkingService? = URLSession.shared) {
self.endpoint = endpoint
self.pollInterval = pollInterval
self.headers = headers
self.networkService = networkService
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@ import Foundation
import Combine

struct Metadata: ProviderMetadata {
var name: String? = "GO Feature Flag provider"
var name: String? = "OFREP provider"
}

public final class GoFeatureFlagProvider: FeatureProvider {
public class OfrepProvider: FeatureProvider {
private let eventHandler = EventHandler(ProviderEvent.notReady)
private var evaluationContext: OpenFeature.EvaluationContext?

private var options: GoFeatureFlagProviderOptions
private var options: OfrepProviderOptions
private let ofrepAPI: OfrepAPI

private var inMemoryCache: [String: OfrepEvaluationResponseFlag] = [:]
private var apiRetryAfter: Date?
private var timer: DispatchSourceTimer?

public init(options: GoFeatureFlagProviderOptions) {
self.options = options
public init(options: OfrepProviderOptions) {
self.options = options

// Define network service to use
var networkService: NetworkingService = URLSession.shared
Expand Down
13 changes: 13 additions & 0 deletions Tests/GOFeatureFlagTests/mock_networking_service.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import XCTest
import Combine
import Foundation
import OpenFeature
@testable import GOFeatureFlag

class GoFeatureFlagProviderTests: XCTestCase {
var defaultEvaluationContext: MutableContext!
var cancellables: Set<AnyCancellable> = []
func testShouldBeInFATALStatusIf401ErrorDuringInitialise() async {
print("toto")
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import Foundation
import OpenFeature
@testable import GOFeatureFlag
@testable import OFREP

class MockNetworkingService: NetworkingService {
public class MockNetworkingService: NetworkingService {
var mockData: Data?
var mockStatus: Int
var mockURLResponse: URLResponse?
var callCounter = 0

init(mockData: Data? = nil, mockStatus: Int = 200, mockURLResponse: URLResponse? = nil) {
public init(mockData: Data? = nil, mockStatus: Int = 200, mockURLResponse: URLResponse? = nil) {
self.mockData = mockData
if mockData == nil {
self.mockData = defaultResponse.data(using: .utf8)
Expand All @@ -17,7 +17,7 @@ class MockNetworkingService: NetworkingService {
self.mockStatus = mockStatus
}

func doRequest(for request: URLRequest) async throws -> (Data, URLResponse) {
public func doRequest(for request: URLRequest) async throws -> (Data, URLResponse) {
callCounter+=1
guard let jsonDictionary = try JSONSerialization.jsonObject(with: request.httpBody!, options: []) as? [String: Any] else {
throw OpenFeatureError.invalidContextError
Expand Down
Loading

0 comments on commit 9615bfd

Please sign in to comment.