Skip to content

Commit

Permalink
Example of singular enrichment
Browse files Browse the repository at this point in the history
  • Loading branch information
bsneed committed May 21, 2024
1 parent 945bddc commit ebf0a93
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 4 deletions.
4 changes: 2 additions & 2 deletions Sources/Segment/Analytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,11 @@ public class Analytics {
Self.removeActiveWriteKey(configuration.values.writeKey)
}

internal func process<E: RawEvent>(incomingEvent: E) {
internal func process<E: RawEvent>(incomingEvent: E, enrichments: [EnrichmentClosure]? = nil) {
guard enabled == true else { return }
let event = incomingEvent.applyRawEventData(store: store)

_ = timeline.process(incomingEvent: event)
_ = timeline.process(incomingEvent: event, enrichments: enrichments)

let flushPolicies = configuration.values.flushPolicies
for policy in flushPolicies {
Expand Down
2 changes: 2 additions & 0 deletions Sources/Segment/Errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public enum AnalyticsError: Error {
case jsonUnknown(Error)

case pluginError(Error)

case enrichmentError(String)
}

extension Analytics {
Expand Down
54 changes: 54 additions & 0 deletions Sources/Segment/Events.swift
Original file line number Diff line number Diff line change
Expand Up @@ -224,3 +224,57 @@ extension Analytics {
process(incomingEvent: event)
}
}

// MARK: - Enrichment event signatures

extension Analytics {
// Tracks an event performed by a user, including some additional event properties.
/// - Parameters:
/// - name: Name of the action, e.g., 'Purchased a T-Shirt'
/// - properties: Properties specific to the named event. For example, an event with
/// the name 'Purchased a Shirt' might have properties like revenue or size.
/// - enrichments: Enrichments to be applied to this specific event only, or `nil` for none.
public func track<P: Codable>(name: String, properties: P?, enrichments: [EnrichmentClosure]?) {
do {
if let properties = properties {
let jsonProperties = try JSON(with: properties)
let event = TrackEvent(event: name, properties: jsonProperties)
process(incomingEvent: event, enrichments: enrichments)
} else {
let event = TrackEvent(event: name, properties: nil)
process(incomingEvent: event, enrichments: enrichments)
}
} catch {
reportInternalError(error, fatal: true)
}
}

/// Tracks an event performed by a user.
/// - Parameters:
/// - name: Name of the action, e.g., 'Purchased a T-Shirt'
/// - enrichments: Enrichments to be applied to this specific event only, or `nil` for none.
public func track(name: String, enrichments: [EnrichmentClosure]?) {
track(name: name, properties: nil as TrackEvent?, enrichments: enrichments)
}

/// Tracks an event performed by a user, including some additional event properties.
/// - Parameters:
/// - name: Name of the action, e.g., 'Purchased a T-Shirt'
/// - properties: A dictionary or properties specific to the named event.
/// For example, an event with the name 'Purchased a Shirt' might have properties
/// like revenue or size.
/// - enrichments: Enrichments to be applied to this specific event only, or `nil` for none.
public func track(name: String, properties: [String: Any]?, enrichments: [EnrichmentClosure]?) {
var props: JSON? = nil
if let properties = properties {
do {
props = try JSON(properties)
} catch {
reportInternalError(error, fatal: true)
}
}
let event = TrackEvent(event: name, properties: props)
process(incomingEvent: event, enrichments: enrichments)
}

}
7 changes: 7 additions & 0 deletions Sources/Segment/Plugins/Context.swift
Original file line number Diff line number Diff line change
Expand Up @@ -160,4 +160,11 @@ public class Context: PlatformPlugin {
// other stuff?? ...
}

public static func insertOrigin(event: RawEvent?, data: [String: Any]) -> RawEvent? {
guard var working = event else { return event }
if let newContext = try? working.context?.add(value: data, forKey: "__eventOrigin") {
working.context = newContext
}
return working
}
}
14 changes: 12 additions & 2 deletions Sources/Segment/Timeline.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,21 @@ public class Timeline {
}

@discardableResult
internal func process<E: RawEvent>(incomingEvent: E) -> E? {
internal func process<E: RawEvent>(incomingEvent: E, enrichments: [EnrichmentClosure]? = nil) -> E? {
// apply .before and .enrichment types first ...
let beforeResult = applyPlugins(type: .before, event: incomingEvent)
// .enrichment here is akin to source middleware in the old analytics-ios.
let enrichmentResult = applyPlugins(type: .enrichment, event: beforeResult)
var enrichmentResult = applyPlugins(type: .enrichment, event: beforeResult)

if let enrichments {
for closure in enrichments {
if let result = closure(enrichmentResult) as? E {
enrichmentResult = result
} else {
Analytics.reportInternalError(AnalyticsError.enrichmentError("The given enrichment attempted to change the event type!"))
}
}
}

// once the event enters a destination, we don't want
// to know about changes that happen there. those changes
Expand Down
18 changes: 18 additions & 0 deletions Tests/Segment-Tests/Analytics_Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,24 @@ final class Analytics_Tests: XCTestCase {
XCTAssertEqual(outputReader.lastEvent?.anonymousId, "blah-111")
XCTAssertEqual(anonIdGenerator.currentId, "blah-111")
XCTAssertEqual(outputReader.lastEvent?.anonymousId, anonIdGenerator.currentId)
}

func testSingularEnrichment() throws {
let analytics = Analytics(configuration: Configuration(writeKey: "test"))
let outputReader = OutputReaderPlugin()
analytics.add(plugin: outputReader)

waitUntilStarted(analytics: analytics)

let addEventOrigin: EnrichmentClosure = { event in
return Context.insertOrigin(event: event, data: [
"type": "mobile"
])
}

analytics.track(name: "enrichment check", enrichments: [addEventOrigin])

let trackEvent: TrackEvent? = outputReader.lastEvent as? TrackEvent
XCTAssertEqual(trackEvent?.context?.value(forKeyPath: "__eventOrigin.type"), "mobile")
}
}

0 comments on commit ebf0a93

Please sign in to comment.