Skip to content

Commit acb77c8

Browse files
MariuszWisniewskiMariusz Wisniewski
authored andcommitted
fix: use compactMap in DestinationMetadataPlugin to tolerate non-DestinationPlugin entries
Previously, a single non-conforming plugin in the destination slot caused the entire array cast to fail, silently skipping metadata enrichment for all events.
1 parent cd91a80 commit acb77c8

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

Sources/Segment/Plugins/DestinationMetadataPlugin.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class DestinationMetadataPlugin: Plugin {
2121
}
2222

2323
guard let integrationSettings = analytics?.settings() else { return event }
24-
guard let destinations = analytics?.timeline.plugins[.destination]?.plugins as? [DestinationPlugin] else { return event }
24+
guard let destinations = analytics?.timeline.plugins[.destination]?.plugins.compactMap({ $0 as? DestinationPlugin }), !destinations.isEmpty else { return event }
2525

2626
// Mark all loaded and enabled destinations as bundled
2727
var bundled: Set<String> = []

Tests/Segment-Tests/Analytics_Tests.swift

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,57 @@ final class Analytics_Tests: XCTestCase {
706706
XCTAssertEqual(metadata?.unbundled.sorted(), ["Amplitude", "Customer.io", "dest1"])
707707
}
708708

709+
// A plugin that has .destination type but does NOT conform to DestinationPlugin.
710+
// This simulates the scenario where a non-DestinationPlugin ends up in the
711+
// destination mediator, which previously caused the entire metadata enrichment to fail.
712+
class NonConformingDestinationPlugin: Plugin {
713+
let type: PluginType = .destination
714+
weak var analytics: Analytics?
715+
}
716+
717+
// Test that DestinationMetadataPlugin still works when a non-DestinationPlugin
718+
// plugin with .destination type is present in the destination mediator.
719+
func testDestinationMetadataWithNonDestinationPluginType() {
720+
let analytics = Analytics(configuration: Configuration(writeKey: "test"))
721+
let mixpanel = AnyDestination(key: "Mixpanel")
722+
let nonConforming = NonConformingDestinationPlugin()
723+
let outputReader = OutputReaderPlugin()
724+
725+
// we want the output reader on the segment plugin
726+
// cuz that's the only place the metadata is getting added.
727+
let segmentDest = analytics.find(pluginType: SegmentDestination.self)
728+
segmentDest?.add(plugin: outputReader)
729+
730+
analytics.add(plugin: mixpanel)
731+
analytics.add(plugin: nonConforming)
732+
733+
var settings = Settings(writeKey: "123")
734+
let integrations = try? JSON([
735+
"Segment.io": JSON([
736+
"unbundledIntegrations":
737+
[
738+
"Customer.io",
739+
"Mixpanel",
740+
"Amplitude",
741+
]
742+
]),
743+
"Mixpanel": JSON(["someKey": "someVal"]),
744+
])
745+
settings.integrations = integrations
746+
analytics.store.dispatch(action: System.UpdateSettingsAction(settings: settings))
747+
748+
waitUntilStarted(analytics: analytics)
749+
750+
analytics.track(name: "sampleEvent")
751+
752+
let trackEvent: TrackEvent? = outputReader.lastEvent as? TrackEvent
753+
let metadata = trackEvent?._metadata
754+
755+
XCTAssertNotNil(metadata, "Metadata should not be nil even with a non-DestinationPlugin in the destination slot")
756+
XCTAssertEqual(metadata?.bundled, ["Mixpanel"])
757+
XCTAssertEqual(metadata?.unbundled.sorted(), ["Amplitude", "Customer.io"])
758+
}
759+
709760
func testRequestFactory() {
710761
let config = Configuration(writeKey: "testSequential").requestFactory { request in
711762
XCTAssertEqual(request.value(forHTTPHeaderField: "Accept-Encoding"), "gzip")

0 commit comments

Comments
 (0)