fix(housekeeping): restore people partial compatibility
This commit is contained in:
1 parent
fcd1dfd96e
commit
a6a288d9ff
6 files changed
+412
-11
No files matched your search
@@ -59,6 +59,14 @@ function parseField(field: PeopleCommandField, formData: FormData): unknown {
|
||||
}
|
||||
|
||||
const initialState: HousekeepingResult<unknown> | null = null;
|
||||
const externalSyncFailureReason =
|
||||
"Database applied; emulator sync failed. Do not retry automatically.";
|
||||
|
||||
interface ExternalSyncFailure {
|
||||
readonly key: string;
|
||||
readonly userId: number;
|
||||
readonly reason: typeof externalSyncFailureReason;
|
||||
}
|
||||
|
||||
export async function submitPeopleCommandForm(
|
||||
configuration: PeopleCommandSubmission,
|
||||
@@ -96,6 +104,7 @@ export function PeopleCommandResult({
|
||||
readonly result: HousekeepingResult<unknown>;
|
||||
}) {
|
||||
const generatedPassword = readGeneratedPassword(result);
|
||||
const externalSyncFailures = readExternalSyncFailures(result);
|
||||
const message = result.ok
|
||||
? result.completion?.status === "partial"
|
||||
? `Partially completed (${result.correlationId})`
|
||||
@@ -103,10 +112,28 @@ export function PeopleCommandResult({
|
||||
: `Failed: ${result.error.messageKey} (${result.correlationId})`;
|
||||
return (
|
||||
<div
|
||||
role="status"
|
||||
role={externalSyncFailures.length > 0 ? "alert" : "status"}
|
||||
className="space-y-1 text-xs text-[var(--admin-text-muted)]"
|
||||
>
|
||||
<p>{message}</p>
|
||||
{externalSyncFailures.length > 0 ? (
|
||||
<>
|
||||
<p>
|
||||
<strong>Do not retry automatically.</strong> The database updates
|
||||
were applied; retrying may duplicate them.
|
||||
</p>
|
||||
<ul
|
||||
aria-label="External synchronization debt"
|
||||
className="list-disc space-y-1 pl-5"
|
||||
>
|
||||
{externalSyncFailures.map((failure) => (
|
||||
<li key={failure.key}>
|
||||
User #{failure.userId}: {failure.reason}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
) : null}
|
||||
{generatedPassword ? (
|
||||
<p>
|
||||
Generated temporary password:{" "}
|
||||
@@ -119,6 +146,42 @@ export function PeopleCommandResult({
|
||||
);
|
||||
}
|
||||
|
||||
function readExternalSyncFailures(
|
||||
result: HousekeepingResult<unknown>,
|
||||
): readonly ExternalSyncFailure[] {
|
||||
if (
|
||||
!result.ok ||
|
||||
result.completion?.status !== "partial" ||
|
||||
result.completion.external !== "failed" ||
|
||||
!isRecord(result.data) ||
|
||||
!isRecord(result.data.after) ||
|
||||
!Array.isArray(result.data.after.externalSyncFailures)
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
const occurrences = new Map<number, number>();
|
||||
const failures: ExternalSyncFailure[] = [];
|
||||
for (const value of result.data.after.externalSyncFailures.slice(0, 100)) {
|
||||
if (
|
||||
!isRecord(value) ||
|
||||
typeof value.userId !== "number" ||
|
||||
!Number.isSafeInteger(value.userId) ||
|
||||
value.userId <= 0 ||
|
||||
value.reason !== externalSyncFailureReason
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const occurrence = (occurrences.get(value.userId) ?? 0) + 1;
|
||||
occurrences.set(value.userId, occurrence);
|
||||
failures.push({
|
||||
key: `${value.userId}:${occurrence}`,
|
||||
userId: value.userId,
|
||||
reason: externalSyncFailureReason,
|
||||
});
|
||||
}
|
||||
return failures;
|
||||
}
|
||||
|
||||
function readGeneratedPassword(
|
||||
result: HousekeepingResult<unknown>,
|
||||
): string | null {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user