File size: 1,800 Bytes
f0634fb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import { isRecord } from './utils';

const DIRECT_ERROR_KEYS = ['error_description', 'message', 'detail'] as const;
const NESTED_ERROR_KEYS = ['message', 'error_description', 'detail', 'code', 'type'] as const;

export function extractApiErrorMessage(value: unknown): string | undefined {
  if (Array.isArray(value)) {
    for (const item of value) {
      const message = extractApiErrorMessage(item);
      if (message !== undefined) return message;
    }
    return undefined;
  }

  if (!isRecord(value)) return undefined;

  for (const key of DIRECT_ERROR_KEYS) {
    const message = stringField(value, key);
    if (message !== undefined) return message;
  }

  const error = value['error'];
  const errorString = nonEmptyString(error);
  if (errorString !== undefined) return errorString;

  if (isRecord(error)) {
    for (const key of NESTED_ERROR_KEYS) {
      const message = stringField(error, key);
      if (message !== undefined) return message;
    }
  }

  const errors = value['errors'];
  if (Array.isArray(errors)) {
    for (const item of errors) {
      const message = extractApiErrorMessage(item);
      if (message !== undefined) return message;
    }
  }

  return undefined;
}

export async function readApiErrorMessage(
  response: Response,
  fallback: string,
): Promise<string> {
  let parsed: unknown;
  try {
    parsed = await response.json();
  } catch {
    return fallback;
  }

  return extractApiErrorMessage(parsed) ?? fallback;
}

function stringField(record: Record<string, unknown>, key: string): string | undefined {
  return nonEmptyString(record[key]);
}

function nonEmptyString(value: unknown): string | undefined {
  if (typeof value !== 'string') return undefined;
  const trimmed = value.trim();
  return trimmed.length > 0 ? trimmed : undefined;
}