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

Add AppSyncClient.queuedMutationCount property #198

Merged
merged 5 commits into from
Mar 11, 2019
Merged
Show file tree
Hide file tree
Changes from 4 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
12 changes: 12 additions & 0 deletions AWSAppSyncClient.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@
FA0C12BB21CD308A00B438CB /* AWSAppSyncClientConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA0C12B921CD2FFB00B438CB /* AWSAppSyncClientConfiguration.swift */; };
FA0C12BF21CD360B00B438CB /* AWSAppSyncAuthType.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA0C12BE21CD360B00B438CB /* AWSAppSyncAuthType.swift */; };
FA0C12C121CD3BB200B438CB /* BasicAWSAPIKeyAuthProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA0C12C021CD3BB200B438CB /* BasicAWSAPIKeyAuthProvider.swift */; };
FA0D825422307BA400E0EA82 /* AWSAppSyncSubscriptionWatcherStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA0D825322307BA400E0EA82 /* AWSAppSyncSubscriptionWatcherStatus.swift */; };
FA0D82582230D0AF00E0EA82 /* AWSAppSyncSubscriptionError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA0D82572230D0AF00E0EA82 /* AWSAppSyncSubscriptionError.swift */; };
FA0D825C22317C7900E0EA82 /* SubscriptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA0D825B22317C7900E0EA82 /* SubscriptionTests.swift */; };
FA1A620C21E6533A00AA54D0 /* AWSAppSyncRetryHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA1A620B21E6533A00AA54D0 /* AWSAppSyncRetryHandlerTests.swift */; };
FA2B4598221DDF2C00F68E6C /* CachePersistenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA2B4597221DDF2C00F68E6C /* CachePersistenceTests.swift */; };
FA2B459A221F436400F68E6C /* MutationQueuePerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA2B4599221F436400F68E6C /* MutationQueuePerformanceTests.swift */; };
Expand Down Expand Up @@ -444,6 +447,9 @@
FA0C12BE21CD360B00B438CB /* AWSAppSyncAuthType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSAppSyncAuthType.swift; sourceTree = "<group>"; };
FA0C12C021CD3BB200B438CB /* BasicAWSAPIKeyAuthProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasicAWSAPIKeyAuthProvider.swift; sourceTree = "<group>"; };
FA0C12C721CD96BF00B438CB /* AWSAppSyncClientConfigurationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSAppSyncClientConfigurationTests.swift; sourceTree = "<group>"; };
FA0D825322307BA400E0EA82 /* AWSAppSyncSubscriptionWatcherStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSAppSyncSubscriptionWatcherStatus.swift; sourceTree = "<group>"; };
FA0D82572230D0AF00E0EA82 /* AWSAppSyncSubscriptionError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSAppSyncSubscriptionError.swift; sourceTree = "<group>"; };
FA0D825B22317C7900E0EA82 /* SubscriptionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionTests.swift; sourceTree = "<group>"; };
FA1A620B21E6533A00AA54D0 /* AWSAppSyncRetryHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSAppSyncRetryHandlerTests.swift; sourceTree = "<group>"; };
FA2B4597221DDF2C00F68E6C /* CachePersistenceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CachePersistenceTests.swift; sourceTree = "<group>"; };
FA2B4599221F436400F68E6C /* MutationQueuePerformanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MutationQueuePerformanceTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -776,7 +782,9 @@
FAAACC2521DD7BC300D24B37 /* AWSAppSyncMutations.swift */,
FAF5D78C21D3F04E00FC04D2 /* AWSAppSyncServiceConfig.swift */,
FA3E128421D5297500F2D19A /* AWSAppSyncServiceConfigError.swift */,
FA0D82572230D0AF00E0EA82 /* AWSAppSyncSubscriptionError.swift */,
1729A0CF1FA1365900F88594 /* AWSAppSyncSubscriptionWatcher.swift */,
FA0D825322307BA400E0EA82 /* AWSAppSyncSubscriptionWatcherStatus.swift */,
DF9468DA20E1CA4A00E40482 /* AWSNetworkTransport.swift */,
17A7F8C91FB8BCBC008D1393 /* AWSS3ObjectProtocol.swift */,
CCEF79E021DE7FF7004AD64D /* Deprecations+Removals.swift */,
Expand Down Expand Up @@ -885,6 +893,7 @@
17664128214F6732003AE269 /* AWSAppSyncAPIKeyAuthTests.swift */,
174F80AE2109229C00775D0D /* AWSAppSyncCognitoAuthTests.swift */,
FA8C62C421D6E41600FF9924 /* Info.plist */,
FA0D825B22317C7900E0EA82 /* SubscriptionTests.swift */,
FAA83BF7220A6AB40029FF7B /* testS3Object.jpg */,
);
path = AWSAppSyncIntegrationTests;
Expand Down Expand Up @@ -1568,6 +1577,7 @@
17E009C61FCAB234005031DB /* GraphQLSelectionSet.swift in Sources */,
FAFB258722051B2100068B38 /* AWSAppSyncClientInfo.swift in Sources */,
17E009C81FCAB234005031DB /* Locking.swift in Sources */,
FA0D825422307BA400E0EA82 /* AWSAppSyncSubscriptionWatcherStatus.swift in Sources */,
FA3E128321D5290900F2D19A /* AWSAppSyncClientInfoError.swift in Sources */,
FABD70692203C17A00C99B47 /* AWSAppSyncCache.swift in Sources */,
174F80922107E74F00775D0D /* AWSMQTTDecoder.m in Sources */,
Expand All @@ -1576,6 +1586,7 @@
FA0C12BB21CD308A00B438CB /* AWSAppSyncClientConfiguration.swift in Sources */,
178B31081FCDB34100EA4619 /* AWSAppSyncClientConflictResolutionExtensions.swift in Sources */,
1729A0D01FA1365900F88594 /* AWSAppSyncSubscriptionWatcher.swift in Sources */,
FA0D82582230D0AF00E0EA82 /* AWSAppSyncSubscriptionError.swift in Sources */,
FAAACC2421DD7AC600D24B37 /* InternalS3ObjectDetails.swift in Sources */,
17E009D91FCAB234005031DB /* JSONStandardTypeConversions.swift in Sources */,
FA0C12C121CD3BB200B438CB /* BasicAWSAPIKeyAuthProvider.swift in Sources */,
Expand Down Expand Up @@ -1635,6 +1646,7 @@
FA902D1021D97EB100C4052F /* AWSAppSyncCognitoAuthTests.swift in Sources */,
FA902D1321D97EC500C4052F /* SubscriptionStressTestHelper.swift in Sources */,
FA902D0F21D97EAD00C4052F /* AWSAppSyncAPIKeyAuthTests.swift in Sources */,
FA0D825C22317C7900E0EA82 /* SubscriptionTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
42 changes: 22 additions & 20 deletions AWSAppSyncClient/AWSAppSyncClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import AWSCore

public typealias SubscriptionResultHandler<Operation: GraphQLSubscription> = (_ result: GraphQLResult<Operation.Data>?, _ transaction: ApolloStore.ReadWriteTransaction?, _ error: Error?) -> Void

public typealias SubscriptionStatusChangeHandler = (AWSAppSyncSubscriptionWatcherStatus) -> Void

public typealias DeltaQueryResultHandler<Operation: GraphQLQuery> = (_ result: GraphQLResult<Operation.Data>?, _ transaction: ApolloStore.ReadWriteTransaction?, _ error: Error?) -> Void

public typealias OptimisticResponseBlock = (ApolloStore.ReadWriteTransaction?) -> Void
Expand All @@ -17,23 +19,6 @@ public typealias MutationConflictHandler<Mutation: GraphQLMutation> = (_ serverS

internal let NoOpOperationString = "No-op"

public struct AWSAppSyncSubscriptionError: Error, LocalizedError {
let additionalInfo: String?
let errorDetails: [String: String]?

public var errorDescription: String? {
return additionalInfo ?? "Unable to start subscription."
}

public var recoverySuggestion: String? {
return errorDetails?["recoverySuggestion"]
}

public var failureReason: String? {
return errorDetails?["failureReason"]
}
}

/// Delegates will be notified when a mutation is performed from the `mutationCallback`. This pattern is necessary
/// in order to provide notifications of mutations which are performed after an app restart and the initial callback
/// context has been lost.
Expand All @@ -54,6 +39,17 @@ public class AWSAppSyncClient {
public var offlineMutationDelegate: AWSAppSyncOfflineMutationDelegate?
private var mutationQueue: AWSPerformMutationQueue!

/// The count of Mutation operations queued for sending to the backend.
///
/// AppSyncClient processes both offline and online mutations, and mutations are queued for processing even while
/// the client is offline, so this count represents a good measure of the number of mutations that have yet to be
/// successfully sent to the service, regardless of the state of the network.
///
/// This value is `nil` if the mutationQueue cannot be accessed (e.g., has not finished initializing).
public var queuedMutationCount: Int? {
return mutationQueue?.operationQueueCount
}

private var connectionStateChangeHandler: ConnectionStateChangeHandler?
private var autoSubmitOfflineMutations: Bool = false
private var appSyncMQTTClient = AppSyncMQTTClient()
Expand All @@ -76,7 +72,6 @@ public class AWSAppSyncClient {

self.autoSubmitOfflineMutations = appSyncConfig.autoSubmitOfflineMutations
self.store = appSyncConfig.store
self.appSyncMQTTClient.allowCellularAccess = appSyncConfig.allowsCellularAccess
self.presignedURLClient = appSyncConfig.presignedURLClient
self.s3ObjectManager = appSyncConfig.s3ObjectManager
self.subscriptionMetadataCache = appSyncConfig.subscriptionMetadataCache
Expand Down Expand Up @@ -155,14 +150,18 @@ public class AWSAppSyncClient {
return apolloClient!.watch(query: query, cachePolicy: cachePolicy, queue: queue, resultHandler: resultHandler)
}

public func subscribe<Subscription: GraphQLSubscription>(subscription: Subscription, queue: DispatchQueue = DispatchQueue.main, resultHandler: @escaping SubscriptionResultHandler<Subscription>) throws -> AWSAppSyncSubscriptionWatcher<Subscription>? {
public func subscribe<Subscription: GraphQLSubscription>(subscription: Subscription,
queue: DispatchQueue = DispatchQueue.main,
statusChangeHandler: SubscriptionStatusChangeHandler? = nil,
resultHandler: @escaping SubscriptionResultHandler<Subscription>) throws -> AWSAppSyncSubscriptionWatcher<Subscription>? {

return AWSAppSyncSubscriptionWatcher(client: self.appSyncMQTTClient,
httpClient: self.httpTransport!,
store: self.store!,
subscriptionsQueue: self.subscriptionsQueue,
subscription: subscription,
handlerQueue: queue,
statusChangeHandler: statusChangeHandler,
resultHandler: resultHandler)
}

Expand All @@ -174,11 +173,14 @@ public class AWSAppSyncClient {
subscriptionsQueue: self.subscriptionsQueue,
subscription: subscription,
handlerQueue: queue,
statusChangeHandler: nil,
connectedCallback: connectCallback,
resultHandler: resultHandler)
}

/// Performs a mutation by sending it to the server.
/// Performs a mutation by sending it to the server. Internally, these mutations are added to a queue and performed
/// serially, in first-in, first-out order. Clients can inspect the size of the queue with the `queuedMutationCount`
/// property.
///
/// - Parameters:
/// - mutation: The mutation to perform.
Expand Down
97 changes: 97 additions & 0 deletions AWSAppSyncClient/AWSAppSyncSubscriptionError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// Licensed under the Amazon Software License
// http://aws.amazon.com/asl/
//

import Foundation

public enum AWSAppSyncSubscriptionError: Error, LocalizedError {
/// The underlying MQTT client reported a status of "connectionError"
case connectionError

/// The underlying MQTT client reported a status of "connectionRefused"
case connectionRefused

/// The underlying MQTT client reported a status of "disconnected"
case disconnected

/// An error occurred parsing the subscription message received from the service
case messageCallbackError(String)

/// Some other error occurred. See associated value for details
case other(Error)

/// An error occurred parsing the published subscription message
case parseError(Error)

/// The underlying MQTT client reported a status of "protocolError"
case protocolError

/// An error occurred while making the initial subscription request to AppSync, parsing its response, or
/// evaluating the response's subscription info payload
case setupError(String)

/// The underlying MQTT client reported a status of "unknown"
case unknownMQTTConnectionStatus

public var errorDescription: String? {
switch self {
case .messageCallbackError(let message):
return message
case .other(let error):
return error.localizedDescription
case .parseError(let error):
return error.localizedDescription
case .setupError(let message):
return message
case .unknownMQTTConnectionStatus:
return "MQTT status unknown"
default:
return "Subscription Terminated."
}
}

public var recoverySuggestion: String? {
switch self {
case .other(let error as NSError):
return error.localizedRecoverySuggestion
case .parseError, .unknownMQTTConnectionStatus:
return nil
default:
return "Restart subscription request."
}
}

public var failureReason: String? {
switch self {
case .other(let error as NSError):
return error.localizedFailureReason
case .parseError, .unknownMQTTConnectionStatus:
return nil
case .setupError(let message):
return message
default:
return "Disconnected from service."
}
}
}

extension AWSAppSyncSubscriptionError {
static func from(status: AWSIoTMQTTStatus) -> AWSAppSyncSubscriptionError? {
switch status {
case .connectionError:
return .connectionError
case .connectionRefused:
return .connectionRefused
case .disconnected:
return .disconnected
case .protocolError:
return .protocolError
case .unknown:
return .unknownMQTTConnectionStatus
default:
return nil
}
}
}
Loading