Skip to content

Commit

Permalink
feat: add timeline
Browse files Browse the repository at this point in the history
  • Loading branch information
yuhao900914 committed Nov 14, 2022
1 parent c359cae commit 7cda5b2
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 17 deletions.
5 changes: 4 additions & 1 deletion Sources/Amplitude/Amplitude.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class Amplitude {
return self.configuration.storageProvider
}()
lazy var timeline: Timeline = {
return Timeline(amplitude: self)
return Timeline()
}()
lazy var logger: any Logger = {
return self.configuration.loggerProvider
Expand All @@ -20,6 +20,7 @@ public class Amplitude {
) {
self.configuration = configuration
self.instanceName = instanceName
//_ = add(LifecyclePlugin())
_ = add(plugin: ContextPlugin())
_ = add(plugin: AmplitudeDestinationPlugin())
}
Expand All @@ -29,6 +30,7 @@ public class Amplitude {
self.init(configuration: configuration)
}

@discardableResult
func track(event: BaseEvent, options: EventOptions? = nil, callback: EventCallBack? = nil) -> Amplitude {
if options != nil {
event.mergeEventOptions(eventOptions: options!)
Expand Down Expand Up @@ -74,6 +76,7 @@ public class Amplitude {
return self
}

@discardableResult
func add(plugin: Plugin) -> Amplitude {
timeline.add(plugin: plugin)
return self
Expand Down
4 changes: 2 additions & 2 deletions Sources/Amplitude/Events/BaseEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class BaseEvent: EventOptions {
deviceId: String? = nil,
timestamp: Double? = nil,
eventId: Double? = nil,
sessionId: Double,
sessionId: Double? = -1,
insertId: String? = nil,
locationLat: Double? = nil,
locationLng: Double? = nil,
Expand Down Expand Up @@ -109,7 +109,7 @@ public class BaseEvent: EventOptions {
deviceId = deviceId ?? eventOptions.deviceId
timestamp = timestamp ?? eventOptions.timestamp
eventId = eventId ?? eventOptions.eventId
sessionId = sessionId < 0 ? eventOptions.sessionId : sessionId
sessionId = (sessionId == nil || sessionId! < 0) ? eventOptions.sessionId : sessionId
insertId = insertId ?? eventOptions.insertId
locationLat = locationLat ?? eventOptions.locationLat
locationLng = locationLng ?? eventOptions.locationLng
Expand Down
4 changes: 2 additions & 2 deletions Sources/Amplitude/Events/EventOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class EventOptions {
var deviceId: String?
var timestamp: Double?
var eventId: Double?
var sessionId: Double
var sessionId: Double? = -1
var insertId: String?
var locationLat: Double?
var locationLng: Double?
Expand Down Expand Up @@ -52,7 +52,7 @@ public class EventOptions {
deviceId: String? = nil,
timestamp: Double? = nil,
eventId: Double? = nil,
sessionId: Double = -1,
sessionId: Double? = -1,
insertId: String? = nil,
locationLat: Double? = nil,
locationLng: Double? = nil,
Expand Down
56 changes: 56 additions & 0 deletions Sources/Amplitude/Mediator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// File.swift
//
//
// Created by Hao Yu on 11/9/22.
//

import Foundation

internal class Mediator {
// create an array with certain type.
internal var plugins = [Plugin]()

internal func add(plugin: Plugin) {
plugins.append(plugin)
}

internal func remove(plugin: Plugin) {
plugins.removeAll { (storedPlugin) -> Bool in
return storedPlugin === plugin
}
}

internal func execute(event: BaseEvent) -> BaseEvent? {
var result : BaseEvent? = event;
plugins.forEach { plugin in
if let r = result {
if plugin is DestinationPlugin {
_ = plugin.execute(event: r)
} else if let p = plugin as? EventPlugin {
result = p.execute(event: r)
if let rr = result {
if let identifyEvent = rr as? IdentifyEvent {
result = p.identify(event: identifyEvent)
} else if let groupIdentifyEvent = rr as? GroupIdentifyEvent {
result = p.groupIdentify(event: groupIdentifyEvent)
} else if let revenueEvent = rr as? RevenueEvent {
result = p.revenue(event: revenueEvent)
} else {
result = p.track(event: rr)
}
}
} else {
result = plugin.execute(event: event)
}
}
}
return result
}

internal func applyClosure(_ closure: (Plugin) -> Void) {
plugins.forEach { plugin in
closure(plugin)
}
}
}
46 changes: 36 additions & 10 deletions Sources/Amplitude/Timeline.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,52 @@
import Foundation

public class Timeline {
var amplitude: Amplitude
var plugins = [PluginType: [any Plugin]]()
internal let plugins: [PluginType: Mediator]

init(amplitude: Amplitude) {
self.amplitude = amplitude

init() {
self.plugins = [
PluginType.before: Mediator(),
PluginType.enrichment: Mediator(),
PluginType.destination: Mediator(),
PluginType.utility: Mediator()
]
}

func process(event: BaseEvent) {
let beforeResult = applyPlugin(pluginType: PluginType.before, event: event)
let enrichmentResult = applyPlugin(pluginType: PluginType.enrichment, event: beforeResult)
_ = applyPlugin(pluginType: PluginType.destination, event: enrichmentResult)

}

func add(plugin: Plugin) {

internal func applyPlugin(pluginType: PluginType, event: BaseEvent?) -> BaseEvent? {
var result: BaseEvent? = event
if let mediator = plugins[pluginType], let e = event {
result = mediator.execute(event: e)
}
return result

}

internal func add(plugin: Plugin) {
if let mediator = plugins[plugin.type] {
mediator.add(plugin: plugin)
}
}

func remove(plugin: Plugin) {

internal func remove(plugin: Plugin) {
// remove all plugins with this name in every category
for _plugin in plugins {
let list = _plugin.value
list.remove(plugin: plugin)
}
}

func apply(event: BaseEvent) -> BaseEvent? {
return event
internal func applyClosure(_ closure: (Plugin) -> Void) {
for plugin in plugins {
let mediator = plugin.value
mediator.applyClosure(closure)
}
}
}
15 changes: 13 additions & 2 deletions Sources/Amplitude/Types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,20 @@ public enum PluginType: String {
case observe = "Observe"
}

public protocol Plugin {
public protocol Plugin: AnyObject {
var type: PluginType { get }
var amplitude: Amplitude? { get set }
func setup(amplitude: Amplitude)
func setup(amplitude: Amplitude)
func execute(event: BaseEvent) -> BaseEvent?
}

public protocol EventPlugin: Plugin {
func track(event: BaseEvent) -> BaseEvent?
func identify(event: IdentifyEvent) -> IdentifyEvent?
func groupIdentify(event: GroupIdentifyEvent) -> GroupIdentifyEvent?
func revenue(event: RevenueEvent) -> RevenueEvent?
func flush()
}

public protocol DestinationPlugin: EventPlugin {
}
29 changes: 29 additions & 0 deletions Tests/AmplitudeTests/Supports/TestUtilities.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
@testable import Amplitude_Swift

class testEnrichmentPlugin : Plugin {
let type: PluginType
var amplitude: Amplitude?
let trackCompletion: (() -> Bool)?

init(trackCompletion: (() -> Bool)? = nil) {
self.type = PluginType.enrichment
self.trackCompletion = trackCompletion
}

func setup(amplitude: Amplitude) {
self.amplitude = amplitude;
}

func execute(event: BaseEvent) -> BaseEvent? {
var returnEvent: BaseEvent? = event
if let completion = trackCompletion {
if !completion() {
returnEvent = nil
}
}
return returnEvent
}



}
43 changes: 43 additions & 0 deletions Tests/AmplitudeTests/Timeline.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import Foundation
import XCTest

@testable import Amplitude_Swift

final class TimelineTest: XCTestCase {
private var timeline: Timeline!

func testTimeline() {
let expectation = XCTestExpectation(description: "First Plugin")
let testPlugin = testEnrichmentPlugin {
expectation.fulfill()
return true
}

let amplitude = Amplitude(configuration: Configuration(apiKey: "testApiKey"))
amplitude.add(plugin: testPlugin)
amplitude.track(event: BaseEvent(eventType: "testEvent"))

wait(for: [expectation], timeout: 1.0)
}

func testTimelineWithTwoPlugin() {
let expectation = XCTestExpectation(description: "First Plugin")
let expectation2 = XCTestExpectation(description: "Second Plugin")
let testPlugin = testEnrichmentPlugin {
expectation.fulfill()
return true
}

let testPlugin2 = testEnrichmentPlugin {
expectation2.fulfill()
return true
}

let amplitude = Amplitude(configuration: Configuration(apiKey: "testApiKey"))
amplitude.add(plugin: testPlugin)
amplitude.add(plugin: testPlugin2)
amplitude.track(event: BaseEvent(eventType: "testEvent"))

wait(for: [expectation, expectation2], timeout: 1.0)
}
}

0 comments on commit 7cda5b2

Please sign in to comment.