/// <summary> /// Unmarshaller the response from the service to the response class. /// </summary> /// <param name="context"></param> /// <returns></returns> public override AmazonWebServiceResponse Unmarshall(JsonUnmarshallerContext context) { ListUpdatesResponse response = new ListUpdatesResponse(); context.Read(); int targetDepth = context.CurrentDepth; while (context.ReadAtDepth(targetDepth)) { if (context.TestExpression("nextToken", targetDepth)) { var unmarshaller = StringUnmarshaller.Instance; response.NextToken = unmarshaller.Unmarshall(context); continue; } if (context.TestExpression("updateIds", targetDepth)) { var unmarshaller = new ListUnmarshaller <string, StringUnmarshaller>(StringUnmarshaller.Instance); response.UpdateIds = unmarshaller.Unmarshall(context); continue; } } return(response); }
private void PopulateListUpdates(string datasetName, long lastSyncCount, List <Record> records, string nextToken, AmazonCognitoCallback callback, object state) { { ListRecordsRequest request = new ListRecordsRequest(); //appendUserAgent(request, userAgent); request.IdentityPoolId = identityPoolId; request.IdentityId = this.GetCurrentIdentityId(); request.DatasetName = datasetName; request.LastSyncCount = lastSyncCount; // mark it large enough to reduce # of requests request.MaxResults = 1024; request.NextToken = nextToken; client.ListRecordsAsync(request, delegate(AmazonServiceResult result) { if (result.Exception != null) { AmazonMainThreadDispatcher.ExecCallback(callback, new AmazonCognitoResult(null, HandleException(result.Exception, "Failed to list records in dataset: " + datasetName), state)); return; } ListRecordsResponse listRecordsResponse = result.Response as ListRecordsResponse; foreach (Amazon.CognitoSync.Model.Record remoteRecord in listRecordsResponse.Records) { //builder.addRecord(modelToRecord(remoteRecord)); records.Add(this.ModelToRecord(remoteRecord)); } if (listRecordsResponse.NextToken == null) { DatasetUpdatesImpl updates = new DatasetUpdatesImpl( datasetName, records, listRecordsResponse.DatasetSyncCount, listRecordsResponse.SyncSessionToken, listRecordsResponse.DatasetExists, listRecordsResponse.DatasetDeletedAfterRequestedSyncCount, listRecordsResponse.MergedDatasetNames ); ListUpdatesResponse listUpdatesResponse = new ListUpdatesResponse { DatasetUpdates = updates }; AmazonMainThreadDispatcher.ExecCallback(callback, new AmazonCognitoResult(listUpdatesResponse, null, state)); return; } // update last evaluated key nextToken = listRecordsResponse.NextToken; // emulating the while loop PopulateListUpdates(datasetName, lastSyncCount, records, nextToken, callback, state); }, state); } }
private void RunSyncOperationAsync(int retry, Action <RunSyncOperationResponse> callback) { if (retry < 0) { callback(new RunSyncOperationResponse(false, null)); return; } long lastSyncCount = _local.GetLastSyncCount(GetIdentityId(), _datasetName); // if dataset is deleted locally, push it to remote if (lastSyncCount == -1) { #if DELETE_METHOD_SUPPORT _remote.DeleteDatasetAsync(_datasetName, delegate(AmazonCognitoResult result) { if (result.Exception != null) { var e = result.Exception as DataStorageException; AmazonLogging.LogError(AmazonLogging.AmazonLoggingLevel.Errors, "CognitoSyncManager", "OnSyncFailure" + e.Message); this.FireSyncFailureEvent(e); callback(new RunSyncOperationResponse(false, null)); return; } _local.PurgeDataset(GetIdentityId(), _datasetName); AmazonLogging.Log(AmazonLogging.AmazonLoggingLevel.Verbose, "CognitoSyncManager", "OnSyncSuccess: dataset delete is pushed to remote"); this.FireSyncSuccessEvent(new List <Record>()); callback(new RunSyncOperationResponse(true, null)); return; }, null); #endif // invalid scenario AmazonLogging.LogError(AmazonLogging.AmazonLoggingLevel.Critical, "CognitoSyncManager", "OnSyncFailure: DeleteDataset is an invalid operation"); FireSyncFailureEvent(new DataStorageException("DeleteDataset is an invalid operation")); callback(new RunSyncOperationResponse(false, null)); return; } // get latest modified records from remote AmazonLogging.Log(AmazonLogging.AmazonLoggingLevel.Verbose, "CognitoSyncManager", "get latest modified records since " + lastSyncCount); _remote.ListUpdatesAsync(_datasetName, lastSyncCount, delegate(AmazonCognitoResult listUpdatesResult) { RemoteDataStorage.DatasetUpdates datasetUpdates = null; if (listUpdatesResult == null || listUpdatesResult.Exception != null) { var e = listUpdatesResult.Exception as DataStorageException; AmazonLogging.LogException(AmazonLogging.AmazonLoggingLevel.Verbose, "CognitoSyncManager", e); FireSyncFailureEvent(e); callback(new RunSyncOperationResponse(false, listUpdatesResult.Exception)); return; } ListUpdatesResponse listUpdatesResponse = listUpdatesResult.Response as ListUpdatesResponse; datasetUpdates = listUpdatesResponse.DatasetUpdates; if (datasetUpdates.MergedDatasetNameList.Count != 0 && this.OnDatasetMerged != null) { bool resume = this.OnDatasetMerged(this, datasetUpdates.MergedDatasetNameList); if (resume) { this.RunSyncOperationAsync(--retry, callback); return; } else { AmazonLogging.Log(AmazonLogging.AmazonLoggingLevel.Verbose, "CognitoSyncManager", "OnSyncFailure: Manual cancel"); FireSyncFailureEvent(new DataStorageException("Manual cancel")); callback(new RunSyncOperationResponse(false, null)); return; } } // if the dataset doesn't exist or is deleted, trigger onDelete if (lastSyncCount != 0 && !datasetUpdates.Exists || datasetUpdates.Deleted && this.OnDatasetDeleted != null) { bool resume = this.OnDatasetDeleted(this); if (resume) { // remove both records and metadata _local.DeleteDataset(GetIdentityId(), _datasetName); _local.PurgeDataset(GetIdentityId(), _datasetName); AmazonLogging.Log(AmazonLogging.AmazonLoggingLevel.Verbose, "CognitoSyncManager", "OnSyncSuccess"); FireSyncSuccessEvent(new List <Record>()); callback(new RunSyncOperationResponse(true, null)); return; } else { AmazonLogging.Log(AmazonLogging.AmazonLoggingLevel.Verbose, "CognitoSyncManager", "OnSyncFailure"); FireSyncFailureEvent(new DataStorageException("Manual cancel")); callback(new RunSyncOperationResponse(false, null)); return; } } List <Record> remoteRecords = datasetUpdates.Records; if (remoteRecords.Count != 0) { // if conflict, prompt developer/user with callback List <SyncConflict> conflicts = new List <SyncConflict>(); List <Record> conflictRecords = new List <Record>(); foreach (Record remoteRecord in remoteRecords) { Record localRecord = _local.GetRecord(GetIdentityId(), _datasetName, remoteRecord.Key); // only when local is changed and its value is different if (localRecord != null && localRecord.Modified && !StringUtils.Equals(localRecord.Value, remoteRecord.Value)) { conflicts.Add(new SyncConflict(remoteRecord, localRecord)); conflictRecords.Add(remoteRecord); } } // retaining only non-conflict records remoteRecords.RemoveAll(t => conflictRecords.Contains(t)); if (conflicts.Count > 0) { AmazonLogging.Log(AmazonLogging.AmazonLoggingLevel.Verbose, "CognitoSyncManager", String.Format("{0} records in conflict!", conflicts.Count)); bool syncConflictResult = false; if (this.OnSyncConflict == null) { // delegate is not implemented so the conflict resolution is applied syncConflictResult = this.DefaultConflictResolution(conflicts); } else { syncConflictResult = this.OnSyncConflict(this, conflicts); } if (!syncConflictResult) { AmazonLogging.Log(AmazonLogging.AmazonLoggingLevel.Verbose, "CognitoSyncManager", "User cancelled conflict resolution"); callback(new RunSyncOperationResponse(false, null)); return; } } // save to local if (remoteRecords.Count > 0) { AmazonLogging.Log(AmazonLogging.AmazonLoggingLevel.Verbose, "CognitoSyncManager", String.Format("save {0} records to local", remoteRecords.Count)); _local.PutRecords(GetIdentityId(), _datasetName, remoteRecords); } // new last sync count AmazonLogging.Log(AmazonLogging.AmazonLoggingLevel.Verbose, "CognitoSyncManager", String.Format("updated sync count {0}", datasetUpdates.SyncCount)); _local.UpdateLastSyncCount(GetIdentityId(), _datasetName, datasetUpdates.SyncCount); } // push changes to remote List <Record> localChanges = this.GetModifiedRecords(); if (localChanges.Count != 0) { AmazonLogging.Log(AmazonLogging.AmazonLoggingLevel.Verbose, "CognitoSyncManager", String.Format("push {0} records to remote", localChanges.Count)); _remote.PutRecordsAsync(_datasetName, localChanges, datasetUpdates.SyncSessionToken, delegate(AmazonCognitoResult putRecordsResult) { if (putRecordsResult.Exception != null) { if (putRecordsResult.Exception.GetType() == typeof(DataConflictException)) { AmazonLogging.LogError(AmazonLogging.AmazonLoggingLevel.Warnings, "CognitoSyncManager", "Conflicts detected when pushing changes to remote: " + putRecordsResult.Exception.Message); this.RunSyncOperationAsync(--retry, callback); return; } else if (putRecordsResult.Exception.GetType() == typeof(DataStorageException)) { AmazonLogging.LogError(AmazonLogging.AmazonLoggingLevel.Verbose, "CognitoSyncManager", "OnSyncFailure" + putRecordsResult.Exception.Message); FireSyncFailureEvent(putRecordsResult.Exception); callback(new RunSyncOperationResponse(false, null)); return; } } PutRecordsResponse putRecordsResponse = putRecordsResult.Response as PutRecordsResponse; List <Record> result = putRecordsResponse.UpdatedRecords; // update local meta data _local.PutRecords(GetIdentityId(), _datasetName, result); // verify the server sync count is increased exactly by one, aka no // other updates were made during this update. long newSyncCount = 0; foreach (Record record in result) { newSyncCount = newSyncCount < record.SyncCount ? record.SyncCount : newSyncCount; } if (newSyncCount == lastSyncCount + 1) { AmazonLogging.Log(AmazonLogging.AmazonLoggingLevel.Info, "DefaultDataset", String.Format("updated sync count %d", newSyncCount)); _local.UpdateLastSyncCount(GetIdentityId(), _datasetName, newSyncCount); } AmazonLogging.Log(AmazonLogging.AmazonLoggingLevel.Verbose, "CognitoSyncManager", "OnSyncSuccess"); // call back FireSyncSuccessEvent(remoteRecords); callback(new RunSyncOperationResponse(true, null)); return; }, null); return; } AmazonLogging.Log(AmazonLogging.AmazonLoggingLevel.Verbose, "CognitoSyncManager", "OnSyncSuccess"); // call back FireSyncSuccessEvent(remoteRecords); callback(new RunSyncOperationResponse(true, null)); return; }, null); }