Skip to content

Commit

Permalink
Merge branch 'main' into test/log_integration
Browse files Browse the repository at this point in the history
* main:
  fix(logging): fix issue with logger namespace not being set (#3213)
  fix(datastore): wrap failures to result when applying remote update events (#3187)
  test(storage): update storage integration test with longer expiration duration (#3208)
  chore: finalize release 2.17.1 [skip ci]
  chore: release 2.17.1 [skip ci]
  • Loading branch information
phantumcode committed Sep 11, 2023
2 parents 4f9edbe + 22ea491 commit ae9901d
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,79 +264,64 @@ class ReconcileAndLocalSaveOperation: AsynchronousOperation {
return dispositions
}

func applyRemoteModelsDisposition(
storageAdapter: StorageEngineAdapter,
disposition: RemoteSyncReconciler.Disposition
) -> AnyPublisher<Result<Void, DataStoreError>, Never> {
let operation: Future<ApplyRemoteModelResult, DataStoreError>
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<Result<Void, DataStoreError>>(.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<Void, DataStoreError> {
Future<Void, DataStoreError> { promise in
var result: Result<Void, DataStoreError> = .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<Void, DataStoreError> {
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<Void, DataStoreError>,
Future<ReconcileAndLocalSaveOperation.ApplyRemoteModelResult, DataStoreError>> 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)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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")
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down

0 comments on commit ae9901d

Please sign in to comment.