Skip to content

Commit

Permalink
Merge branch 'main' into feat/api-breakage-checker
Browse files Browse the repository at this point in the history
  • Loading branch information
rozaychen committed Jul 17, 2024
2 parents 421d7a4 + 151fac5 commit 2c8441f
Show file tree
Hide file tree
Showing 34 changed files with 268 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ import Foundation
/// directly by host applications. The behavior of this may change without warning.
public enum ModelAssociation {
case hasMany(associatedFieldName: String?, associatedFieldNames: [String] = [])
case hasOne(associatedFieldName: String?, targetNames: [String])
case hasOne(associatedFieldName: String?, associatedFieldNames: [String] = [], targetNames: [String])
case belongsTo(associatedFieldName: String?, targetNames: [String])

public static let belongsTo: ModelAssociation = .belongsTo(associatedFieldName: nil, targetNames: [])
Expand All @@ -108,14 +108,20 @@ public enum ModelAssociation {
)
}

@available(*, deprecated, message: "Use hasOne(associatedWith:targetNames:)")
@available(*, deprecated, message: "Use hasOne(associatedWith:associatedFields:targetNames:)")
public static func hasOne(associatedWith: CodingKey?, targetName: String? = nil) -> ModelAssociation {
let targetNames = targetName.map { [$0] } ?? []
return .hasOne(associatedWith: associatedWith, targetNames: targetNames)
}

