Skip to content

Commit

Permalink
Add emitter configuration support to remote configuration (close #782)
Browse files Browse the repository at this point in the history
  • Loading branch information
matus-tomlein authored and greg-el committed Sep 28, 2023
1 parent 20abd8f commit e120ba4
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 3 deletions.
17 changes: 16 additions & 1 deletion Sources/Snowplow/Configurations/ConfigurationBundle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public class ConfigurationBundle: SerializableConfiguration {
public var subjectConfiguration: SubjectConfiguration?
@objc
public var sessionConfiguration: SessionConfiguration?
@objc
public var emitterConfiguration: EmitterConfiguration?

@objc
public var configurations: [ConfigurationProtocol] {
Expand All @@ -42,6 +44,9 @@ public class ConfigurationBundle: SerializableConfiguration {
if let sessionConfiguration = sessionConfiguration {
array.append(sessionConfiguration)
}
if let emitterConfiguration = emitterConfiguration {
array.append(emitterConfiguration)
}
return array
}

Expand Down Expand Up @@ -73,9 +78,12 @@ public class ConfigurationBundle: SerializableConfiguration {
if let config = dictionary["subjectConfiguration"] as? [String : Any] {
subjectConfiguration = SubjectConfiguration(dictionary: config)
}
if let config = dictionary["sessionConfiguration"] as? [String: Any] {
if let config = dictionary["sessionConfiguration"] as? [String : Any] {
sessionConfiguration = SessionConfiguration(dictionary: config)
}
if let config = dictionary["emitterConfiguration"] as? [String : Any] {
emitterConfiguration = EmitterConfiguration(dictionary: config)
}
}

func updateSourceConfig(_ sourceBundle: ConfigurationBundle) {
Expand All @@ -95,6 +103,10 @@ public class ConfigurationBundle: SerializableConfiguration {
if sessionConfiguration == nil { sessionConfiguration = SessionConfiguration() }
sessionConfiguration?.sourceConfig = sourceSessionConfig
}
if let sourceEmitterConfig = sourceBundle.emitterConfiguration {
if emitterConfiguration == nil { emitterConfiguration = EmitterConfiguration() }
emitterConfiguration?.sourceConfig = sourceEmitterConfig
}
}

// MARK: - NSCopying
Expand All @@ -106,6 +118,7 @@ public class ConfigurationBundle: SerializableConfiguration {
copy.trackerConfiguration = trackerConfiguration?.copy(with: zone) as? TrackerConfiguration
copy.subjectConfiguration = subjectConfiguration?.copy(with: zone) as? SubjectConfiguration
copy.sessionConfiguration = sessionConfiguration?.copy(with: zone) as? SessionConfiguration
copy.emitterConfiguration = emitterConfiguration?.copy(with: zone) as? EmitterConfiguration
return copy
}

Expand All @@ -121,6 +134,7 @@ public class ConfigurationBundle: SerializableConfiguration {
coder.encode(trackerConfiguration, forKey: "trackerConfiguration")
coder.encode(subjectConfiguration, forKey: "subjectConfiguration")
coder.encode(sessionConfiguration, forKey: "sessionConfiguration")
coder.encode(emitterConfiguration, forKey: "emitterConfiguration")
}

required init?(coder: NSCoder) {
Expand All @@ -133,5 +147,6 @@ public class ConfigurationBundle: SerializableConfiguration {
trackerConfiguration = coder.decodeObject(forKey: "trackerConfiguration") as? TrackerConfiguration
subjectConfiguration = coder.decodeObject(forKey: "subjectConfiguration") as? SubjectConfiguration
sessionConfiguration = coder.decodeObject(forKey: "sessionConfiguration") as? SessionConfiguration
emitterConfiguration = coder.decodeObject(forKey: "emitterConfiguration") as? EmitterConfiguration
}
}
71 changes: 70 additions & 1 deletion Sources/Snowplow/Configurations/EmitterConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public protocol EmitterConfigurationProtocol: AnyObject {
/// The EmitterConfiguration can be used to setup details about how the tracker should treat the events
/// to emit to the collector.
@objc(SPEmitterConfiguration)
public class EmitterConfiguration: NSObject, EmitterConfigurationProtocol, ConfigurationProtocol {
public class EmitterConfiguration: SerializableConfiguration, EmitterConfigurationProtocol, ConfigurationProtocol {
private var _bufferOption: BufferOption?
/// Sets whether the buffer should send events instantly or after the buffer
/// has reached it's limit. By default, this is set to BufferOption Default.
Expand Down Expand Up @@ -147,6 +147,27 @@ public class EmitterConfiguration: NSObject, EmitterConfigurationProtocol, Confi
super.init()
}

@objc
internal convenience init?(dictionary: [String : Any]) {
self.init()
if let bufferOption = dictionary["bufferOption"] as? String {
self._bufferOption = BufferOption.fromString(value: bufferOption)
}
self._emitRange = dictionary["emitRange"] as? Int
self._threadPoolSize = dictionary["threadPoolSize"] as? Int
self._byteLimitGet = dictionary["byteLimitGet"] as? Int
self._byteLimitPost = dictionary["byteLimitPost"] as? Int
if let retryCodes = dictionary["customRetryForStatusCodes"] as? [String : Bool] {
self._customRetryForStatusCodes = Dictionary(
uniqueKeysWithValues: retryCodes
.filter { Int($0.key) != nil }
.map { (Int($0.key)!, $0.value) }
)
}
self._serverAnonymisation = dictionary["serverAnonymisation"] as? Bool
}


// MARK: - Builders

/// Sets whether the buffer should send events instantly or after the buffer
Expand Down Expand Up @@ -214,4 +235,52 @@ public class EmitterConfiguration: NSObject, EmitterConfigurationProtocol, Confi
self.eventStore = eventStore
return self
}

// MARK: - NSCopying

@objc
override public func copy(with zone: NSZone? = nil) -> Any {
let copy = EmitterConfiguration()
copy.bufferOption = bufferOption
copy.emitRange = emitRange
copy.threadPoolSize = threadPoolSize
copy.byteLimitGet = byteLimitGet
copy.byteLimitPost = byteLimitPost
copy.requestCallback = requestCallback
copy.customRetryForStatusCodes = customRetryForStatusCodes
copy.serverAnonymisation = serverAnonymisation
copy.eventStore = eventStore
return copy
}

// MARK: - NSSecureCoding

@objc
public override class var supportsSecureCoding: Bool { return true }

@objc
public override func encode(with coder: NSCoder) {
coder.encode(bufferOption.rawValue, forKey: "bufferOption")
coder.encode(emitRange, forKey: "emitRange")
coder.encode(threadPoolSize, forKey: "threadPoolSize")
coder.encode(byteLimitGet, forKey: "byteLimitGet")
coder.encode(byteLimitPost, forKey: "byteLimitPost")
coder.encode(customRetryForStatusCodes, forKey: "customRetryForStatusCodes")
coder.encode(serverAnonymisation, forKey: "serverAnonymisation")
}

required init?(coder: NSCoder) {
super.init()
if let bufferOption = BufferOption(rawValue: coder.decodeInteger(forKey: "bufferOption")) {
self.bufferOption = bufferOption
}
emitRange = coder.decodeInteger(forKey: "emitRange")
threadPoolSize = coder.decodeInteger(forKey: "threadPoolSize")
byteLimitGet = coder.decodeInteger(forKey: "byteLimitGet")
byteLimitPost = coder.decodeInteger(forKey: "byteLimitPost")
if let retryCodes = coder.decodeObject(forKey: "customRetryForStatusCodes") as? [Int: Bool] {
customRetryForStatusCodes = retryCodes
}
serverAnonymisation = coder.decodeBool(forKey: "serverAnonymisation")
}
}
15 changes: 15 additions & 0 deletions Sources/Snowplow/Emitter/BufferOption.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,18 @@ public enum BufferOption : Int {
/// need to be sent. All GET requests will still emit one at a time.
case largeGroup = 25
}

extension BufferOption {
static func fromString(value: String) -> BufferOption? {
switch value {
case "Single":
return .single
case "DefaultGroup":
return .defaultGroup
case "HeavyGroup":
return .largeGroup
default:
return nil
}
}
}
29 changes: 28 additions & 1 deletion Tests/Configurations/TestRemoteConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ class TestRemoteConfiguration: XCTestCase {
{"namespace": "default1",\
"networkConfiguration": {"endpoint":"https://fake.snowplow.io","method":"get"},\
"trackerConfiguration": {"applicationContext":false,"screenContext":false,},\
"sessionConfiguration": {"backgroundTimeout":60,"foregroundTimeout":60}\
"sessionConfiguration": {"backgroundTimeout":60,"foregroundTimeout":60},\
"emitterConfiguration": {"serverAnonymisation":true,"customRetryForStatusCodes":{"500":true}}\
},\
{"namespace": "default2",\
"subjectConfiguration": {"userId":"testUserId"}\
Expand Down Expand Up @@ -59,6 +60,9 @@ class TestRemoteConfiguration: XCTestCase {
XCTAssertFalse(trackerConfiguration.applicationContext)
let sessionConfiguration = configurationBundle.sessionConfiguration
XCTAssertEqual(60, sessionConfiguration?.backgroundTimeoutInSeconds)
let emitterConfiguration = configurationBundle.emitterConfiguration
XCTAssertTrue(emitterConfiguration?.serverAnonymisation ?? false)
XCTAssertEqual([500: true], emitterConfiguration?.customRetryForStatusCodes)

// Regular setup without NetworkConfiguration
configurationBundle = fetchedConfigurationBundle.configurationBundle[1]
Expand Down Expand Up @@ -117,6 +121,29 @@ class TestRemoteConfiguration: XCTestCase {
XCTAssertEqual(expectedBundle.networkConfiguration?.endpoint, configBundle?.networkConfiguration?.endpoint)
XCTAssertNil(configBundle?.trackerConfiguration)
}

func testCacheEmitterConfiguration() {
let bundle = ConfigurationBundle(namespace: "namespace",
networkConfiguration: NetworkConfiguration(endpoint: "endpoint"))
bundle.emitterConfiguration = EmitterConfiguration()
.serverAnonymisation(true)
.customRetryForStatusCodes([500: true])
let remoteBundle = RemoteConfigurationBundle(schema: "", configurationVersion: 1)
remoteBundle.configurationBundle = [bundle]
let remoteConfig = RemoteConfiguration(endpoint: generateRemoteConfigEndpoint(), method: .get)
var cache = RemoteConfigurationCache(remoteConfiguration: remoteConfig)
cache.clear()
cache.write(remoteBundle)

Thread.sleep(forTimeInterval: 1) // wait the config is written on cache.

cache = RemoteConfigurationCache(remoteConfiguration: remoteConfig)
let config = cache.read()

let configBundle = config?.configurationBundle[0]
XCTAssertTrue(configBundle?.emitterConfiguration?.serverAnonymisation ?? false)
XCTAssertEqual([500: true], configBundle?.emitterConfiguration?.customRetryForStatusCodes)
}
#endif

func testProvider_notDownloading_fails() {
Expand Down

0 comments on commit e120ba4

Please sign in to comment.