eraseScheduledAccount static method

Future<void> eraseScheduledAccount(
  1. DVAccountErasureJob job
)

Runs job: erases the account and removes it, when its deletion is still scheduled and its window has closed.

The deletion is claimed by compare-and-set first. A person who signed in to cancel moved it to cancelled, and the claim finds nothing to take -- however long the job waited in the queue. An erasure that cannot reach an adapter (DV-PRIVACY-009) puts the deletion back and throws, so the account stays until one completes.

Implementation

static Future<void> eraseScheduledAccount(DVAccountErasureJob job) async {
  final DVPrivacy? privacy = _configuredPrivacy();
  if (privacy == null) {
    throw StateError('An account erasure is due and DV.Privacy is not '
        'configured in this process. Set DARTVEL_PRIVACY_KEY.');
  }
  final DVAccountDeletionStore store = _deletionStore(privacy);
  final DVAccountDeletion? deletion = await store.find(job.userId);
  if (deletion == null) return;
  final DateTime now = clock().toUtc();
  final DateTime? claimed = deletion.claimedAt;
  final bool claimable = switch (deletion.state) {
    DVAccountDeletionState.scheduled => !now.isBefore(deletion.dueAt),
    DVAccountDeletionState.erasing =>
      claimed != null && now.difference(claimed) >= staleErasureClaim,
    _ => false,
  };
  if (!claimable) return;
  if (!await store.move(job.userId,
      from: deletion.state,
      to: DVAccountDeletionState.erasing,
      claimedAt: claimed,
      newClaimedAt: now)) {
    return;
  }
  try {
    final Object? provider = _credentials?.provider;
    if (provider is! DVAccountProvider) {
      throw StateError('An account erasure is due and no DVAccountProvider '
          'is installed with DVAuthEndpoints.install in this process.');
    }
    final DVErasureResult erasure = await privacy.erase(
      subject: job.userId,
      reason: 'The account holder deleted their account.',
      requestedAt: deletion.requestedAt,
      requestedBy: job.userId,
      runBy: 'DVAuthEndpoints.eraseScheduledAccount',
    );
    if (!erasure.complete) {
      throw StateError('The account erasure could not reach '
          '${erasure.unreached.join(', ')} (DV-PRIVACY-009).');
    }
    await _removeAccount(provider, job.userId);
    await store.move(job.userId,
        from: DVAccountDeletionState.erasing,
        to: DVAccountDeletionState.erased,
        claimedAt: now);
  } on Object {
    await store.move(job.userId,
        from: DVAccountDeletionState.erasing,
        to: DVAccountDeletionState.scheduled,
        claimedAt: now);
    rethrow;
  }
}