Skip to content

Commit a8e7c72

Browse files
bsneedBrandon Sneed
andauthored
Modify destinations to avoid processing if they don't have settings (#36)
* Added configuration method to insert default settings. * added settings convenience init. * Modified destination plugin such that we skip execution if no settings * added destination enabled/disabled tests. * Fixed linux test suite. * return nil if destination isn't able to process event. Co-authored-by: Brandon Sneed <brandon.sneed@segment.com>
1 parent f1138bf commit a8e7c72

File tree

5 files changed

+106
-27
lines changed

5 files changed

+106
-27
lines changed

Sources/Segment/Configuration.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ public extension Configuration {
7373
return self
7474
}
7575

76+
@discardableResult
77+
func defaultSettings(_ settings: Settings) -> Configuration {
78+
values.defaultSettings = settings
79+
return self
80+
}
81+
7682
@discardableResult
7783
func autoAddSegmentDestination(_ value: Bool) -> Configuration {
7884
values.autoAddSegmentDestination = value

Sources/Segment/Settings.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ public struct Settings: Codable {
2121
])
2222
}
2323

24+
public init(writeKey: String) {
25+
integrations = try! JSON([
26+
SegmentDestination.Constants.integrationName.rawValue: [
27+
SegmentDestination.Constants.apiKey.rawValue: writeKey,
28+
SegmentDestination.Constants.apiHost.rawValue: HTTPClient.getDefaultAPIHost()
29+
]
30+
])
31+
}
32+
2433
public init(from decoder: Decoder) throws {
2534
let values = try decoder.container(keyedBy: CodingKeys.self)
2635
self.integrations = try? values.decode(JSON.self, forKey: CodingKeys.integrations)

Sources/Segment/Timeline.swift

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -209,37 +209,40 @@ extension DestinationPlugin {
209209
// This will process plugins (think destination middleware) that are tied
210210
// to this destination.
211211

212+
var result: E? = nil
213+
212214
// For destination plugins, we will always have some kind of `settings`,
213215
// and if we don't, it means this destination hasn't been setup on app.segment.com,
214216
// which in turn ALSO means that we shouldn't be sending events to it.
215-
216-
217-
// apply .before and .enrichment types first ...
218-
let beforeResult = timeline.applyPlugins(type: .before, event: incomingEvent)
219-
let enrichmentResult = timeline.applyPlugins(type: .enrichment, event: beforeResult)
220-
221-
// now we execute any overrides we may have made. basically, the idea is to take an
222-
// incoming event, like identify, and map it to whatever is appropriate for this destination.
223-
var destinationResult: E? = nil
224-
switch enrichmentResult {
225-
case let e as IdentifyEvent:
226-
destinationResult = identify(event: e) as? E
227-
case let e as TrackEvent:
228-
destinationResult = track(event: e) as? E
229-
case let e as ScreenEvent:
230-
destinationResult = screen(event: e) as? E
231-
case let e as GroupEvent:
232-
destinationResult = group(event: e) as? E
233-
case let e as AliasEvent:
234-
destinationResult = alias(event: e) as? E
235-
default:
236-
break
217+
218+
if let enabled = analytics?.settings()?.isDestinationEnabled(key: self.key), enabled == true {
219+
// apply .before and .enrichment types first ...
220+
let beforeResult = timeline.applyPlugins(type: .before, event: incomingEvent)
221+
let enrichmentResult = timeline.applyPlugins(type: .enrichment, event: beforeResult)
222+
223+
// now we execute any overrides we may have made. basically, the idea is to take an
224+
// incoming event, like identify, and map it to whatever is appropriate for this destination.
225+
var destinationResult: E? = nil
226+
switch enrichmentResult {
227+
case let e as IdentifyEvent:
228+
destinationResult = identify(event: e) as? E
229+
case let e as TrackEvent:
230+
destinationResult = track(event: e) as? E
231+
case let e as ScreenEvent:
232+
destinationResult = screen(event: e) as? E
233+
case let e as GroupEvent:
234+
destinationResult = group(event: e) as? E
235+
case let e as AliasEvent:
236+
destinationResult = alias(event: e) as? E
237+
default:
238+
break
239+
}
240+
241+
// apply .after plugins ...
242+
result = timeline.applyPlugins(type: .after, event: destinationResult)
237243
}
238244

239-
// apply .after plugins ...
240-
let afterResult = timeline.applyPlugins(type: .after, event: destinationResult)
241-
242-
return afterResult
245+
return result
243246
}
244247
}
245248

Tests/Segment-Tests/Analytics_Tests.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,59 @@ final class Analytics_Tests: XCTestCase {
4949

5050
wait(for: [expectation], timeout: 1.0)
5151
}
52+
53+
func testDestinationEnabled() {
54+
// need to clear settings for this one.
55+
UserDefaults.standard.removePersistentDomain(forName: "com.segment.storage.test")
56+
57+
let expectation = XCTestExpectation(description: "MyDestination Expectation")
58+
let myDestination = MyDestination {
59+
expectation.fulfill()
60+
}
61+
62+
var settings = Settings(writeKey: "test")
63+
if let existing = settings.integrations?.dictionaryValue {
64+
var newIntegrations = existing
65+
newIntegrations[myDestination.key] = true
66+
settings.integrations = try! JSON(newIntegrations)
67+
}
68+
let configuration = Configuration(writeKey: "test")
69+
configuration.defaultSettings(settings)
70+
let analytics = Analytics(configuration: configuration)
71+
72+
analytics.add(plugin: myDestination)
73+
74+
waitUntilStarted(analytics: analytics)
75+
76+
analytics.track(name: "testDestinationEnabled")
77+
78+
wait(for: [expectation], timeout: 1.0)
79+
}
80+
81+
// Linux doesn't support XCTExpectFailure
82+
#if !os(Linux)
83+
func testDestinationNotEnabled() {
84+
// need to clear settings for this one.
85+
UserDefaults.standard.removePersistentDomain(forName: "com.segment.storage.test")
86+
87+
let expectation = XCTestExpectation(description: "MyDestination Expectation")
88+
let myDestination = MyDestination {
89+
expectation.fulfill()
90+
}
91+
92+
let configuration = Configuration(writeKey: "test")
93+
let analytics = Analytics(configuration: configuration)
94+
95+
analytics.add(plugin: myDestination)
96+
97+
waitUntilStarted(analytics: analytics)
98+
99+
analytics.track(name: "testDestinationEnabled")
100+
101+
XCTExpectFailure()
102+
wait(for: [expectation], timeout: 1.0)
103+
}
104+
#endif
52105

53106
func testAnonymousId() {
54107
let analytics = Analytics(configuration: Configuration(writeKey: "test"))

Tests/Segment-Tests/Support/TestUtilities.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,17 +73,25 @@ class MyDestination: DestinationPlugin {
7373
let type: PluginType
7474
let key: String
7575
var analytics: Analytics?
76+
let trackCompletion: (() -> Void)?
7677

77-
init() {
78+
init(trackCompletion: (() -> Void)? = nil) {
7879
self.key = "MyDestination"
7980
self.type = .destination
8081
self.timeline = Timeline()
82+
self.trackCompletion = trackCompletion
8183
}
8284

8385
func update(settings: Settings) {
8486
//
8587
}
8688

89+
func track(event: TrackEvent) -> TrackEvent? {
90+
if let completion = trackCompletion {
91+
completion()
92+
}
93+
return event
94+
}
8795
}
8896

8997
class OutputReaderPlugin: Plugin {

0 commit comments

Comments
 (0)