public static func hasOne(associatedWith: CodingKey?, targetNames: [String] = []) -> ModelAssociation {
return .hasOne(associatedFieldName: associatedWith?.stringValue, targetNames: targetNames)
public static func hasOne(
associatedWith: CodingKey? = nil,
associatedFields: [CodingKey] = [],
targetNames: [String] = []) -> ModelAssociation {
return .hasOne(
associatedFieldName: associatedWith?.stringValue,
associatedFieldNames: associatedFields.map { $0.stringValue },
targetNames: targetNames)
}

@available(*, deprecated, message: "Use belongsTo(associatedWith:targetNames:)")
Expand Down Expand Up @@ -254,7 +260,7 @@ extension ModelField {
let associatedModel = requiredAssociatedModelName
switch association {
case .belongsTo(let associatedKey, _),
.hasOne(let associatedKey, _),
.hasOne(let associatedKey, _, _),
.hasMany(let associatedKey, _):
// TODO handle modelName casing (convert to camelCase)
let key = associatedKey ?? associatedModel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,21 @@ public enum ModelFieldDefinition {
association: .hasOne(associatedWith: associatedKey, targetNames: targetNames))
}

public static func hasOne(_ key: CodingKey,
is nullability: ModelFieldNullability = .required,
isReadOnly: Bool = false,
ofType type: Model.Type,
associatedFields associatedKeys: [CodingKey],
targetNames: [String] = []) -> ModelFieldDefinition {
return .field(key,
is: nullability,
isReadOnly: isReadOnly,
ofType: .model(type: type),
association: .hasOne(associatedWith: associatedKeys.first,
associatedFields: associatedKeys,
targetNames: targetNames))
}

public static func belongsTo(_ key: CodingKey,
is nullability: ModelFieldNullability = .required,
isReadOnly: Bool = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ public class AppSyncListProvider<Element: Model>: ModelListProvider {
}
let defaultFieldName = modelSchema.name.camelCased() + field.pascalCased() + "Id"
switch modelField.association {
case .belongsTo(_, let targetNames), .hasOne(_, let targetNames):
case .belongsTo(_, let targetNames), .hasOne(_, _, let targetNames):
guard !targetNames.isEmpty else {
return [defaultFieldName]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ extension Customer4 {
model.fields(
.field(customer4.id, is: .required, ofType: .string),
.field(customer4.name, is: .optional, ofType: .string),
.hasOne(customer4.activeCart, is: .optional, ofType: Cart4.self, associatedWith: Cart4.keys.customer),
.hasOne(customer4.activeCart, is: .optional, ofType: Cart4.self, associatedFields: [Cart4.keys.customer]),
.field(customer4.createdAt, is: .optional, isReadOnly: true, ofType: .dateTime),
.field(customer4.updatedAt, is: .optional, isReadOnly: true, ofType: .dateTime)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,14 @@ class ShowHostedUISignOut: NSObject, Action {

guard let environment = environment as? AuthEnvironment,
let hostedUIEnvironment = environment.hostedUIEnvironment else {
let message = AuthPluginErrorConstants.configurationError
let error = AuthenticationError.configuration(message: message)
let error = HostedUIError.pluginConfiguration(AuthPluginErrorConstants.configurationError)
await sendEvent(with: error, dispatcher: dispatcher, environment: environment)
return
}
let hostedUIConfig = hostedUIEnvironment.configuration

guard let callbackURL = URL(string: hostedUIConfig.oauth.signOutRedirectURI),
let callbackURLScheme = callbackURL.scheme else {
let error = AuthenticationError.configuration(message: "Callback URL could not be retrieved")
await sendEvent(with: error, dispatcher: dispatcher, environment: environment)
await sendEvent(with: HostedUIError.signOutRedirectURI, dispatcher: dispatcher, environment: environment)
return
}

Expand All @@ -48,13 +45,7 @@ class ShowHostedUISignOut: NSObject, Action {
callbackScheme: callbackURLScheme,
inPrivate: false,
presentationAnchor: signOutEvent.presentationAnchor)

await sendEvent(with: nil, dispatcher: dispatcher, environment: environment)

} catch HostedUIError.signOutURI {
let error = AuthenticationError.configuration(message: "Could not create logout URL")
await sendEvent(with: error, dispatcher: dispatcher, environment: environment)
return
} catch {
self.logVerbose("\(#fileID) Received error \(error)", environment: environment)
await sendEvent(with: error, dispatcher: dispatcher, environment: environment)
Expand All @@ -65,32 +56,33 @@ class ShowHostedUISignOut: NSObject, Action {
dispatcher: EventDispatcher,
environment: Environment) async {

var hostedUIError: AWSCognitoHostedUIError?
if let hostedUIInternalError = error as? HostedUIError,
case .cancelled = hostedUIInternalError {
let event = SignOutEvent(eventType: .userCancelled)
self.logVerbose("\(#fileID) Sending event \(event.type)", environment: environment)
await dispatcher.send(event)
return
}

if let error = error as? AuthErrorConvertible {
hostedUIError = AWSCognitoHostedUIError(error: error.authError)
let event: SignOutEvent
if let hostedUIInternalError = error as? HostedUIError {
event = SignOutEvent(eventType: .hostedUISignOutError(hostedUIInternalError))
} else if let error = error as? AuthErrorConvertible {
event = getEvent(for: AWSCognitoHostedUIError(error: error.authError))
} else if let error = error {
let serviceError = AuthError.service("HostedUI failed with error",
"", error)
hostedUIError = AWSCognitoHostedUIError(error: serviceError)
let serviceError = AuthError.service(
"HostedUI failed with error",
"",
error
)
event = getEvent(for: AWSCognitoHostedUIError(error: serviceError))
} else {
event = getEvent(for: nil)
}
let event: SignOutEvent
self.logVerbose("\(#fileID) Sending event \(event.type)", environment: environment)
await dispatcher.send(event)
}

private func getEvent(for hostedUIError: AWSCognitoHostedUIError?) -> SignOutEvent {
if self.signOutEvent.globalSignOut {
event = SignOutEvent(eventType: .signOutGlobally(self.signInData,
return SignOutEvent(eventType: .signOutGlobally(self.signInData,
hostedUIError: hostedUIError))
} else {
event = SignOutEvent(eventType: .revokeToken(self.signInData,
return SignOutEvent(eventType: .revokeToken(self.signInData,
hostedUIError: hostedUIError))
}
self.logVerbose("\(#fileID) Sending event \(event.type)", environment: environment)
await dispatcher.send(event)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,23 +66,21 @@ class FetchAuthSessionOperationHelper: DefaultLogger {
authStateMachine: AuthStateMachine,
forceRefresh: Bool) async throws -> AuthSession {

var event: AuthorizationEvent
if forceRefresh || !credentials.areValid() {
if case .identityPoolWithFederation(
let federatedToken,
let identityId,
_
) = credentials {
event = AuthorizationEvent(
eventType: .startFederationToIdentityPool(federatedToken, identityId)
)
} else {
var event: AuthorizationEvent
switch credentials {
case .identityPoolWithFederation(let federatedToken, let identityId, _):
event = AuthorizationEvent(eventType: .startFederationToIdentityPool(federatedToken, identityId))
case .noCredentials:
event = AuthorizationEvent(eventType: .fetchUnAuthSession)
case .userPoolOnly, .identityPoolOnly, .userPoolAndIdentityPool:
event = AuthorizationEvent(eventType: .refreshSession(forceRefresh))
}
await authStateMachine.send(event)
return try await listenForSession(authStateMachine: authStateMachine)
} else {
return credentials.cognitoSession
}
return credentials.cognitoSession
}

func listenForSession(authStateMachine: AuthStateMachine) async throws -> AuthSession {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ enum HostedUIError: Error {

case signOutURI

case signOutRedirectURI

case proofCalculation

case codeValidation
Expand All @@ -23,6 +25,8 @@ enum HostedUIError: Error {

case serviceMessage(String)

case pluginConfiguration(String)

case cancelled

case invalidContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,30 @@ import Amplify

enum SignOutError: Error {

case userCancelled
case hostedUI(HostedUIError)

case localSignOut
}

extension SignOutError: AuthErrorConvertible {
var authError: AuthError {
switch self {
case .userCancelled:
return AuthError.service("", "", AWSCognitoAuthError.userCancelled)
case .hostedUI(let error):
return error.authError
case .localSignOut:
return AuthError.unknown("", nil)
}
}
}

extension SignOutError: Equatable {
static func == (lhs: SignOutError, rhs: SignOutError) -> Bool {
switch (lhs, rhs) {
case (.hostedUI, .hostedUI),
(.localSignOut, .localSignOut):
return true
default:
return false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ struct SignOutEvent: StateMachineEvent {

case signedOutFailure(AuthenticationError)

case userCancelled
case hostedUISignOutError(HostedUIError)
}

let id: String
Expand All @@ -66,8 +66,8 @@ struct SignOutEvent: StateMachineEvent {
return "SignOutEvent.globalSignOutError"
case .signOutGuest:
return "SignOutEvent.signOutGuest"
case .userCancelled:
return "SignOutEvent.userCancelled"
case .hostedUISignOutError:
return "SignOutEvent.hostedUISignOutError"
}
}

Expand All @@ -94,7 +94,7 @@ extension SignOutEvent.EventType: Equatable {
(.signedOutFailure, .signedOutFailure),
(.globalSignOutError, .globalSignOutError),
(.signOutGuest, .signOutGuest),
(.userCancelled, .userCancelled):
(.hostedUISignOutError, .hostedUISignOutError):
return true
default:
return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,15 @@ extension HostedUIError: AuthErrorConvertible {
AuthPluginErrorConstants.hostedUITokenURI.recoverySuggestion)

case .signOutURI:
return .service(
return .configuration(
AuthPluginErrorConstants.hostedUISignOutURI.errorDescription,
AuthPluginErrorConstants.hostedUISignOutURI.recoverySuggestion)

case .signOutRedirectURI:
return .configuration(
AuthPluginErrorConstants.hostedUISignOutRedirectURI.errorDescription,
AuthPluginErrorConstants.hostedUISignOutRedirectURI.recoverySuggestion)

case .proofCalculation:
return .invalidState(
AuthPluginErrorConstants.hostedUIProofCalculation.errorDescription,
Expand Down Expand Up @@ -107,11 +112,15 @@ extension HostedUIError: AuthErrorConvertible {
case .unableToStartASWebAuthenticationSession:
return .service(
AuthPluginErrorConstants.hostedUIUnableToStartASWebAuthenticationSession.errorDescription,
AuthPluginErrorConstants.hostedUIUnableToStartASWebAuthenticationSession.recoverySuggestion)
AuthPluginErrorConstants.hostedUIUnableToStartASWebAuthenticationSession.recoverySuggestion,
AWSCognitoAuthError.errorLoadingUI)

case .serviceMessage(let message):
return .service(message, AuthPluginErrorConstants.serviceError)

case .pluginConfiguration(let message):
return .configuration(message, AuthPluginErrorConstants.configurationError)

case .unknown:
return .unknown("WebUI signIn encountered an unknown error", nil)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,9 @@ extension SignOutState {
newState: SignOutState.revokingToken,
actions: [action]
)
case .userCancelled:
case .hostedUISignOutError(let error):
let action = CancelSignOut(signedInData: signedInData)
return .init(newState: .error(.userCancelled), actions: [action])
return .init(newState: .error(.hostedUI(error)), actions: [action])
default:
return .from(oldState)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ enum AuthPluginErrorConstants {
"SignOut URI could not be created",
"Check the configuration to make sure that HostedUI related information are present")

static let hostedUISignOutRedirectURI: AuthPluginErrorString = (
"Callback URL could not be retrieved",
"Check the configuration to make sure that HostedUI related information are present")

static let hostedUIProofCalculation: AuthPluginErrorString = (
"Proof calculation failed",
"Reach out with amplify team via github to raise an issue")
Expand Down
Loading

0 comments on commit 2c8441f

Please sign in to comment.