Skip to content

Commit 7c3ffca

Browse files
authored
Improved debug apparatus (#27)
* Improved debug apparatus * Fixed empty wait.
1 parent 3b381b2 commit 7c3ffca

File tree

6 files changed

+140
-51
lines changed

6 files changed

+140
-51
lines changed

Sources/AnalyticsLive/Signals/Broadcasters/SegmentBroadcaster.swift

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,32 @@ import Segment
1111
public class SegmentBroadcaster: SignalBroadcaster {
1212
public weak var analytics: Analytics? = nil {
1313
didSet {
14-
#if DEBUG
15-
guard let analytics else { return }
16-
self.mini = MiniAnalytics(analytics: analytics)
17-
#endif
14+
if sendToSegment {
15+
guard let analytics else { return }
16+
self.mini = MiniAnalytics(analytics: analytics)
17+
}
1818
}
1919
}
2020

21+
internal let sendToSegment: Bool
22+
internal let obfuscate: Bool
2123
internal var mini: MiniAnalytics? = nil
2224

2325
public func added(signal: any RawSignal) {
24-
#if DEBUG
25-
mini?.track(signal: signal)
26-
#endif
26+
var s = signal
27+
if sendToSegment {
28+
mini?.track(signal: s, obfuscate: obfuscate)
29+
}
2730
}
2831

2932
public func relay() {
30-
#if DEBUG
31-
mini?.flush()
32-
#endif
33+
if sendToSegment {
34+
mini?.flush()
35+
}
3336
}
3437

35-
public init() { }
38+
public init(sendToSegment: Bool = false, obfuscate: Bool = true) {
39+
self.obfuscate = obfuscate
40+
self.sendToSegment = sendToSegment
41+
}
3642
}

Sources/AnalyticsLive/Signals/Configuration.swift

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,27 @@ public struct SignalsConfiguration {
1919
"signals.segment.build",
2020
]
2121

22-
public let writeKey: String
23-
public let maximumBufferSize: Int
24-
public let relayCount: Int
25-
public let relayInterval: TimeInterval
26-
public let broadcasters: [SignalBroadcaster]?
27-
public let useUIKitAutoSignal: Bool
28-
public let useSwiftUIAutoSignal: Bool
29-
public let useNetworkAutoSignal: Bool
30-
public let allowedNetworkHosts: [String]
31-
public let blockedNetworkHosts: [String]
22+
internal let writeKey: String
23+
internal let maximumBufferSize: Int
24+
internal let relayCount: Int
25+
internal let relayInterval: TimeInterval
26+
internal var broadcasters: [SignalBroadcaster]
27+
internal let sendDebugSignalsToSegment: Bool
28+
internal let obfuscateDebugSignals: Bool
29+
internal let useUIKitAutoSignal: Bool
30+
internal let useSwiftUIAutoSignal: Bool
31+
internal let useNetworkAutoSignal: Bool
32+
internal let allowedNetworkHosts: [String]
33+
internal let blockedNetworkHosts: [String]
3234

3335
public init(
3436
writeKey: String,
3537
maximumBufferSize: Int = 1000,
3638
relayCount: Int = 20,
3739
relayInterval: TimeInterval = 60,
38-
broadcasters: [SignalBroadcaster]? = [SegmentBroadcaster()],
40+
broadcasters: [SignalBroadcaster] = [],
41+
sendDebugSignalsToSegment: Bool = false,
42+
obfuscateDebugSignals: Bool = true,
3943
useUIKitAutoSignal: Bool = false,
4044
useSwiftUIAutoSignal: Bool = false,
4145
useNetworkAutoSignal: Bool = false,
@@ -47,20 +51,31 @@ public struct SignalsConfiguration {
4751
self.relayCount = relayCount
4852
self.relayInterval = relayInterval
4953
self.broadcasters = broadcasters
54+
self.sendDebugSignalsToSegment = sendDebugSignalsToSegment
55+
self.obfuscateDebugSignals = obfuscateDebugSignals
5056
self.useUIKitAutoSignal = useUIKitAutoSignal
5157
self.useSwiftUIAutoSignal = useSwiftUIAutoSignal
5258
self.useNetworkAutoSignal = useNetworkAutoSignal
5359
self.allowedNetworkHosts = allowedNetworkHosts
5460

61+
if !self.broadcasters.contains(where: { $0 is SegmentBroadcaster }) {
62+
if self.sendDebugSignalsToSegment {
63+
let seg = SegmentBroadcaster(
64+
sendToSegment: self.sendDebugSignalsToSegment,
65+
obfuscate: self.obfuscateDebugSignals
66+
)
67+
self.broadcasters.append(seg)
68+
}
69+
}
70+
5571
var blocked = blockedNetworkHosts + Self.autoBlockedHosts
5672
// block the webhook if it's in use
57-
if let broadcasters = self.broadcasters {
58-
for b in broadcasters {
59-
if let webhook = b as? WebhookBroadcaster, let host = webhook.webhookURL.host() {
60-
blocked.append(host)
61-
}
73+
for b in self.broadcasters {
74+
if let webhook = b as? WebhookBroadcaster, let host = webhook.webhookURL.host() {
75+
blocked.append(host)
6276
}
6377
}
78+
6479
self.blockedNetworkHosts = blocked
6580
}
6681
}

Sources/AnalyticsLive/Signals/Minilytics/MiniAnalytics.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ internal class MiniAnalytics {
6161
let storage: TransientDB
6262

6363
@Atomic var flushing: Bool = false
64+
// used for testing only.
65+
internal static var observer: ((_ in: any RawSignal, _ out: MiniTrackEvent) -> Void)? = nil
6466

6567
init(analytics: Analytics) {
6668
self.analytics = analytics
@@ -78,14 +80,13 @@ internal class MiniAnalytics {
7880
self.storage = TransientDB(store: fileStore, asyncAppend: true)
7981
}
8082

81-
func track(signal: any RawSignal) {
83+
func track(signal: any RawSignal, obfuscate: Bool) {
84+
var input = signal
8285
var signal = signal
8386

84-
#if !DEBUG
85-
if let obf = signal as? JSONObfuscation {
87+
if obfuscate, let obf = signal as? JSONObfuscation {
8688
signal = obf.obfuscated()
8789
}
88-
#endif
8990

9091
guard let props = try? JSON(with: signal) else { return }
9192

@@ -102,6 +103,10 @@ internal class MiniAnalytics {
102103
properties: props)
103104

104105
storage.append(data: track)
106+
107+
if let observer = Self.observer {
108+
observer(input, track)
109+
}
105110
}
106111

107112
func flush() {

Sources/AnalyticsLive/Signals/Signals.swift

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ public class Signals: Plugin, LivePluginsDependent {
8585
public func useConfiguration(_ configuration: SignalsConfiguration) {
8686
_configuration.set(configuration)
8787

88-
addDefaultBroadcasters()
8988
updateJSConfiguration()
9089
updateNativeConfiguration()
9190

@@ -239,23 +238,12 @@ extension Signals {
239238
}
240239
}
241240

242-
internal func addDefaultBroadcasters() {
243-
if let cb = configuration.broadcasters {
244-
broadcasters = cb
245-
}
246-
247-
if !broadcasters.contains(where: { broadcaster in
248-
return broadcaster is SegmentBroadcaster
249-
}) {
250-
broadcasters.append(SegmentBroadcaster())
251-
}
252-
}
253-
254241
internal func updateJSConfiguration() {
255242
signalObject?.setValue(configuration.maximumBufferSize, for: "maxBufferSize")
256243
}
257244

258245
internal func updateNativeConfiguration() {
246+
broadcasters = configuration.broadcasters
259247
broadcastTimer = QueueTimer(interval: configuration.relayInterval, handler: { [weak self] in
260248
guard let self else { return }
261249
for b in self.broadcasters { b.relay() }

Sources/AnalyticsLive/Signals/Utilities/Obfuscation.swift

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,6 @@ extension NetworkSignal: JSONObfuscation {
4646

4747
extension JSONObfuscation {
4848
func obfuscate(_ data: JSON?) -> JSON? {
49-
#if DEBUG
50-
let debugging = true
51-
#else
52-
let debugging = false
53-
#endif
54-
if debugging { return data }
55-
5649
guard let data else { return data }
5750

5851
switch data {
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,85 @@
11
import XCTest
2+
import Segment
23
@testable import AnalyticsLive
34

5+
final class TestSignals: XCTestCase {
6+
7+
override func setUpWithError() throws {
8+
// Put setup code here. This method is called before the invocation of each test method in the class.
9+
}
10+
11+
override func tearDownWithError() throws {
12+
// Put teardown code here. This method is called after the invocation of each test method in the class.
13+
}
14+
15+
func testSendToSegment() throws {
16+
LivePlugins.clearCache()
17+
18+
let config = Configuration(writeKey: "signals_test")
19+
.flushInterval(999999999)
20+
.flushAt(99999999)
21+
let analytics = Analytics(configuration: config)
22+
23+
let signalsConfig = SignalsConfiguration(
24+
writeKey: "signals_test",
25+
sendDebugSignalsToSegment: true
26+
)
27+
28+
// set up an observer.
29+
let expectation = self.expectation(description: "observer called")
30+
MiniAnalytics.observer = { signal, event in
31+
print("signal: \(signal.prettyPrint())")
32+
print("event: \(event.prettyPrint())")
33+
34+
XCTAssertEqual(event.properties.value(forKeyPath: "data.data.customer_name"), "XXXX XXX")
35+
XCTAssertEqual(event.properties.value(forKeyPath: "data.data.price"), "99.99")
36+
expectation.fulfill()
37+
}
38+
Signals.shared.useConfiguration(signalsConfig)
39+
analytics.add(plugin: LivePlugins(fallbackFileURL: bundleTestFile(file: "MyEdgeFunctions.js")))
40+
analytics.add(plugin: Signals.shared)
41+
42+
analytics.waitUntilStarted()
43+
44+
let localData = LocalDataSignal(action: .loaded, identifier: "1234", data: ["price": "19.95", "customer_name": "John Doe"])
45+
Signals.emit(signal: localData)
46+
47+
waitForExpectations(timeout: 5)
48+
}
49+
50+
func testSendToSegmentUnobfuscated() throws {
51+
LivePlugins.clearCache()
52+
53+
let config = Configuration(writeKey: "signals_test2")
54+
.flushInterval(999999999)
55+
.flushAt(99999999)
56+
let analytics = Analytics(configuration: config)
57+
58+
let signalsConfig = SignalsConfiguration(
59+
writeKey: "signals_test2",
60+
sendDebugSignalsToSegment: true,
61+
obfuscateDebugSignals: false
62+
)
63+
64+
// set up an observer.
65+
let expectation = self.expectation(description: "observer called")
66+
MiniAnalytics.observer = { signal, event in
67+
print("signal: \(signal.prettyPrint())")
68+
print("event: \(event.prettyPrint())")
69+
70+
XCTAssertEqual(event.properties.value(forKeyPath: "data.data.customer_name"), "John Doe")
71+
XCTAssertEqual(event.properties.value(forKeyPath: "data.data.price"), "19.95")
72+
expectation.fulfill()
73+
}
74+
Signals.shared.useConfiguration(signalsConfig)
75+
analytics.add(plugin: LivePlugins(fallbackFileURL: bundleTestFile(file: "MyEdgeFunctions.js")))
76+
analytics.add(plugin: Signals.shared)
77+
78+
analytics.waitUntilStarted()
79+
80+
let localData = LocalDataSignal(action: .loaded, identifier: "1234", data: ["price": "19.95", "customer_name": "John Doe"])
81+
Signals.emit(signal: localData)
82+
83+
waitForExpectations(timeout: 5)
84+
}
85+
}

0 commit comments

Comments
 (0)