diff --git a/Sources/Segment/Configuration.swift b/Sources/Segment/Configuration.swift index f9866a61..75a802b5 100644 --- a/Sources/Segment/Configuration.swift +++ b/Sources/Segment/Configuration.swift @@ -73,6 +73,12 @@ public extension Configuration { return self } + @discardableResult + func defaultSettings(_ settings: Settings) -> Configuration { + values.defaultSettings = settings + return self + } + @discardableResult func autoAddSegmentDestination(_ value: Bool) -> Configuration { values.autoAddSegmentDestination = value diff --git a/Sources/Segment/Settings.swift b/Sources/Segment/Settings.swift index ef22470b..1eb754ce 100644 --- a/Sources/Segment/Settings.swift +++ b/Sources/Segment/Settings.swift @@ -21,6 +21,15 @@ public struct Settings: Codable { ]) } + public init(writeKey: String) { + integrations = try! JSON([ + SegmentDestination.Constants.integrationName.rawValue: [ + SegmentDestination.Constants.apiKey.rawValue: writeKey, + SegmentDestination.Constants.apiHost.rawValue: HTTPClient.getDefaultAPIHost() + ] + ]) + } + public init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) self.integrations = try? values.decode(JSON.self, forKey: CodingKeys.integrations) diff --git a/Sources/Segment/Timeline.swift b/Sources/Segment/Timeline.swift index 02239cb6..7f84ad28 100644 --- a/Sources/Segment/Timeline.swift +++ b/Sources/Segment/Timeline.swift @@ -209,37 +209,40 @@ extension DestinationPlugin { // This will process plugins (think destination middleware) that are tied // to this destination. + var result: E? = nil + // For destination plugins, we will always have some kind of `settings`, // and if we don't, it means this destination hasn't been setup on app.segment.com, // which in turn ALSO means that we shouldn't be sending events to it. - - - // apply .before and .enrichment types first ... - let beforeResult = timeline.applyPlugins(type: .before, event: incomingEvent) - let enrichmentResult = timeline.applyPlugins(type: .enrichment, event: beforeResult) - - // now we execute any overrides we may have made. basically, the idea is to take an - // incoming event, like identify, and map it to whatever is appropriate for this destination. - var destinationResult: E? = nil - switch enrichmentResult { - case let e as IdentifyEvent: - destinationResult = identify(event: e) as? E - case let e as TrackEvent: - destinationResult = track(event: e) as? E - case let e as ScreenEvent: - destinationResult = screen(event: e) as? E - case let e as GroupEvent: - destinationResult = group(event: e) as? E - case let e as AliasEvent: - destinationResult = alias(event: e) as? E - default: - break + + if let enabled = analytics?.settings()?.isDestinationEnabled(key: self.key), enabled == true { + // apply .before and .enrichment types first ... + let beforeResult = timeline.applyPlugins(type: .before, event: incomingEvent) + let enrichmentResult = timeline.applyPlugins(type: .enrichment, event: beforeResult) + + // now we execute any overrides we may have made. basically, the idea is to take an + // incoming event, like identify, and map it to whatever is appropriate for this destination. + var destinationResult: E? = nil + switch enrichmentResult { + case let e as IdentifyEvent: + destinationResult = identify(event: e) as? E + case let e as TrackEvent: + destinationResult = track(event: e) as? E + case let e as ScreenEvent: + destinationResult = screen(event: e) as? E + case let e as GroupEvent: + destinationResult = group(event: e) as? E + case let e as AliasEvent: + destinationResult = alias(event: e) as? E + default: + break + } + + // apply .after plugins ... + result = timeline.applyPlugins(type: .after, event: destinationResult) } - // apply .after plugins ... - let afterResult = timeline.applyPlugins(type: .after, event: destinationResult) - - return afterResult + return result } } diff --git a/Tests/Segment-Tests/Analytics_Tests.swift b/Tests/Segment-Tests/Analytics_Tests.swift index 7d3e3a7d..59250b3a 100644 --- a/Tests/Segment-Tests/Analytics_Tests.swift +++ b/Tests/Segment-Tests/Analytics_Tests.swift @@ -49,6 +49,59 @@ final class Analytics_Tests: XCTestCase { wait(for: [expectation], timeout: 1.0) } + + func testDestinationEnabled() { + // need to clear settings for this one. + UserDefaults.standard.removePersistentDomain(forName: "com.segment.storage.test") + + let expectation = XCTestExpectation(description: "MyDestination Expectation") + let myDestination = MyDestination { + expectation.fulfill() + } + + var settings = Settings(writeKey: "test") + if let existing = settings.integrations?.dictionaryValue { + var newIntegrations = existing + newIntegrations[myDestination.key] = true + settings.integrations = try! JSON(newIntegrations) + } + let configuration = Configuration(writeKey: "test") + configuration.defaultSettings(settings) + let analytics = Analytics(configuration: configuration) + + analytics.add(plugin: myDestination) + + waitUntilStarted(analytics: analytics) + + analytics.track(name: "testDestinationEnabled") + + wait(for: [expectation], timeout: 1.0) + } + + // Linux doesn't support XCTExpectFailure + #if !os(Linux) + func testDestinationNotEnabled() { + // need to clear settings for this one. + UserDefaults.standard.removePersistentDomain(forName: "com.segment.storage.test") + + let expectation = XCTestExpectation(description: "MyDestination Expectation") + let myDestination = MyDestination { + expectation.fulfill() + } + + let configuration = Configuration(writeKey: "test") + let analytics = Analytics(configuration: configuration) + + analytics.add(plugin: myDestination) + + waitUntilStarted(analytics: analytics) + + analytics.track(name: "testDestinationEnabled") + + XCTExpectFailure() + wait(for: [expectation], timeout: 1.0) + } + #endif func testAnonymousId() { let analytics = Analytics(configuration: Configuration(writeKey: "test")) diff --git a/Tests/Segment-Tests/Support/TestUtilities.swift b/Tests/Segment-Tests/Support/TestUtilities.swift index 3b3e0a24..b3731060 100644 --- a/Tests/Segment-Tests/Support/TestUtilities.swift +++ b/Tests/Segment-Tests/Support/TestUtilities.swift @@ -73,17 +73,25 @@ class MyDestination: DestinationPlugin { let type: PluginType let key: String var analytics: Analytics? + let trackCompletion: (() -> Void)? - init() { + init(trackCompletion: (() -> Void)? = nil) { self.key = "MyDestination" self.type = .destination self.timeline = Timeline() + self.trackCompletion = trackCompletion } func update(settings: Settings) { // } + func track(event: TrackEvent) -> TrackEvent? { + if let completion = trackCompletion { + completion() + } + return event + } } class OutputReaderPlugin: Plugin {