From 485c9da9a8edb86ff56d115b8646c0701e6f2c74 Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Tue, 5 Sep 2023 22:22:35 +0000 Subject: [PATCH 1/5] chore: release 2.17.1 [skip ci] --- .../ServiceConfiguration/AmplifyAWSServiceConfiguration.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AmplifyPlugins/Core/AWSPluginsCore/ServiceConfiguration/AmplifyAWSServiceConfiguration.swift b/AmplifyPlugins/Core/AWSPluginsCore/ServiceConfiguration/AmplifyAWSServiceConfiguration.swift index d840270246..39e6384356 100644 --- a/AmplifyPlugins/Core/AWSPluginsCore/ServiceConfiguration/AmplifyAWSServiceConfiguration.swift +++ b/AmplifyPlugins/Core/AWSPluginsCore/ServiceConfiguration/AmplifyAWSServiceConfiguration.swift @@ -16,7 +16,7 @@ import Amplify public class AmplifyAWSServiceConfiguration { /// - Tag: AmplifyAWSServiceConfiguration.amplifyVersion - public static let amplifyVersion = "2.17.0" + public static let amplifyVersion = "2.17.1" /// - Tag: AmplifyAWSServiceConfiguration.platformName public static let platformName = "amplify-swift" From 76ba8f1ead1cac4d53f313fb8d214c7bc5000551 Mon Sep 17 00:00:00 2001 From: aws-amplify-ops Date: Tue, 5 Sep 2023 22:25:43 +0000 Subject: [PATCH 2/5] chore: finalize release 2.17.1 [skip ci] --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f07d5439a6..b454fa06b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 2.17.1 (2023-09-05) + +### Bug Fixes + +- **logging**: create log file if it doesn't exist (#3202) +- **PushNotifications**: Adding missing escape hatch to the plugin (#3201) + ## 2.17.0 (2023-08-31) ### Features From a236f6c15180c961f601fb95d0092881883243a6 Mon Sep 17 00:00:00 2001 From: Tuan Pham <103537251+phantumcode@users.noreply.github.com> Date: Tue, 5 Sep 2023 16:18:45 -0700 Subject: [PATCH 3/5] test(storage): update storage integration test with longer expiration duration (#3208) --- .../AWSS3StoragePluginOptionsUsabilityTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginOptionsUsabilityTests.swift b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginOptionsUsabilityTests.swift index 34fc3e954e..192bed0f53 100644 --- a/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginOptionsUsabilityTests.swift +++ b/AmplifyPlugins/Storage/Tests/StorageHostApp/AWSS3StoragePluginIntegrationTests/AWSS3StoragePluginOptionsUsabilityTests.swift @@ -24,7 +24,7 @@ class AWSS3StoragePluginOptionsUsabilityTests: AWSS3StoragePluginTestBase { await uploadData(key: key, dataString: key) #if os(iOS) - let expires = 10 + let expires = 20 #else let expires = 1 #endif @@ -69,7 +69,7 @@ class AWSS3StoragePluginOptionsUsabilityTests: AWSS3StoragePluginTestBase { task.resume() await waitForExpectations(timeout: TestCommonConstants.networkTimeout) - try await Task.sleep(seconds: 15) + try await Task.sleep(seconds: 30) #else try await Task.sleep(seconds: 2) #endif From 659fdf994456c09621634a6dafa88ab92da43c9b Mon Sep 17 00:00:00 2001 From: Di Wu Date: Wed, 6 Sep 2023 10:05:33 -0700 Subject: [PATCH 4/5] fix(datastore): wrap failures to result when applying remote update events (#3187) --- .../ReconcileAndLocalSaveOperation.swift | 115 ++++++++---------- .../ReconcileAndLocalSaveOperationTests.swift | 78 ++++++------ 2 files changed, 85 insertions(+), 108 deletions(-) diff --git a/AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Sync/SubscriptionSync/ReconcileAndLocalSave/ReconcileAndLocalSaveOperation.swift b/AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Sync/SubscriptionSync/ReconcileAndLocalSave/ReconcileAndLocalSaveOperation.swift index d027edc0aa..9afaf2e8a1 100644 --- a/AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Sync/SubscriptionSync/ReconcileAndLocalSave/ReconcileAndLocalSaveOperation.swift +++ b/AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Sync/SubscriptionSync/ReconcileAndLocalSave/ReconcileAndLocalSaveOperation.swift @@ -264,79 +264,64 @@ class ReconcileAndLocalSaveOperation: AsynchronousOperation { return dispositions } + func applyRemoteModelsDisposition( + storageAdapter: StorageEngineAdapter, + disposition: RemoteSyncReconciler.Disposition + ) -> AnyPublisher, Never> { + let operation: Future + let mutationType: MutationEvent.MutationType + switch disposition { + case .create(let remoteModel): + operation = self.save(storageAdapter: storageAdapter, remoteModel: remoteModel) + mutationType = .create + case .update(let remoteModel): + operation = self.save(storageAdapter: storageAdapter, remoteModel: remoteModel) + mutationType = .update + case .delete(let remoteModel): + operation = self.delete(storageAdapter: storageAdapter, remoteModel: remoteModel) + mutationType = .delete + } + + return operation + .flatMap { applyResult in + self.saveMetadata(storageAdapter: storageAdapter, applyResult: applyResult, mutationType: mutationType) + } + .map {_ in Result.success(()) } + .catch { Just>(.failure($0))} + .eraseToAnyPublisher() + } + // TODO: refactor - move each the publisher constructions to its own utility method for readability of the // `switch` and a single method that you can invoke in the `map` func applyRemoteModelsDispositions( - _ dispositions: [RemoteSyncReconciler.Disposition]) -> Future { - Future { promise in - var result: Result = .failure(Self.unfulfilledDataStoreError()) - defer { - promise(result) - } - guard !self.isCancelled else { - self.log.info("\(#function) - cancelled, aborting") - result = .successfulVoid - return - } - guard let storageAdapter = self.storageAdapter else { - let error = DataStoreError.nilStorageAdapter() - self.notifyDropped(count: dispositions.count, error: error) - result = .failure(error) - return - } + _ dispositions: [RemoteSyncReconciler.Disposition] + ) -> Future { + guard !self.isCancelled else { + self.log.info("\(#function) - cancelled, aborting") + return Future { $0(.successfulVoid) } + } - guard !dispositions.isEmpty else { - result = .successfulVoid - return - } + guard let storageAdapter = self.storageAdapter else { + let error = DataStoreError.nilStorageAdapter() + self.notifyDropped(count: dispositions.count, error: error) + return Future { $0(.failure(error)) } + } - let publishers = dispositions.map { disposition -> - Publishers.FlatMap, - Future> in - - switch disposition { - case .create(let remoteModel): - let publisher = self.save(storageAdapter: storageAdapter, - remoteModel: remoteModel) - .flatMap { applyResult in - self.saveMetadata(storageAdapter: storageAdapter, - applyResult: applyResult, - mutationType: .create) - } - return publisher - case .update(let remoteModel): - let publisher = self.save(storageAdapter: storageAdapter, - remoteModel: remoteModel) - .flatMap { applyResult in - self.saveMetadata(storageAdapter: storageAdapter, - applyResult: applyResult, - mutationType: .update) - } - return publisher - case .delete(let remoteModel): - let publisher = self.delete(storageAdapter: storageAdapter, - remoteModel: remoteModel) - .flatMap { applyResult in - self.saveMetadata(storageAdapter: storageAdapter, - applyResult: applyResult, - mutationType: .delete) - } - return publisher - } - } + guard !dispositions.isEmpty else { + return Future { $0(.successfulVoid) } + } + + let publishers = dispositions.map { + applyRemoteModelsDisposition(storageAdapter: storageAdapter, disposition: $0) + } + return Future { promise in Publishers.MergeMany(publishers) .collect() - .sink( - receiveCompletion: { - if case .failure(let error) = $0 { - result = .failure(error) - } - }, - receiveValue: { _ in - result = .successfulVoid - } - ) + .sink { _ in + // This stream will never fail, as we wrapped error in the result type. + promise(.successfulVoid) + } receiveValue: { _ in } .store(in: &self.cancellables) } } diff --git a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/SubscriptionSync/ReconcileAndLocalSaveOperationTests.swift b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/SubscriptionSync/ReconcileAndLocalSaveOperationTests.swift index ac7d3d0af8..2b7d4220e6 100644 --- a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/SubscriptionSync/ReconcileAndLocalSaveOperationTests.swift +++ b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/SubscriptionSync/ReconcileAndLocalSaveOperationTests.swift @@ -832,11 +832,7 @@ class ReconcileAndLocalSaveOperationTests: XCTestCase { waitForExpectations(timeout: 1) } - func testApplyRemoteModels_saveFail() throws { - if skipBrokenTests { - throw XCTSkip("TODO: fix this test") - } - + func testApplyRemoteModels_skipFailedOperations() throws { let dispositions: [RemoteSyncReconciler.Disposition] = [.create(anyPostMutationSync), .create(anyPostMutationSync), .update(anyPostMutationSync), @@ -846,7 +842,7 @@ class ReconcileAndLocalSaveOperationTests: XCTestCase { .create(anyPostMutationSync), .update(anyPostMutationSync), .delete(anyPostMutationSync)] - let expect = expectation(description: "should fail") + let expect = expectation(description: "should complete") let expectedDeleteSuccess = expectation(description: "delete should be successful") expectedDeleteSuccess.expectedFulfillmentCount = 3 // 3 delete depositions let expectedDropped = expectation(description: "mutationEventDropped received") @@ -881,12 +877,12 @@ class ReconcileAndLocalSaveOperationTests: XCTestCase { .sink(receiveCompletion: { completion in switch completion { case .failure: - expect.fulfill() + XCTFail("Unexpected failure completion") case .finished: - XCTFail("Unexpected successfully completion") + expect.fulfill() } }, receiveValue: { _ in - XCTFail("Unexpected value received") + }).store(in: &cancellables) waitForExpectations(timeout: 1) } @@ -949,20 +945,18 @@ class ReconcileAndLocalSaveOperationTests: XCTestCase { } func testApplyRemoteModels_deleteFail() throws { - if skipBrokenTests { - throw XCTSkip("TODO: fix this test") - } - - let dispositions: [RemoteSyncReconciler.Disposition] = [.create(anyPostMutationSync), - .create(anyPostMutationSync), - .update(anyPostMutationSync), - .update(anyPostMutationSync), - .delete(anyPostMutationSync), - .delete(anyPostMutationSync), - .create(anyPostMutationSync), - .update(anyPostMutationSync), - .delete(anyPostMutationSync)] - let expect = expectation(description: "should fail") + let dispositions: [RemoteSyncReconciler.Disposition] = [ + .create(anyPostMutationSync), + .create(anyPostMutationSync), + .update(anyPostMutationSync), + .update(anyPostMutationSync), + .delete(anyPostMutationSync), + .delete(anyPostMutationSync), + .create(anyPostMutationSync), + .update(anyPostMutationSync), + .delete(anyPostMutationSync) + ] + let expect = expectation(description: "should success") let expectedCreateAndUpdateSuccess = expectation(description: "create and updates should be successful") expectedCreateAndUpdateSuccess.expectedFulfillmentCount = 6 // 3 creates and 3 updates let expectedDropped = expectation(description: "mutationEventDropped received") @@ -997,31 +991,29 @@ class ReconcileAndLocalSaveOperationTests: XCTestCase { .sink(receiveCompletion: { completion in switch completion { case .failure: - expect.fulfill() + XCTFail("Unexpected failure completion") case .finished: - XCTFail("Unexpected successfully completion") + expect.fulfill() } }, receiveValue: { _ in - XCTFail("Unexpected value received") + }).store(in: &cancellables) waitForExpectations(timeout: 1) } func testApplyRemoteModels_saveMetadataFail() throws { - if skipBrokenTests { - throw XCTSkip("TODO: fix this test") - } - - let dispositions: [RemoteSyncReconciler.Disposition] = [.create(anyPostMutationSync), - .create(anyPostMutationSync), - .update(anyPostMutationSync), - .update(anyPostMutationSync), - .delete(anyPostMutationSync), - .delete(anyPostMutationSync), - .create(anyPostMutationSync), - .update(anyPostMutationSync), - .delete(anyPostMutationSync)] - let expect = expectation(description: "should fail") + let dispositions: [RemoteSyncReconciler.Disposition] = [ + .create(anyPostMutationSync), + .create(anyPostMutationSync), + .update(anyPostMutationSync), + .update(anyPostMutationSync), + .delete(anyPostMutationSync), + .delete(anyPostMutationSync), + .create(anyPostMutationSync), + .update(anyPostMutationSync), + .delete(anyPostMutationSync) + ] + let expect = expectation(description: "should success") let expectedDropped = expectation(description: "mutationEventDropped received") expectedDropped.expectedFulfillmentCount = 9 // 1 for each of the 9 dispositions let saveResponder = SaveUntypedModelResponder { _, completion in @@ -1053,12 +1045,12 @@ class ReconcileAndLocalSaveOperationTests: XCTestCase { .sink(receiveCompletion: { completion in switch completion { case .failure: - expect.fulfill() + XCTFail("Unexpected failure completion") case .finished: - XCTFail("Unexpected successfully completion") + expect.fulfill() } }, receiveValue: { _ in - XCTFail("Unexpected value received") + }).store(in: &cancellables) waitForExpectations(timeout: 1) } From 22ea4919b69b3e0cf722ca002be5539443a2c2d0 Mon Sep 17 00:00:00 2001 From: Tuan Pham <103537251+phantumcode@users.noreply.github.com> Date: Mon, 11 Sep 2023 08:07:01 -0700 Subject: [PATCH 5/5] fix(logging): fix issue with logger namespace not being set (#3213) * fix(logging): fix issue with logger namespace not being set * chore: update unit test --- .../AWSCloudWatchLoggingCategoryClient.swift | 8 ++++++++ .../AWSCloudWatchLoggingPlugin.swift | 2 +- .../AWSCloudWatchLoggingSessionController.swift | 2 +- .../AWSCloudWatchLoggingPluginTests.swift | 17 ++++++++++++++++- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingCategoryClient.swift b/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingCategoryClient.swift index bf38758815..9416ecae34 100644 --- a/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingCategoryClient.swift +++ b/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingCategoryClient.swift @@ -105,6 +105,14 @@ final class AWSCloudWatchLoggingCategoryClient { loggersByKey = [:] } } + + func getLoggerSessionController(forCategory category: String, logLevel: LogLevel) -> AWSCloudWatchLoggingSessionController? { + let key = LoggerKey(category: category, logLevel: logLevel) + if let existing = loggersByKey[key] { + return existing + } + return nil + } } extension AWSCloudWatchLoggingCategoryClient: LoggingCategoryClientBehavior { diff --git a/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingPlugin.swift b/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingPlugin.swift index d4ab6d6e0e..8f40831cf7 100644 --- a/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingPlugin.swift +++ b/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingPlugin.swift @@ -72,7 +72,7 @@ public class AWSCloudWatchLoggingPlugin: LoggingCategoryPlugin { } public func logger(forCategory category: String, forNamespace namespace: String) -> Logger { - return loggingClient.logger(forCategory: category) + return loggingClient.logger(forCategory: category, forNamespace: namespace) } /// enable plugin diff --git a/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingSessionController.swift b/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingSessionController.swift index d665753f51..18d2483ffb 100644 --- a/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingSessionController.swift +++ b/AmplifyPlugins/Logging/Sources/AWSCloudWatchLoggingPlugin/AWSCloudWatchLoggingSessionController.swift @@ -20,13 +20,13 @@ import Network final class AWSCloudWatchLoggingSessionController { var client: CloudWatchLogsClientProtocol? + let namespace: String? private let logGroupName: String private let region: String private let localStoreMaxSizeInMB: Int private let credentialsProvider: CredentialsProvider private let authentication: AuthCategoryUserBehavior private let category: String - private let namespace: String? private var session: AWSCloudWatchLoggingSession? private var consumer: LogBatchConsumer? private let logFilter: AWSCloudWatchLoggingFilterBehavior diff --git a/AmplifyPlugins/Logging/Tests/AWSCloudWatchLoggingPluginTests/AWSCloudWatchLoggingPluginTests.swift b/AmplifyPlugins/Logging/Tests/AWSCloudWatchLoggingPluginTests/AWSCloudWatchLoggingPluginTests.swift index 4ac6cc81d1..8262d4b61c 100644 --- a/AmplifyPlugins/Logging/Tests/AWSCloudWatchLoggingPluginTests/AWSCloudWatchLoggingPluginTests.swift +++ b/AmplifyPlugins/Logging/Tests/AWSCloudWatchLoggingPluginTests/AWSCloudWatchLoggingPluginTests.swift @@ -43,6 +43,21 @@ final class AWSCloudWatchLoggingPluginTests: XCTestCase { let defaultLogger = plugin.logger(forNamespace: "test") XCTAssertEqual(defaultLogger.logLevel.rawValue, 0) - + } + + /// Given: a AWSCloudWatchLoggingPlugin + /// When: a logger is requested with a namespace + /// Then: the namespace is set in the logger session controller + func testPluginLoggerNamespace() throws { + let configuration = AWSCloudWatchLoggingPluginConfiguration(logGroupName: "testLogGroup", region: "us-east-1") + let plugin = AWSCloudWatchLoggingPlugin(loggingPluginConfiguration: configuration) + _ = plugin.logger(forCategory: "Category1") + var sessionController = plugin.loggingClient.getLoggerSessionController(forCategory: "Category1", logLevel: .error) + XCTAssertEqual(sessionController?.namespace, nil) + + _ = plugin.logger(forCategory: "Category2", forNamespace: "testNamespace") + sessionController = plugin.loggingClient.getLoggerSessionController(forCategory: "Category2", logLevel: .error) + XCTAssertEqual(sessionController?.namespace, "testNamespace") + } }