handle method
Implementation
Future<DVOfflineReplayResult> handle(Object? body) async {
if (body is! Map<Object?, Object?>) {
return const DVOfflineReplayResult(400, 'not a replay request');
}
final Object? model = body['model'];
final Object? mutations = body['mutations'];
if (model is! String || model.isEmpty || mutations is! List<Object?>) {
return const DVOfflineReplayResult(400, 'not a replay request');
}
if (mutations.length > maxMutations) {
return const DVOfflineReplayResult(413, 'too many mutations at once');
}
final DVOfflineRemote? remote = remotes[model];
if (remote == null) {
// The same answer for a model that does not exist and one that exists
// and is not offline. Telling them apart is telling a caller what this
// application is made of.
return const DVOfflineReplayResult(404, 'no such offline model');
}
// Decoded before any of it is applied, so a batch with one bad mutation
// in the middle does not leave the ones before it written and the ones
// after it not.
final List<DVMutation> decoded = <DVMutation>[];
for (final Object? entry in mutations) {
final DVMutation? mutation = _decode(entry);
if (mutation == null) {
return const DVOfflineReplayResult(400, 'not a replay request');
}
decoded.add(mutation);
}
final List<Object?> outcomes = <Object?>[];
for (final DVMutation mutation in decoded) {
final DVRemoteOutcome outcome = await remote.apply(mutation);
outcomes.add(<String, Object?>{
'mutationId': mutation.mutationId,
...dvOutcomeToJson(outcome, sensitive: remote.sensitiveColumns),
});
}
return DVOfflineReplayResult(200, 'applied', body: <String, Object?>{
'outcomes': outcomes,
// Read after the batch, so it is no earlier than any write it applied.
// The device takes its clock offset from this: nothing else it
// receives carries the server's time, and without it a phone whose
// owner moved the date forward would win every conflict.
'serverTime': DateTime.now().toUtc().toIso8601String(),
});
}