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(),
  });
}