Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions AEPCore/Sources/eventhub/EventHub.swift
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ final class EventHub {
/// - Parameters:
/// - extensionName: Extension whose `SharedState` is to be updated
/// - data: Data for the `SharedState`
/// - event: If not nil, the `SharedState` will be versioned at `event`, if nil, it will be versioned at the latest
/// - event: If not nil, the `SharedState` will be versioned at `event`, if nil the shared state is versioned zero
func createSharedState(extensionName: String, data: [String: Any]?, event: Event?) {
guard let (sharedState, version) = versionSharedState(extensionName: extensionName, event: event) else {
Log.error(label: "\(LOG_TAG):\(#function)", "Error in creating shared state.")
Expand All @@ -164,7 +164,7 @@ final class EventHub {
/// Sets the `SharedState` for the extension to pending at `event`'s version and returns a `SharedStateResolver` which is to be invoked with data for the `SharedState` once available.
/// - Parameters:
/// - extensionName: Extension whose `SharedState` is to be updated
/// - event: Event which has the `SharedState` should be versioned for
/// - event: Event which has the `SharedState` should be versioned for, if nil the shared state is versioned zero
/// - Returns: A `SharedStateResolver` which is invoked to set pending the `SharedState` versioned at `event`
func createPendingSharedState(extensionName: String, event: Event?) -> SharedStateResolver {
var pendingVersion: Int?
Expand All @@ -184,17 +184,17 @@ final class EventHub {
/// Retrieves the `SharedState` for a specific extension
/// - Parameters:
/// - extensionName: An extension name whose `SharedState` will be returned
/// - event: If not nil, will retrieve the `SharedState` that corresponds with this event's version, if nil will return the latest `SharedState`
/// - event: If not nil, will retrieve the `SharedState` that corresponds with this event's version, if nil will return the earliest `SharedState`
/// - Returns: The `SharedState` data and status for the extension with `extensionName`
func getSharedState(extensionName: String, event: Event?) -> SharedStateResult? {
guard let sharedState = registeredExtensions.first(where: { $1.sharedStateName == extensionName })?.value.sharedState else {
Log.error(label: "\(LOG_TAG):\(#function)", "Extension not registered")
return nil
}

var version = Int.max
var version = 0 // default to version 0 if event nil
if let unwrappedEvent = event {
version = eventNumberMap[unwrappedEvent.id] ?? Int.max
version = eventNumberMap[unwrappedEvent.id] ?? 0
}

let result = sharedState.resolve(version: version)
Expand Down Expand Up @@ -233,7 +233,16 @@ final class EventHub {
// TODO: Determine which version of Core to use in the top level version field
let data: [String: Any] = [EventHubConstants.EventDataKeys.VERSION: ConfigurationConstants.EXTENSION_VERSION,
EventHubConstants.EventDataKeys.EXTENSIONS: extensionsInfo]
createSharedState(extensionName: EventHubConstants.NAME, data: data, event: nil)

guard let sharedState = registeredExtensions.first(where: { $1.sharedStateName == EventHubConstants.NAME })?.value.sharedState else {
Log.error(label: "\(LOG_TAG):\(#function)", "Extension not registered with EventHub")
return
}

let version = sharedState.resolve(version: 0).value == nil ? 0 : eventNumberCounter.incrementAndGet()
sharedState.set(version: version, data: data)
dispatch(event: createSharedStateEvent(extensionName: EventHubConstants.NAME))
Log.debug(label: "\(LOG_TAG):\(#function)", "Shared state is created for \(EventHubConstants.NAME) with data \(String(describing: data)) and version \(version)")
}

// MARK: Private
Expand All @@ -244,13 +253,10 @@ final class EventHub {
return nil
}

var version = -1
var version = 0 // default to version 0
// attempt to version at the event
if let unwrappedEvent = event, let eventNumber = eventNumberMap[unwrappedEvent.id] {
version = eventNumber
} else {
// default to next event number
version = eventNumberCounter.incrementAndGet()
}

guard let sharedState = extensionContainer.sharedState else { return nil }
Expand All @@ -268,6 +274,7 @@ final class EventHub {
return Event(name: EventHubConstants.STATE_CHANGE, type: EventType.hub, source: EventSource.sharedState,
data: [EventHubConstants.EventDataKeys.Configuration.EVENT_STATE_OWNER: extensionName])
}

}

private extension Extension {
Expand Down
4 changes: 2 additions & 2 deletions AEPCore/Sources/eventhub/ExtensionRuntime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ import Foundation
/// Creates a new `SharedState for this extension
/// - Parameters:
/// - data: Data for the `SharedState`
/// - event: An event for the `SharedState` to be versioned at, if nil the shared state is versioned at the latest
/// - event: An event for the `SharedState` to be versioned at, if nil the shared state is versioned zero
func createSharedState(data: [String: Any], event: Event?)

/// Creates a pending `SharedState` versioned at `event`
/// - Parameter event: The event for the pending `SharedState` to be created at
/// - Parameter event: The event for the pending `SharedState` to be created at, if nil the shared state is versioned zero
func createPendingSharedState(event: Event?) -> SharedStateResolver

/// Gets the `SharedState` data for a specified extension
Expand Down
73 changes: 61 additions & 12 deletions AEPCore/Tests/EventHubTests/EventHubTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -703,11 +703,15 @@ class EventHubTests: XCTestCase {
eventHub.start()

// test
eventHub.createSharedState(extensionName: EventHubTests.MOCK_EXTENSION_NAME, data: SharedStateTestHelper.ONE, event: nil)
eventHub.createSharedState(extensionName: EventHubTests.MOCK_EXTENSION_NAME, data: SharedStateTestHelper.TWO, event: nil)
let event = Event(name: "Test event", type: EventType.analytics, source: EventSource.none, data: nil)
eventHub.dispatch(event: event)
eventHub.createSharedState(extensionName: EventHubTests.MOCK_EXTENSION_NAME, data: SharedStateTestHelper.ONE, event: event)
let event1 = Event(name: "Test event", type: EventType.analytics, source: EventSource.none, data: nil)
eventHub.dispatch(event: event1)
eventHub.createSharedState(extensionName: EventHubTests.MOCK_EXTENSION_NAME, data: SharedStateTestHelper.TWO, event: event1)

// verify
validateSharedState(EventHubTests.MOCK_EXTENSION_NAME, nil, "two")
validateSharedState(EventHubTests.MOCK_EXTENSION_NAME, event1, "two")
}

/// Tests that multiple shared state updates function properly
Expand All @@ -716,14 +720,22 @@ class EventHubTests: XCTestCase {
eventHub.start()

// test
eventHub.createSharedState(extensionName: EventHubTests.MOCK_EXTENSION_NAME, data: SharedStateTestHelper.ONE, event: nil)
validateSharedState(EventHubTests.MOCK_EXTENSION_NAME, nil, "one")
eventHub.createSharedState(extensionName: EventHubTests.MOCK_EXTENSION_NAME, data: SharedStateTestHelper.TWO, event: nil)
validateSharedState(EventHubTests.MOCK_EXTENSION_NAME, nil, "two")
eventHub.createSharedState(extensionName: EventHubTests.MOCK_EXTENSION_NAME, data: SharedStateTestHelper.THREE, event: nil)
let event = Event(name: "Test event", type: EventType.analytics, source: EventSource.none, data: nil)
eventHub.dispatch(event: event)
eventHub.createSharedState(extensionName: EventHubTests.MOCK_EXTENSION_NAME, data: SharedStateTestHelper.ONE, event: event)
validateSharedState(EventHubTests.MOCK_EXTENSION_NAME, event, "one")

let event1 = Event(name: "Test event", type: EventType.analytics, source: EventSource.none, data: nil)
eventHub.dispatch(event: event1)
eventHub.createSharedState(extensionName: EventHubTests.MOCK_EXTENSION_NAME, data: SharedStateTestHelper.TWO, event: event1)
validateSharedState(EventHubTests.MOCK_EXTENSION_NAME, event1, "two")

let event2 = Event(name: "Test event", type: EventType.analytics, source: EventSource.none, data: nil)
eventHub.dispatch(event: event2)
eventHub.createSharedState(extensionName: EventHubTests.MOCK_EXTENSION_NAME, data: SharedStateTestHelper.THREE, event: event2)

// verify
validateSharedState(EventHubTests.MOCK_EXTENSION_NAME, nil, "three")
validateSharedState(EventHubTests.MOCK_EXTENSION_NAME, event2, "three")
}

/// Shared state is versioned at event correctly
Expand All @@ -747,17 +759,54 @@ class EventHubTests: XCTestCase {

// test
let event = Event(name: "test", type: EventType.analytics, source: EventSource.requestContent, data: nil)
eventHub.createSharedState(extensionName: EventHubTests.MOCK_EXTENSION_NAME, data: SharedStateTestHelper.ONE, event: nil)
eventHub.dispatch(event: event)
eventHub.createSharedState(extensionName: EventHubTests.MOCK_EXTENSION_NAME, data: SharedStateTestHelper.ONE, event: event)
let event1 = Event(name: "test1", type: EventType.analytics, source: EventSource.requestContent, data: nil)
eventHub.createSharedState(extensionName: EventHubTests.MOCK_EXTENSION_NAME, data: SharedStateTestHelper.TWO, event: nil)
eventHub.dispatch(event: event1)
eventHub.createSharedState(extensionName: EventHubTests.MOCK_EXTENSION_NAME, data: SharedStateTestHelper.TWO, event: event1)

// verify
validateSharedState(EventHubTests.MOCK_EXTENSION_NAME, event, "one")
validateSharedState(EventHubTests.MOCK_EXTENSION_NAME, event1, "two")
}

func testGetSharedStateNilEvent() {
// setup
eventHub.start()

// test
eventHub.createSharedState(extensionName: EventHubTests.MOCK_EXTENSION_NAME, data: SharedStateTestHelper.ONE, event: nil)

// verify
validateSharedState(EventHubTests.MOCK_EXTENSION_NAME, nil, "one")
}

func testGetSharedStateNilEventTwice() {
// setup
eventHub.start()

// test
eventHub.createSharedState(extensionName: EventHubTests.MOCK_EXTENSION_NAME, data: SharedStateTestHelper.ONE, event: nil)
eventHub.createSharedState(extensionName: EventHubTests.MOCK_EXTENSION_NAME, data: SharedStateTestHelper.TWO, event: nil)

// verify
validateSharedState(EventHubTests.MOCK_EXTENSION_NAME, nil, "one")
}

func testGetSharedStateNilEventVersionsAtZero() {
// setup
eventHub.start()

// test
eventHub.createSharedState(extensionName: EventHubTests.MOCK_EXTENSION_NAME, data: SharedStateTestHelper.ONE, event: nil)
let event1 = Event(name: "test1", type: EventType.analytics, source: EventSource.requestContent, data: nil)
eventHub.dispatch(event: event1)
eventHub.createSharedState(extensionName: EventHubTests.MOCK_EXTENSION_NAME, data: SharedStateTestHelper.TWO, event: event1)

// verify
validateSharedState(EventHubTests.MOCK_EXTENSION_NAME, nil, "one")
}

/// Tests that events are associated with current shared state when updated rapidly
func testGetSharedStateUsesCorrectVersionManyEvents() {
// setup
Expand Down Expand Up @@ -879,7 +928,7 @@ class EventHubTests: XCTestCase {
pendingResolver(SharedStateTestHelper.TWO)

wait(for: [expectation], timeout: 0.5)
validateSharedState(EventHubTests.MOCK_EXTENSION_NAME, nil, "two")
validateSharedState(EventHubTests.MOCK_EXTENSION_NAME, event, "two")
}

/// Tests that we can create and resolve a pending shared state from many queues
Expand Down
11 changes: 6 additions & 5 deletions AEPIdentity/Sources/IdentityState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,9 @@ class IdentityState {
identityProperties.lastSync = Date()

// check privacy here in case the status changed while response was in-flight
if identityProperties.privacyStatus != .optedOut {
if identityProperties.privacyStatus != .optedOut, let data = hit.data, let hit = try? JSONDecoder().decode(IdentityHit.self, from: data) {
// update properties
handleNetworkResponse(response: response, eventDispatcher: eventDispatcher, createSharedState: createSharedState)
handleNetworkResponse(response: response, eventDispatcher: eventDispatcher, createSharedState: createSharedState, event: hit.event)

// save
identityProperties.saveToPersistence()
Expand Down Expand Up @@ -308,7 +308,8 @@ class IdentityState {
/// - response: the network response
/// - eventDispatcher: a function which when invoked dispatches an `Event` to the `EventHub`
/// - createSharedState: a function which when invoked creates a shared state for the Identity extension
private func handleNetworkResponse(response: Data?, eventDispatcher: (Event) -> Void, createSharedState: ([String: Any], Event?) -> Void) {
/// - event: The event responsible for the network response
private func handleNetworkResponse(response: Data?, eventDispatcher: (Event) -> Void, createSharedState: (([String: Any], Event?) -> Void), event: Event) {
guard let data = response, let identityResponse = try? JSONDecoder().decode(IdentityHitResponse.self, from: data) else {
Log.debug(label: "\(LOG_TAG):\(#function)", "Failed to decode Identity hit response")
return
Expand All @@ -327,7 +328,7 @@ class IdentityState {
// Still, generate ECID locally if there's none yet.
identityProperties.ecid = identityProperties.ecid ?? ECID()
Log.error(label: "\(LOG_TAG):\(#function)", "Identity response returned error: \(error)")
createSharedState(identityProperties.toEventData(), nil)
createSharedState(identityProperties.toEventData(), event)
return
}

Expand All @@ -337,7 +338,7 @@ class IdentityState {
identityProperties.locationHint = identityResponse.hint
identityProperties.ttl = identityResponse.ttl ?? IdentityConstants.Default.TTL
if shouldShareState {
createSharedState(identityProperties.toEventData(), nil)
createSharedState(identityProperties.toEventData(), event)
}
}
}
Expand Down