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

Downmerge to Dev after 5.0.3 release #502

Closed
wants to merge 6 commits into from
Closed
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
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ jobs:
test-ios:
macos:
xcode: 15.1.0 # Specify the Xcode version to use
resource_class: macos.x86.medium.gen2
resource_class: macos.m1.medium.gen1

steps:
- checkout
Expand Down Expand Up @@ -158,7 +158,7 @@ jobs:
test-tvos:
macos:
xcode: 15.1.0 # Specify the Xcode version to use
resource_class: macos.x86.medium.gen2
resource_class: macos.m1.medium.gen1

steps:
- checkout
Expand Down
4 changes: 2 additions & 2 deletions AEPEdge.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "AEPEdge"
s.version = "5.0.2"
s.version = "5.0.3"
s.summary = "Experience Platform Edge extension for Adobe Experience Platform Mobile SDK. Written and maintained by Adobe."

s.description = <<-DESC
Expand All @@ -18,7 +18,7 @@ Pod::Spec.new do |s|
s.swift_version = '5.1'

s.pod_target_xcconfig = { 'BUILD_LIBRARY_FOR_DISTRIBUTION' => 'YES' }
s.dependency 'AEPCore', '>= 5.1.0', '< 6.0.0'
s.dependency 'AEPCore', '>= 5.3.1', '< 6.0.0'
s.dependency 'AEPEdgeIdentity', '>= 5.0.0', '< 6.0.0'

