Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RUMM-1791 WebEventConsumers added #683

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@
import Foundation
import WebKit

internal protocol WebRUMEventContextProviding {
var context: RUMContext? { get }
}

internal class WebRUMEventContextProvider: WebRUMEventContextProviding {
var context: RUMContext? {
// TODO: RUMM-1786 implement web event context provider
return nil
}
}
buranmert marked this conversation as resolved.
Show resolved Hide resolved

// TODO: RUMM-1794 rename the methods
public extension WKUserContentController {
func addDatadogMessageHandler(allowedWebViewHosts: Set<String>) {
Expand All @@ -16,10 +27,19 @@ public extension WKUserContentController {
internal func __addDatadogMessageHandler(allowedWebViewHosts: Set<String>, hostsSanitizer: HostsSanitizing) {
let bridgeName = DatadogMessageHandler.name

let contextProvider = WebRUMEventContextProvider()
let logEventConsumer = WebLogEventConsumer()
let rumEventConsumer = WebRUMEventConsumer(
dataWriter: RUMFeature.instance?.storage.writer,
dateCorrector: RUMFeature.instance?.dateCorrector,
webRUMEventMapper: WebRUMEventMapper(),
contextProvider: contextProvider
)

let messageHandler = DatadogMessageHandler(
eventBridge: WebEventBridge(
logEventConsumer: WebLogEventConsumer(),
rumEventConsumer: WebRUMEventConsumer()
logEventConsumer: logEventConsumer,
rumEventConsumer: rumEventConsumer
)
)
add(messageHandler, name: bridgeName)
Expand Down Expand Up @@ -73,9 +93,11 @@ private class DatadogMessageHandler: NSObject, WKScriptMessageHandler {
_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage
) {
// message.body must be called within UI thread
let messageBody = message.body
queue.async {
do {
try self.eventBridge.consume(message.body)
try self.eventBridge.consume(messageBody)
} catch {
userLogger.error("🔥 Web Event Error: \(error)")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation
internal typealias JSON = [String: Any]

internal protocol WebEventConsumer {
func consume(event: JSON, eventType: String)
func consume(event: JSON, eventType: String) throws
}

internal enum WebEventError: Error, Equatable {
Expand Down Expand Up @@ -47,9 +47,9 @@ internal class WebEventBridge {
}

if eventType == Constants.eventTypeLog {
logEventConsumer.consume(event: wrappedEvent, eventType: eventType)
try logEventConsumer.consume(event: wrappedEvent, eventType: eventType)
} else {
rumEventConsumer.consume(event: wrappedEvent, eventType: eventType)
try rumEventConsumer.consume(event: wrappedEvent, eventType: eventType)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import Foundation

internal class WebLogEventConsumer: WebEventConsumer {
func consume(event: [String: Any], eventType: String) {
// TODO: RUMM-1791 implement event consumers
func consume(event: JSON, eventType: String) throws {
// TODO: RUMM-1791 implement log event consumer
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,91 @@

import Foundation

// TODO: RUMM-1786 implement mappers
internal class WebRUMEventMapper { }

internal class WebRUMEventConsumer: WebEventConsumer {
func consume(event: [String: Any], eventType: String) {
// TODO: RUMM-1791 implement event consumers
private let dataWriter: AsyncWriter?
private let dateCorrector: DateCorrectorType?
private let webRUMEventMapper: WebRUMEventMapper?
private let contextProvider: WebRUMEventContextProviding?

init(
dataWriter: AsyncWriter?,
dateCorrector: DateCorrectorType?,
webRUMEventMapper: WebRUMEventMapper?,
contextProvider: WebRUMEventContextProviding?
) {
self.dataWriter = dataWriter
self.dateCorrector = dateCorrector
self.webRUMEventMapper = webRUMEventMapper
self.contextProvider = contextProvider
}

func consume(event: JSON, eventType: String) throws {
let eventData = try JSONSerialization.data(withJSONObject: event, options: [])
let jsonDecoder = JSONDecoder()
let rumContext = contextProvider?.context

switch eventType {
case "view":
let viewEvent = try jsonDecoder.decode(RUMViewEvent.self, from: eventData)
let mappedViewEvent = mapIfNeeded(dataModel: viewEvent, context: rumContext, offset: getOffset(viewID: viewEvent.view.id))
write(mappedViewEvent)
case "action":
let actionEvent = try jsonDecoder.decode(RUMViewEvent.self, from: eventData)
let mappedActionEvent = mapIfNeeded(dataModel: actionEvent, context: rumContext, offset: getOffset(viewID: actionEvent.view.id))
write(mappedActionEvent)
case "resource":
let resourceEvent = try jsonDecoder.decode(RUMViewEvent.self, from: eventData)
let mappedResourceEvent = mapIfNeeded(dataModel: resourceEvent, context: rumContext, offset: getOffset(viewID: resourceEvent.view.id))
write(mappedResourceEvent)
case "error":
let errorEvent = try jsonDecoder.decode(RUMViewEvent.self, from: eventData)
let mappedErrorEvent = mapIfNeeded(dataModel: errorEvent, context: rumContext, offset: getOffset(viewID: errorEvent.view.id))
write(mappedErrorEvent)
case "long_task":
let longTaskEvent = try jsonDecoder.decode(RUMViewEvent.self, from: eventData)
let mappedLongTaskEvent = mapIfNeeded(dataModel: longTaskEvent, context: rumContext, offset: getOffset(viewID: longTaskEvent.view.id))
write(mappedLongTaskEvent)
default:
userLogger.error("🔥 Web RUM Event Error - Unknown event type: \(eventType)")
}
}

private func mapIfNeeded<T: RUMDataModel>(dataModel: T, context: RUMContext?, offset: Offset) -> T {
guard let context = context else {
return dataModel
}
// TODO: RUMM-1786 implement mappers
let mappedDataModel = dataModel
return mappedDataModel
}

private func write<T: RUMDataModel>(_ model: T) {
dataWriter?.write(value: model)
}

// MARK: - Time offsets

// Q: do we really need to cache `offsets`? can't we just read `dateCorrector?.currentCorrection.serverTimeOffset`?

private typealias Offset = TimeInterval
private var offsets = [String: Offset]()

private func getOffset(viewID: String) -> Offset {
var offset = offsets[viewID]
if offset == nil {
offset = dateCorrector?.currentCorrection.serverTimeOffset
offsets[viewID] = offset
}

purgeOffsets()
return offset ?? 0.0
}
maxep marked this conversation as resolved.
Show resolved Hide resolved

private func purgeOffsets() {
// TODO: RUMM-1791 keep only 3 most recent entries.
// android uses LinkedHashMap/OrderedDictionary.
}
}