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

chore: kickoff release #3406

Merged
merged 2 commits into from
Dec 7, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,15 @@ extension AWSCognitoAuthPlugin {
configuration.httpClientEngine = .userAgentEngine(for: configuration)
}

if let requestTimeout = networkPreferences?.timeoutIntervalForRequest {
let requestTimeOutMs = requestTimeout * 1_000
configuration.connectTimeoutMs = UInt32(requestTimeOutMs)
}

if let maxRetryUnwrapped = networkPreferences?.maxRetryCount {
configuration.retryStrategyOptions = RetryStrategyOptions(maxRetriesBase: Int(maxRetryUnwrapped))
}

return CognitoIdentityProviderClient(config: configuration)
default:
fatalError()
Expand All @@ -116,6 +125,15 @@ extension AWSCognitoAuthPlugin {
)
configuration.httpClientEngine = .userAgentEngine(for: configuration)

if let requestTimeout = networkPreferences?.timeoutIntervalForRequest {
let requestTimeOutMs = requestTimeout * 1_000
configuration.connectTimeoutMs = UInt32(requestTimeOutMs)
}

if let maxRetryUnwrapped = networkPreferences?.maxRetryCount {
configuration.retryStrategyOptions = RetryStrategyOptions(maxRetriesBase: Int(maxRetryUnwrapped))
}