s.source_files = 'Sources/**/*.swift'
Expand Down
4 changes: 2 additions & 2 deletions AEPEdge.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1554,7 +1554,7 @@
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MARKETING_VERSION = 5.0.2;
MARKETING_VERSION = 5.0.3;
OTHER_LDFLAGS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = com.adobe.aep.edge;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
Expand Down Expand Up @@ -1590,7 +1590,7 @@
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
MARKETING_VERSION = 5.0.2;
MARKETING_VERSION = 5.0.3;
OTHER_LDFLAGS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = com.adobe.aep.edge;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ let package = Package(
.library(name: "AEPEdge", targets: ["AEPEdge"])
],
dependencies: [
.package(url: "https://github.com/adobe/aepsdk-core-ios.git", .upToNextMajor(from: "5.1.0")),
.package(url: "https://github.com/adobe/aepsdk-core-ios.git", .upToNextMajor(from: "5.3.1")),
.package(url: "https://github.com/adobe/aepsdk-edgeidentity-ios.git", .upToNextMajor(from: "5.0.0"))
],
targets: [
Expand Down
6 changes: 3 additions & 3 deletions Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@ end

target 'UnitTests' do
core_pods
pod 'AEPTestUtils', :git => 'https://github.com/adobe/aepsdk-testutils-ios.git', :tag => '5.0.0'
pod 'AEPTestUtils', :git => 'https://github.com/adobe/aepsdk-testutils-ios.git', :tag => '5.0.2'
end

target 'UpstreamIntegrationTests' do
core_pods
edge_pods
pod 'AEPTestUtils', :git => 'https://github.com/adobe/aepsdk-testutils-ios.git', :tag => '5.0.0'
pod 'AEPTestUtils', :git => 'https://github.com/adobe/aepsdk-testutils-ios.git', :tag => '5.0.2'
end

target 'FunctionalTests' do
core_pods
edge_pods
pod 'AEPTestUtils', :git => 'https://github.com/adobe/aepsdk-testutils-ios.git', :tag => '5.0.0'
pod 'AEPTestUtils', :git => 'https://github.com/adobe/aepsdk-testutils-ios.git', :tag => '5.0.2'
end

target 'TestAppiOS' do
Expand Down
38 changes: 19 additions & 19 deletions Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
PODS:
- AEPAssurance (5.0.0):
- AEPAssurance (5.0.1):
- AEPCore (< 6.0.0, >= 5.0.0)
- AEPServices (< 6.0.0, >= 5.0.0)
- AEPCore (5.1.0):
- AEPCore (5.3.1):
- AEPRulesEngine (< 6.0.0, >= 5.0.0)
- AEPServices (< 6.0.0, >= 5.1.0)
- AEPEdge (5.0.2):
- AEPCore (< 6.0.0, >= 5.1.0)
- AEPServices (< 6.0.0, >= 5.3.1)
- AEPEdge (5.0.3):
- AEPCore (< 6.0.0, >= 5.3.1)
- AEPEdgeIdentity (< 6.0.0, >= 5.0.0)
- AEPEdgeConsent (5.0.0):
- AEPCore (< 6.0.0, >= 5.0.0)
- AEPEdge (< 6.0.0, >= 5.0.0)
- AEPEdgeIdentity (5.0.0):
- AEPCore (< 6.0.0, >= 5.0.0)
- AEPRulesEngine (5.0.0)
- AEPServices (5.1.0)
- AEPTestUtils (5.0.0):
- AEPCore
- AEPServices
- AEPServices (5.3.1)
- AEPTestUtils (5.0.2):
- AEPCore (>= 5.2.0)
- AEPServices (>= 5.2.0)
- SwiftLint (0.52.0)

DEPENDENCIES:
Expand All @@ -26,7 +26,7 @@ DEPENDENCIES:
- AEPEdge (from `./AEPEdge.podspec`)
- AEPEdgeConsent
- AEPEdgeIdentity
- AEPTestUtils (from `https://github.com/adobe/aepsdk-testutils-ios.git`, tag `5.0.0`)
- AEPTestUtils (from `https://github.com/adobe/aepsdk-testutils-ios.git`, tag `5.0.2`)
- SwiftLint (= 0.52.0)

SPEC REPOS:
Expand All @@ -44,24 +44,24 @@ EXTERNAL SOURCES:
:path: "./AEPEdge.podspec"
AEPTestUtils:
:git: https://github.com/adobe/aepsdk-testutils-ios.git
:tag: 5.0.0
:tag: 5.0.2

CHECKOUT OPTIONS:
AEPTestUtils:
:git: https://github.com/adobe/aepsdk-testutils-ios.git
:tag: 5.0.0
:tag: 5.0.2

SPEC CHECKSUMS:
AEPAssurance: 7f260ded4df38a70a06efebade8c33a3e3221984
AEPCore: 55489e1c4e48a88659055f8e96290f2cccce9747
AEPEdge: edf73ae8900016940cd7fcb29a89a576a1c6b0ae
AEPAssurance: df04baeace42befb0cc213fd6cdfe51651d11ba6
AEPCore: 28191f2df03225a5e88b6f8f343f7e18a604c9ef
AEPEdge: 105afc7958acd7c016d57f7ac1d6f632bf05e6ee
AEPEdgeConsent: d7db1d19eb4c1e2146360ed3c8df315f671b26d5
AEPEdgeIdentity: 3161ff33434586962946912d6b8e9e8fca1c4d23
AEPRulesEngine: fe5800653a4bee07b1e41e61b4d5551f0dba557b
AEPServices: c38b809c32336f10f773525e65c71a949abe54a7
AEPTestUtils: 20495b368da57904ca2e9f241d1d8b114f9887b5
AEPServices: fcba979e90f6916b066aa66f016700d7b7534d96
AEPTestUtils: ef5a4f6cf9ef61630b7c68b5d205bb885a6f19a2
SwiftLint: 13280e21cdda6786ad908dc6e416afe5acd1fcb7

PODFILE CHECKSUM: 559b845c969523123ac98dd7a3e4559871b3c2aa
PODFILE CHECKSUM: 6202390e59242cb95df7c5f4f4fd723056c21974

COCOAPODS: 1.13.0
COCOAPODS: 1.14.3
2 changes: 1 addition & 1 deletion Sources/EdgeConstants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import Foundation
enum EdgeConstants {

static let EXTENSION_NAME = "com.adobe.edge"
static let EXTENSION_VERSION = "5.0.2"
static let EXTENSION_VERSION = "5.0.3"
static let FRIENDLY_NAME = "AEPEdge"
static let LOG_TAG = FRIENDLY_NAME

Expand Down
16 changes: 7 additions & 9 deletions Sources/EdgeNetworkHandlers/EdgeNetworkService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,21 @@ enum HttpResponseCodes: Int {
case ok = 200
case noContent = 204
case multiStatus = 207
case clientTimeout = 408
case tooManyRequests = 429
case badGateway = 502
case serviceUnavailable = 503
case gatewayTimeout = 504
case insufficientStorage = 507
}

/// Network service for requests to the Adobe Experience Edge
class EdgeNetworkService {
private let SELF_TAG: String = "EdgeNetworkService"
private let DEFAULT_GENERIC_ERROR_MESSAGE = "Request to Experience Edge failed with an unknown exception"
private let DEFAULT_GENERIC_ERROR_TITLE = "Unexpected Error"
private let recoverableNetworkErrorCodes: [Int] = [HttpResponseCodes.clientTimeout.rawValue,
HttpResponseCodes.tooManyRequests.rawValue,
HttpResponseCodes.badGateway.rawValue,
HttpResponseCodes.serviceUnavailable.rawValue,
HttpResponseCodes.gatewayTimeout.rawValue]
private let recoverableNetworkErrorCodes = Set(NetworkServiceConstants.RECOVERABLE_ERROR_CODES +
[HttpResponseCodes.tooManyRequests.rawValue,
HttpResponseCodes.badGateway.rawValue,
HttpResponseCodes.insufficientStorage.rawValue])

private let waitTimeout: TimeInterval = max(EdgeConstants.NetworkKeys.DEFAULT_CONNECT_TIMEOUT, EdgeConstants.NetworkKeys.DEFAULT_READ_TIMEOUT) + 1
private var defaultHeaders = [EdgeConstants.NetworkKeys.HEADER_KEY_ACCEPT: EdgeConstants.NetworkKeys.HEADER_VALUE_APPLICATION_JSON,
EdgeConstants.NetworkKeys.HEADER_KEY_CONTENT_TYPE: EdgeConstants.NetworkKeys.HEADER_VALUE_APPLICATION_JSON]
Expand Down Expand Up @@ -220,7 +218,7 @@ class EdgeNetworkService {
let retryHeader = connection.responseHttpHeader(forKey: EdgeConstants.NetworkKeys.HEADER_KEY_RETRY_AFTER)
var retryInterval = EdgeConstants.Defaults.RETRY_INTERVAL
// Do not currently support HTTP-date only parsing Ints for now. Konductor will only send back Retry-After as Ints.
if let retryHeader = retryHeader, let retryAfterInterval = TimeInterval(retryHeader) {
if let retryHeader = retryHeader, let retryAfterInterval = TimeInterval(retryHeader), retryAfterInterval > 0 {
retryInterval = retryAfterInterval
}
Log.debug(label: EdgeConstants.LOG_TAG,
Expand Down
110 changes: 91 additions & 19 deletions Tests/UnitTests/EdgeHitProcessorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ class EdgeHitProcessorTests: XCTestCase, AnyCodableAsserts {
private let MEDIA_ENDPOINT_PRE_PROD_LOC_HINT = "https://edge.adobedc.net/ee-pre-prd/lh1/va/v1/sessionstart"
private let MEDIA_ENDPOINT_INT_LOC_HINT = "https://edge-int.adobedc.net/ee/lh1/va/v1/sessionstart"

private let EDGE_RECOVERABLE_ERROR_CODES = Set(NetworkServiceConstants.RECOVERABLE_ERROR_CODES +
[HttpResponseCodes.tooManyRequests.rawValue,
HttpResponseCodes.badGateway.rawValue,
HttpResponseCodes.insufficientStorage.rawValue])

private static let CUSTOM_DOMAIN = "my.awesome.site"
private static let CUSTOM_CONSENT_ENDPOINT = "https://\(CUSTOM_DOMAIN)/ee/v1/privacy/set-consent"
private static let CUSTOM_CONSENT_ENDPOINT_PRE_PROD = "https://\(CUSTOM_DOMAIN)/ee-pre-prd/v1/privacy/set-consent"
Expand Down Expand Up @@ -175,23 +180,54 @@ class EdgeHitProcessorTests: XCTestCase, AnyCodableAsserts {
XCTAssertTrue(requestString.contains("test-datastream-id-override"))
}

/// Tests that when the network request fails but has a recoverable error that we will retry the hit and do not invoke the response handler for that hit
func testProcessHit_experienceEvent_whenRecoverableNetworkError_sendsNetworkRequest_returnsFalse_setsRetryInterval() {
/// Test with all recoverable error codes and retry-after header (in seconds), returns false so hit is retried and sets retry timeout
func testProcessHit_experienceEvent_whenRecoverableNetworkError_andRetryAfterResponseHeader_setsRetryInterval() {
// setup
let recoverableNetworkErrorCodes = [HttpResponseCodes.clientTimeout.rawValue,
HttpResponseCodes.tooManyRequests.rawValue,
HttpResponseCodes.serviceUnavailable.rawValue,
HttpResponseCodes.gatewayTimeout.rawValue]

let retryTimeout = 60
let responseHeaders = ["Retry-After": String(retryTimeout)]
let expectation = XCTestExpectation(description: "Callback should be invoked with false signaling this hit should be retried")
expectation.expectedFulfillmentCount = recoverableNetworkErrorCodes.count
expectation.expectedFulfillmentCount = EDGE_RECOVERABLE_ERROR_CODES.count

mockNetworkService.setExpectationForNetworkRequest(url: INTERACT_ENDPOINT_PROD, httpMethod: .post, expectedCount: Int32(EDGE_RECOVERABLE_ERROR_CODES.count))

for code in EDGE_RECOVERABLE_ERROR_CODES {
let error = EdgeEventError(title: "test-title", detail: nil, status: code, type: "test-type", report: EdgeErrorReport(eventIndex: 0, errors: nil, requestId: nil, orgId: nil))
let edgeResponse = EdgeResponse(requestId: "test-req-id", handle: nil, errors: [error], warnings: nil)
let responseData = try? JSONEncoder().encode(edgeResponse)

mockNetworkService.setMockResponse(
url: INTERACT_ENDPOINT_PROD,
httpMethod: .post,
responseConnection: HttpConnection(
data: responseData,
response: HTTPURLResponse(url: url, statusCode: code, httpVersion: nil, headerFields: responseHeaders),
error: nil))

let edgeEntity = getEdgeDataEntity(event: experienceEvent, configuration: defaultEdgeConfig, identityMap: defaultIdentityMap)
let entity = DataEntity(uniqueIdentifier: "test-uuid", timestamp: Date(), data: try? JSONEncoder().encode(edgeEntity))

// (headerValue, actualRetryValue)
let retryValues = [("60", 60.0), ("InvalidHeader", 5.0), ("", 5.0), ("1", 1.0)]
// test
hitProcessor.processHit(entity: entity) { success in
XCTAssertFalse(success)
XCTAssertEqual(self.hitProcessor.retryInterval(for: entity), TimeInterval(retryTimeout))
expectation.fulfill()
}
}

mockNetworkService.setExpectationForNetworkRequest(url: INTERACT_ENDPOINT_PROD, httpMethod: .post, expectedCount: Int32(recoverableNetworkErrorCodes.count))
// verify
wait(for: [expectation], timeout: 1)
mockNetworkService.assertAllNetworkRequestExpectations(ignoreUnexpectedRequests: false)
}

/// Tests that when the network request fails but has a recoverable error that it retries the hit and does not invoke the response handler for that hit
func testProcessHit_experienceEvent_whenRecoverableNetworkError_sendsNetworkRequest_returnsFalse_setsDefaultRetryInterval() {
// setup
let expectation = XCTestExpectation(description: "Callback should be invoked with false signaling this hit should be retried")
expectation.expectedFulfillmentCount = EDGE_RECOVERABLE_ERROR_CODES.count

for (code, retryValueTuple) in zip(recoverableNetworkErrorCodes, retryValues) {
mockNetworkService.setExpectationForNetworkRequest(url: INTERACT_ENDPOINT_PROD, httpMethod: .post, expectedCount: Int32(EDGE_RECOVERABLE_ERROR_CODES.count))

for code in EDGE_RECOVERABLE_ERROR_CODES {
let error = EdgeEventError(title: "test-title", detail: nil, status: code, type: "test-type", report: EdgeErrorReport(eventIndex: 0, errors: nil, requestId: nil, orgId: nil))
let edgeResponse = EdgeResponse(requestId: "test-req-id", handle: nil, errors: [error], warnings: nil)
let responseData = try? JSONEncoder().encode(edgeResponse)
Expand All @@ -201,10 +237,7 @@ class EdgeHitProcessorTests: XCTestCase, AnyCodableAsserts {
httpMethod: .post,
responseConnection: HttpConnection(
data: responseData,
response: HTTPURLResponse(url: url,
statusCode: code,
httpVersion: nil,
headerFields: ["Retry-After": retryValueTuple.0]),
response: HTTPURLResponse(url: url, statusCode: code, httpVersion: nil, headerFields: nil),
error: nil))

let edgeEntity = getEdgeDataEntity(event: experienceEvent, configuration: defaultEdgeConfig, identityMap: defaultIdentityMap)
Expand All @@ -213,7 +246,7 @@ class EdgeHitProcessorTests: XCTestCase, AnyCodableAsserts {
// test
hitProcessor.processHit(entity: entity) { success in
XCTAssertFalse(success)
XCTAssertEqual(self.hitProcessor.retryInterval(for: entity), retryValueTuple.1)
XCTAssertEqual(self.hitProcessor.retryInterval(for: entity), 5.0) // default retry interval
expectation.fulfill()
}
}
Expand All @@ -223,7 +256,7 @@ class EdgeHitProcessorTests: XCTestCase, AnyCodableAsserts {
mockNetworkService.assertAllNetworkRequestExpectations(ignoreUnexpectedRequests: false)
}

/// Tests that when the network request fails and does not have a recoverable response code that we invoke the response handler and do not retry the hit
/// Tests that when the network request fails and does not have a recoverable response code, it invokes the response handler and does not retry the hit
func testProcessHit_experienceEvent_whenUnrecoverableNetworkError_sendsNetworkRequest_returnsTrue() {
// setup
mockNetworkService.setMockResponse(
Expand Down Expand Up @@ -441,7 +474,7 @@ class EdgeHitProcessorTests: XCTestCase, AnyCodableAsserts {
headerFields: nil),
error: nil))

let event = Event(name: "test-consent-event", type: EventType.edge, source: EventSource.requestContent, data: nil)
let event = Event(name: "test-experience-event", type: EventType.edge, source: EventSource.requestContent, data: nil)
let edgeEntity = getEdgeDataEntity(event: event, configuration: defaultEdgeConfig, identityMap: defaultIdentityMap)

let entity = DataEntity(uniqueIdentifier: "test-uuid", timestamp: Date(), data: try? JSONEncoder().encode(edgeEntity))
Expand Down Expand Up @@ -575,6 +608,45 @@ class EdgeHitProcessorTests: XCTestCase, AnyCodableAsserts {
XCTAssertEqual(1, waitingEvents?.count)
}

/// Tests that when the consent network request fails with a recoverable error and retry-after header (in seconds), sets this value as timeout for the retry
func testProcessHit_consentUpdateEvent_whenRecoverableNetworkError_andRetryAfterResponseHeader_setsRetryInterval() {
// setup
let retryTimeout = 60
let responseHeaders = ["Retry-After": String(retryTimeout)]
let expectation = XCTestExpectation(description: "Callback should be invoked with false signaling this hit should be retried")
expectation.expectedFulfillmentCount = EDGE_RECOVERABLE_ERROR_CODES.count

mockNetworkService.setExpectationForNetworkRequest(url: CONSENT_ENDPOINT, httpMethod: .post, expectedCount: Int32(EDGE_RECOVERABLE_ERROR_CODES.count))

for code in EDGE_RECOVERABLE_ERROR_CODES {
let error = EdgeEventError(title: "test-title", detail: nil, status: code, type: "test-type", report: EdgeErrorReport(eventIndex: 0, errors: nil, requestId: nil, orgId: nil))
let edgeResponse = EdgeResponse(requestId: "test-req-id", handle: nil, errors: [error], warnings: nil)
let responseData = try? JSONEncoder().encode(edgeResponse)

mockNetworkService.setMockResponse(
url: CONSENT_ENDPOINT,
httpMethod: .post,
responseConnection: HttpConnection(
data: responseData,
response: HTTPURLResponse(url: url, statusCode: code, httpVersion: nil, headerFields: responseHeaders),
error: nil))

let edgeEntity = getEdgeDataEntity(event: consentUpdateEvent, configuration: defaultEdgeConfig, identityMap: defaultIdentityMap)
let entity = DataEntity(uniqueIdentifier: "test-uuid", timestamp: Date(), data: try? JSONEncoder().encode(edgeEntity))

// test
hitProcessor.processHit(entity: entity) { success in
XCTAssertFalse(success)
XCTAssertEqual(self.hitProcessor.retryInterval(for: entity), TimeInterval(retryTimeout))
expectation.fulfill()
}
}

// verify
wait(for: [expectation], timeout: 1)
mockNetworkService.assertAllNetworkRequestExpectations(ignoreUnexpectedRequests: false)
}

func testProcessHit_resetIdentitiesEvent_clearsStateStore_returnsTrue() {
let storeResponsePayloadManager = StoreResponsePayloadManager(EdgeConstants.DataStoreKeys.STORE_NAME)
storeResponsePayloadManager.saveStorePayloads([StoreResponsePayload(payload: StorePayload(key: "key",
Expand Down
Loading
Loading