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

fix(analytics): update error handling #3261

Merged
merged 1 commit into from
Oct 2, 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

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import Foundation
import Amplify
import AWSPinpoint
import ClientRuntime

extension AWSPinpoint.BadRequestException: AnalyticsErrorConvertible {
var analyticsError: AnalyticsError {
.unknown(properties.message ?? "", self)
}
}

extension AWSPinpoint.ForbiddenException: AnalyticsErrorConvertible {
var analyticsError: AnalyticsError {
.unknown(properties.message ?? "", self)
}
}

extension AWSPinpoint.InternalServerErrorException: AnalyticsErrorConvertible {
var analyticsError: AnalyticsError {
.unknown(properties.message ?? "", self)
}
}

extension AWSPinpoint.MethodNotAllowedException: AnalyticsErrorConvertible {
var analyticsError: AnalyticsError {
.unknown(properties.message ?? "", self)
}
}

extension AWSPinpoint.NotFoundException: AnalyticsErrorConvertible {
var analyticsError: AnalyticsError {
.unknown(properties.message ?? "", self)
}
}

extension AWSPinpoint.PayloadTooLargeException: AnalyticsErrorConvertible {
var analyticsError: AnalyticsError {
.unknown(properties.message ?? "", self)
}
}

extension AWSPinpoint.TooManyRequestsException: AnalyticsErrorConvertible {
var analyticsError: AnalyticsError {
.unknown(properties.message ?? "", self)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import Foundation
import Amplify

protocol AnalyticsErrorConvertible {
var analyticsError: AnalyticsError { get }
}

extension AnalyticsError: AnalyticsErrorConvertible {
var analyticsError: AnalyticsError {
self
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,18 @@
// SPDX-License-Identifier: Apache-2.0
//

import Amplify
import AWSPinpoint
import ClientRuntime
import Foundation
import Amplify
import AwsCommonRuntimeKit

class AnalyticsErrorHelper {
enum AnalyticsErrorHelper {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious why this was changed from class to enum?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I must've instinctively made that change without realizing it.
As a simple namespace, it should be an enum. That said, I don't mind reverting as it doesn't really belong in this PR.

static func getDefaultError(_ error: Error) -> AnalyticsError {
if let sdkError = error as? SdkError<PutEventsOutputError>{
return sdkError.analyticsError
switch error {
case let error as AnalyticsErrorConvertible:
return error.analyticsError
default:
return getDefaultError(error as NSError)
}

if let analyticsError = error as? AnalyticsError {
return analyticsError
}

return getDefaultError(error as NSError)
}

static func getDefaultError(_ error: NSError) -> AnalyticsError {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import Foundation
import Amplify
@_spi(InternalAWSPinpoint) import InternalAWSPinpoint
import AwsCommonRuntimeKit

extension CommonRunTimeError: AnalyticsErrorConvertible {
var analyticsError: AnalyticsError {
switch self {
case .crtError(let crtError):
let errorDescription = isConnectivityError
? AWSPinpointErrorConstants.deviceOffline.errorDescription
: crtError.message
return .unknown(errorDescription, self)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import ClientRuntime
import Foundation

extension ClientError {
// TODO: Should some of these really be retried?
var isRetryable: Bool {
switch self {
case .authError:
return true
case .dataNotFound:
return true
case .pathCreationFailed:
return true
case .queryItemCreationFailed:
return true
case .serializationFailed:
return false
case .unknownError:
return true
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import Amplify
import AWSPinpoint
import ClientRuntime
import enum AwsCommonRuntimeKit.CommonRunTimeError
import Foundation

/// AnalyticsEventRecording saves and submits pinpoint events
Expand Down Expand Up @@ -206,42 +207,30 @@ class EventRecorder: AnalyticsEventRecording {
}

private func isErrorRetryable(_ error: Error) -> Bool {
switch error {
case let clientError as ClientError:
return clientError.isRetryable
case let putEventsOutputError as PutEventsOutputError:
return putEventsOutputError.isRetryable
case let sdkPutEventsOutputError as SdkError<PutEventsOutputError>:
return sdkPutEventsOutputError.isRetryable
case let sdkError as SdkError<Error>:
return sdkError.isRetryable
default:
guard case let modeledError as ModeledError = error else {
return false
}
return type(of: modeledError).isRetryable
}

private func errorDescription(_ error: Error) -> String {
switch error {
case let sdkPutEventsOutputError as SdkError<PutEventsOutputError>:
return sdkPutEventsOutputError.errorDescription
case let sdkError as SdkError<Error>:
return sdkError.errorDescription
case let error as ModeledErrorDescribable:
return error.errorDescription
case let error as CommonRunTimeError:
switch error {
case .crtError(let crtError):
return crtError.message
}
default:
return error.localizedDescription
}
}

private func isConnectivityError(_ error: Error) -> Bool {
switch error {
case let clientError as ClientError:
if case .networkError(_) = clientError {
return true
}
return false
case let sdkPutEventsOutputError as SdkError<PutEventsOutputError>:
return sdkPutEventsOutputError.isConnectivityError
case let sdkError as SdkError<Error>:
return sdkError.isConnectivityError
case let error as CommonRunTimeError:
return error.isConnectivityError
case let error as NSError:
let networkErrorCodes = [
NSURLErrorCannotFindHost,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,14 @@ struct PinpointContextConfiguration {
/// The Pinpoint region
let region: String
/// Used to retrieve the proper AWSCredentials when creating the PinpointCLient
let credentialsProvider: CredentialsProvider
let credentialsProvider: CredentialsProviding
/// Indicates if the App is in Debug or Release build. Defaults to `false`
/// Setting this flag to true will set the Endpoint Profile to have a channel type of "APNS_SANDBOX".
let isDebug: Bool

init(appId: String,
region: String,
credentialsProvider: CredentialsProvider,
credentialsProvider: CredentialsProviding,
isDebug: Bool = false) {
self.appId = appId
self.region = region
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import Amplify
import AwsCIo
import AwsCHttp
import AwsCommonRuntimeKit
import AWSPinpoint
import ClientRuntime
import Foundation

@_spi(InternalAWSPinpoint)
extension CommonRunTimeError {
static let connectivityErrorCodes: Set<UInt32> = [
AWS_ERROR_HTTP_CONNECTION_CLOSED.rawValue,
AWS_ERROR_HTTP_SERVER_CLOSED.rawValue,
AWS_IO_DNS_INVALID_NAME.rawValue,
AWS_IO_DNS_NO_ADDRESS_FOR_HOST.rawValue,
AWS_IO_DNS_QUERY_FAILED.rawValue,
AWS_IO_SOCKET_CONNECT_ABORTED.rawValue,
AWS_IO_SOCKET_CONNECTION_REFUSED.rawValue,
AWS_IO_SOCKET_CLOSED.rawValue,
AWS_IO_SOCKET_NETWORK_DOWN.rawValue,
AWS_IO_SOCKET_NO_ROUTE_TO_HOST.rawValue,
AWS_IO_SOCKET_NOT_CONNECTED.rawValue,
AWS_IO_SOCKET_TIMEOUT.rawValue,
AWS_IO_TLS_NEGOTIATION_TIMEOUT.rawValue,
UInt32(AWS_HTTP_STATUS_CODE_408_REQUEST_TIMEOUT.rawValue)
]

public var isConnectivityError: Bool {
switch self {
case .crtError(let error):
Self.connectivityErrorCodes.contains(UInt32(error.code))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import AWSPinpoint
@_spi(FoundationClientEngine) import AWSPluginsCore

extension PinpointClient {
convenience init(region: String, credentialsProvider: CredentialsProvider) throws {
convenience init(region: String, credentialsProvider: CredentialsProviding) throws {
// TODO: FrameworkMetadata Replacement
let configuration = try PinpointClientConfiguration(
credentialsProvider: credentialsProvider,
frameworkMetadata: AmplifyAWSServiceConfiguration.frameworkMetaData(),
region: region
region: region,
credentialsProvider: credentialsProvider
)
#if os(iOS) || os(macOS) // no-op
#else
Expand Down
Loading