Skip to content

Commit 5482dbc

Browse files
authored
chore: kickoff release
2 parents 1f3bcfe + 887c2d9 commit 5482dbc

File tree

5 files changed

+75
-6
lines changed

5 files changed

+75
-6
lines changed

AmplifyPlugins/API/Sources/AWSAPIPlugin/AppSyncRealTimeClient/AppSyncRealTimeClient.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ actor AppSyncRealTimeClient: AppSyncRealTimeClientProtocol {
5454
self.state.value == .connected
5555
}
5656

57+
internal var numberOfSubscriptions: Int {
58+
self.subscriptions.count
59+
}
60+
5761
/**
5862
Creates a new AppSyncRealTimeClient with endpoint, requestInterceptor and webSocketClient.
5963
- Parameters:

AmplifyPlugins/API/Sources/AWSAPIPlugin/Operation/AWSGraphQLSubscriptionTaskRunner.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ public class AWSGraphQLSubscriptionTaskRunner<R: Decodable>: InternalTaskRunner,
4444
self.apiAuthProviderFactory = apiAuthProviderFactory
4545
}
4646

47+
/// When the top-level AmplifyThrowingSequence is canceled, this cancel method is invoked.
48+
/// In this situation, we need to send the disconnected event because
49+
/// the top-level AmplifyThrowingSequence is terminated immediately upon cancellation.
4750
public func cancel() {
4851
self.send(GraphQLSubscriptionEvent<R>.connection(.disconnected))
4952
Task {
@@ -210,12 +213,7 @@ final public class AWSGraphQLSubscriptionOperation<R: Decodable>: GraphQLSubscri
210213

211214
override public func cancel() {
212215
super.cancel()
213-
214-
Task { [weak self] in
215-
guard let self else {
216-
return
217-
}
218-
216+
Task {
219217
guard let appSyncRealTimeClient = self.appSyncRealTimeClient else {
220218
return
221219
}

AmplifyPlugins/API/Tests/APIHostApp/AWSAPIPluginFunctionalTests/GraphQLModelBasedTests.swift

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,65 @@ class GraphQLModelBasedTests: XCTestCase {
448448
await fulfillment(of: [progressInvoked], timeout: TestCommonConstants.networkTimeout)
449449
}
450450

451+
452+
/// Given: Several subscriptions with Amplify API plugin
453+
/// When: Cancel subscriptions
454+
/// Then: AppSync real time client automatically unsubscribe and remove the subscription
455+
func testCancelledSubscription_automaticallyUnsubscribeAndRemoved() async throws {
456+
let numberOfSubscription = 5
457+
let allSubscribedExpectation = expectation(description: "All subscriptions are subscribed")
458+
allSubscribedExpectation.expectedFulfillmentCount = numberOfSubscription
459+
460+
let subscriptions = (0..<5).map { _ in
461+
Amplify.API.subscribe(request: .subscription(of: Comment.self, type: .onCreate))
462+
}
463+
subscriptions.forEach { subscription in
464+
Task {
465+
do {
466+
for try await subscriptionEvent in subscription {
467+
switch subscriptionEvent {
468+
case .connection(let state):
469+
switch state {
470+
case .connecting:
471+
break
472+
case .connected:
473+
allSubscribedExpectation.fulfill()
474+
case .disconnected:
475+
break
476+
}
477+
case .data(let result):
478+
switch result {
479+
case .success: break
480+
case .failure(let error):
481+
XCTFail("\(error)")
482+
}
483+
}
484+
}
485+
} catch {
486+
XCTFail("Unexpected subscription failure")
487+
}
488+
}
489+
}
490+
491+
await fulfillment(of: [allSubscribedExpectation], timeout: 3)
492+
if let appSyncRealTimeClientFactory =
493+
getUnderlyingAPIPlugin()?.appSyncRealTimeClientFactory as? AppSyncRealTimeClientFactory,
494+
let appSyncRealTimeClient =
495+
await appSyncRealTimeClientFactory.apiToClientCache.values.first as? AppSyncRealTimeClient
496+
{
497+
var appSyncSubscriptions = await appSyncRealTimeClient.numberOfSubscriptions
498+
XCTAssertEqual(appSyncSubscriptions, numberOfSubscription)
499+
500+
subscriptions.forEach { $0.cancel() }
501+
try await Task.sleep(seconds: 2)
502+
appSyncSubscriptions = await appSyncRealTimeClient.numberOfSubscriptions
503+
XCTAssertEqual(appSyncSubscriptions, 0)
504+
505+
} else {
506+
XCTFail("There should be at least one AppSyncRealTimeClient instance")
507+
}
508+
}
509+
451510
// MARK: Helpers
452511

453512
func createPost(id: String, title: String) async throws -> Post? {
@@ -499,4 +558,8 @@ class GraphQLModelBasedTests: XCTestCase {
499558
throw error
500559
}
501560
}
561+
562+
func getUnderlyingAPIPlugin() -> AWSAPIPlugin? {
563+
return Amplify.API.plugins["awsAPIPlugin"] as? AWSAPIPlugin
564+
}
502565
}

AmplifyPlugins/Predictions/AWSPredictionsPlugin/Liveness/Events/LivenessEvent.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public enum LivenessEventKind {
5252
public static let serviceQuotaExceeded = Self(rawValue: "ServiceQuotaExceededException")
5353
public static let serviceUnavailable = Self(rawValue: "ServiceUnavailableException")
5454
public static let sessionNotFound = Self(rawValue: "SessionNotFoundException")
55+
public static let invalidSignature = Self(rawValue: "InvalidSignatureException")
5556
}
5657
}
5758

AmplifyPlugins/Predictions/AWSPredictionsPlugin/Liveness/SPI/AWSPredictionsPlugin+Liveness.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public struct FaceLivenessSessionError: Swift.Error, Equatable {
6666
public static let accessDenied = Self(code: 7)
6767
public static let invalidRegion = Self(code: 8)
6868
public static let invalidURL = Self(code: 9)
69+
public static let invalidSignature = Self(code: 10)
6970
}
7071

7172
extension FaceLivenessSessionError {
@@ -85,6 +86,8 @@ extension FaceLivenessSessionError {
8586
self = .serviceUnavailable
8687
case .sessionNotFound:
8788
self = .sessionNotFound
89+
case .invalidSignature:
90+
self = .invalidSignature
8891
default:
8992
self = .unknown
9093
}

0 commit comments

Comments
 (0)