return CognitoIdentityClient(config: configuration)
default:
fatalError()
Expand All @@ -129,6 +147,15 @@ extension AWSCognitoAuthPlugin {
private func makeURLSession() -> URLSession {
let configuration = URLSessionConfiguration.default
configuration.urlCache = nil

if let timeoutIntervalForRequest = networkPreferences?.timeoutIntervalForRequest {
configuration.timeoutIntervalForRequest = timeoutIntervalForRequest
}

if let timeoutIntervalForResource = networkPreferences?.timeoutIntervalForResource {
configuration.timeoutIntervalForResource = timeoutIntervalForResource
}

return URLSession(configuration: configuration)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ public final class AWSCognitoAuthPlugin: AWSCognitoAuthPluginBehavior {

var httpClientEngineProxy: HttpClientEngineProxy?

/// The user network preferences for timeout and retry
let networkPreferences: AWSCognitoNetworkPreferences?

@_spi(InternalAmplifyConfiguration)
internal(set) public var jsonConfiguration: JSONValue?

Expand All @@ -42,5 +45,13 @@ public final class AWSCognitoAuthPlugin: AWSCognitoAuthPluginBehavior {

/// Instantiates an instance of the AWSCognitoAuthPlugin.
public init() {
self.networkPreferences = nil
}

/// Instantiates an instance of the AWSCognitoAuthPlugin with custom network preferences
/// - Parameters:
/// - networkPreferences: network preferences
public init(networkPreferences: AWSCognitoNetworkPreferences) {
self.networkPreferences = networkPreferences
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import Foundation

public struct AWSCognitoNetworkPreferences {

/// The maximum number of retries for failed requests.
public let maxRetryCount: UInt32

/// The timeout interval to use when waiting for additional data.
public let timeoutIntervalForRequest: TimeInterval

/// The maximum amount of time that a resource request should be allowed to take.
///
/// NOTE: This value is only applicable to HostedUI because the underlying Swift SDK does
/// not support resource timeouts
public let timeoutIntervalForResource: TimeInterval?

public init(maxRetryCount: UInt32,
timeoutIntervalForRequest: TimeInterval,
timeoutIntervalForResource: TimeInterval? = nil) {
self.maxRetryCount = maxRetryCount
self.timeoutIntervalForRequest = timeoutIntervalForRequest
self.timeoutIntervalForResource = timeoutIntervalForResource
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,50 @@ class AWSCognitoAuthPluginConfigTests: XCTestCase {
}
}

/// Test Auth configuration with valid config for user pool and identity pool, with network preferences
///
/// - Given: Given valid config for user pool and identity pool, and network preferences
/// - When:
/// - I configure auth with the given configuration and network preferences
/// - Then:
/// - I should not get any error while configuring auth
///
func testConfigWithUserPoolAndIdentityPoolWithNetworkPreferences() throws {
let plugin = AWSCognitoAuthPlugin(
networkPreferences: .init(
maxRetryCount: 2,
timeoutIntervalForRequest: 60,
timeoutIntervalForResource: 60))
try Amplify.add(plugin: plugin)

let categoryConfig = AuthCategoryConfiguration(plugins: [
"awsCognitoAuthPlugin": [
"CredentialsProvider": ["CognitoIdentity": ["Default":
["PoolId": "xx",
"Region": "us-east-1"]
]],
"CognitoUserPool": ["Default": [
"PoolId": "xx",
"Region": "us-east-1",
"AppClientId": "xx",
"AppClientSecret": "xx"]]
]
])
let amplifyConfig = AmplifyConfiguration(auth: categoryConfig)
do {
try Amplify.configure(amplifyConfig)

let escapeHatch = plugin.getEscapeHatch()
guard case .userPoolAndIdentityPool(let userPoolClient, let identityPoolClient) = escapeHatch else {
XCTFail("Expected .userPool, got \(escapeHatch)")
return
}
XCTAssertNotNil(userPoolClient)
XCTAssertNotNil(identityPoolClient)

} catch {
XCTFail("Should not throw error. \(error)")
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ import AWSPluginsCore
import Foundation

extension PinpointEvent {
var clientTypeSession: PinpointClientTypes.Session {
var clientTypeSession: PinpointClientTypes.Session? {
#if os(watchOS)
// If the session duration cannot be represented by Int, return a nil session instead.
// This is extremely unlikely to happen since a session's stopTime is set when the app is closed
if let duration = session.duration, duration > Int.max {
return nil
}
#endif
return PinpointClientTypes.Session(duration: Int(session.duration),
id: session.sessionId,
startTimestamp: session.startTime.asISO8601String,
Expand Down Expand Up @@ -48,3 +55,12 @@ extension Bundle {
object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? ""
}
}

private extension Int {
init?(_ value: Int64?) {
guard let value = value else {
return nil
}
self.init(value)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@ public class PinpointSession: Codable {
return stopTime != nil
}

var duration: Date.Millisecond {
let endTime = stopTime ?? Date()
var duration: Date.Millisecond? {
/// According to Pinpoint's documentation, `duration` is only required if `stopTime` is not nil.
guard let endTime = stopTime else {
return nil
}
return endTime.millisecondsSince1970 - startTime.millisecondsSince1970
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,37 @@ class EventRecorderTests: XCTestCase {
XCTAssertEqual(storage.deleteEventCallCount, 2)
}

/// - Given: a event recorder with events saved in the local storage with active and stopped sessions
/// - When: submitAllEvents is invoked
/// - Then: the input is generated accordingly by including duration only for the stopped session
func testSubmitAllEvents_withActiveAndStoppedSessions_shouldGenerateRightInput() async throws {
let activeSession = PinpointSession(
sessionId: "active",
startTime: Date(),
stopTime: nil
)
let stoppedSession = PinpointSession(
sessionId: "stopped",
startTime: Date().addingTimeInterval(-10),
stopTime: Date()
)
storage.events = [
.init(id: "1", eventType: "eventType1", eventDate: Date(), session: activeSession),
.init(id: "2", eventType: "eventType2", eventDate: Date(), session: stoppedSession)
]

_ = try? await recorder.submitAllEvents()
XCTAssertEqual(pinpointClient.putEventsCount, 1)
let input = try XCTUnwrap(pinpointClient.putEventsLastInput)
let batchItem = try XCTUnwrap(input.eventsRequest?.batchItem?.first?.value as? PinpointClientTypes.EventsBatch)
let events = try XCTUnwrap(batchItem.events)
XCTAssertEqual(events.count, 2)
XCTAssertNotNil(events["1"]?.session, "Expected session for eventType1")
XCTAssertNil(events["1"]?.session?.duration, "Expected nil session duration for eventType")
XCTAssertNotNil(events["2"]?.session, "Expected session for eventType2")
XCTAssertNotNil(events["2"]?.session?.duration, "Expected session duration for eventType2")
}

/// - Given: a event recorder with events saved in the local storage
/// - When: submitAllEvents is invoked and fails with a non-retryable error
/// - Then: the events are marked as dirty
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,8 +367,10 @@ class MockPinpointClient: PinpointClientProtocol {

var putEventsCount = 0
var putEventsResult: Result<PutEventsOutput, Error> = .failure(CancellationError())
var putEventsLastInput: PutEventsInput?
func putEvents(input: PutEventsInput) async throws -> PutEventsOutput {
putEventsCount += 1
putEventsLastInput = input
return try putEventsResult.get()
}

Expand Down
Loading