refactor(account): isRowMissing als gemeinsame Hilfsfunktion statt dreier Kopien
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0ad51bf7c6
commit
04404ffdae
5 files changed
+40
-22
No files matched your search
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
import { errorMessage, errorType, isInvalidLinkError, isUnauthorized, type ErrorContext } from './errors';
|
import { errorMessage, errorType, isInvalidLinkError, isRowMissing, isUnauthorized, type ErrorContext } from './errors';
|
||||||
|
|
||||||
/** Fehler wie vom SDK (AppwriteException): Felder code, type, message. */
|
/** Fehler wie vom SDK (AppwriteException): Felder code, type, message. */
|
||||||
const fail = (code: number, type: string, message = '') => ({ code, type, message });
|
const fail = (code: number, type: string, message = '') => ({ code, type, message });
|
||||||
@@ -149,6 +149,33 @@ describe('errorType', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('isRowMissing', () => {
|
||||||
|
it('404 mit row_not_found heißt: die Zeile gibt es nicht (oder nicht für mich)', () => {
|
||||||
|
expect(isRowMissing(fail(404, 'row_not_found'))).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('404 ohne Typ (leer oder fehlend) zählt ebenso', () => {
|
||||||
|
expect(isRowMissing(fail(404, ''))).toBe(true);
|
||||||
|
expect(isRowMissing({ code: 404 })).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('eine fehlende Tabelle, Datenbank oder Datei ist ein echter Fehler, kein „Zeile weg“', () => {
|
||||||
|
for (const type of ['table_not_found', 'database_not_found', 'storage_file_not_found']) {
|
||||||
|
expect(isRowMissing(fail(404, type)), type).toBe(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('andere Status und Nicht-Fehler nicht', () => {
|
||||||
|
expect(isRowMissing(fail(401, 'row_not_found'))).toBe(false);
|
||||||
|
expect(isRowMissing(fail(500, 'row_not_found'))).toBe(false);
|
||||||
|
expect(isRowMissing({ code: '404', type: 'row_not_found' })).toBe(false);
|
||||||
|
expect(isRowMissing(new TypeError('Failed to fetch'))).toBe(false);
|
||||||
|
expect(isRowMissing('row_not_found')).toBe(false);
|
||||||
|
expect(isRowMissing(null)).toBe(false);
|
||||||
|
expect(isRowMissing(undefined)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('isInvalidLinkError (Passwort-neu-Seite)', () => {
|
describe('isInvalidLinkError (Passwort-neu-Seite)', () => {
|
||||||
it('Link-Fehler', () => {
|
it('Link-Fehler', () => {
|
||||||
expect(isInvalidLinkError(fail(401, 'user_invalid_token'))).toBe(true);
|
expect(isInvalidLinkError(fail(401, 'user_invalid_token'))).toBe(true);
|
||||||
|
|||||||
@@ -24,6 +24,15 @@ export function isUnauthorized(err: unknown): boolean {
|
|||||||
return field(err, 'code') === 401;
|
return field(err, 'code') === 401;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* „Die Zeile gibt es nicht (oder nicht für mich)“: 404 `row_not_found` (auch ein 404 ohne Typ). Eine fehlende Tabelle, Datenbank oder
|
||||||
|
* Datei ist dagegen ein echter Fehler. Die Einzel-Abfragen der Tabellen (`getGalleryCard`, `getLogo`, `getUsername`) werten damit 404 als „nicht da“.
|
||||||
|
*/
|
||||||
|
export function isRowMissing(err: unknown): boolean {
|
||||||
|
const type = field(err, 'type');
|
||||||
|
return field(err, 'code') === 404 && (type === 'row_not_found' || type === undefined || type === '');
|
||||||
|
}
|
||||||
|
|
||||||
/** Bei `general_argument_invalid` nennt die Nachricht den Parameter: „Invalid `email` param: …“. */
|
/** Bei `general_argument_invalid` nennt die Nachricht den Parameter: „Invalid `email` param: …“. */
|
||||||
function invalidParam(err: unknown): string | null {
|
function invalidParam(err: unknown): string | null {
|
||||||
const message = field(err, 'message');
|
const message = field(err, 'message');
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { API_MESSAGES } from '../i18n/api-messages';
|
|||||||
import { msg, t } from '../i18n/i18n';
|
import { msg, t } from '../i18n/i18n';
|
||||||
import { IDS, tables } from './appwrite';
|
import { IDS, tables } from './appwrite';
|
||||||
import { currentUserId } from './auth';
|
import { currentUserId } from './auth';
|
||||||
|
import { isRowMissing } from './errors';
|
||||||
|
|
||||||
/** Wie im Dienst (deploy/api/common.js): 3–20 Zeichen, das erste ein Buchstabe oder eine Zahl, danach Buchstaben, Zahlen, `.`, `_`, `-`. */
|
/** Wie im Dienst (deploy/api/common.js): 3–20 Zeichen, das erste ein Buchstabe oder eine Zahl, danach Buchstaben, Zahlen, `.`, `_`, `-`. */
|
||||||
export const USERNAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{2,19}$/;
|
export const USERNAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{2,19}$/;
|
||||||
@@ -112,13 +113,6 @@ export function myUsername(): string | null {
|
|||||||
|
|
||||||
// ---- Lesen --------------------------------------------------------------------------------------------------
|
// ---- Lesen --------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/** „Es gibt kein Profil (oder keines für mich)“; eine fehlende Tabelle oder Datenbank ist dagegen ein echter Fehler. */
|
|
||||||
function isRowMissing(err: unknown): boolean {
|
|
||||||
if (typeof err !== 'object' || err === null) return false;
|
|
||||||
const { code, type } = err as { code?: unknown; type?: unknown };
|
|
||||||
return code === 404 && (type === 'row_not_found' || type === undefined || type === '');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Der Name eines Kontos. Hat es kein Profil, ist das Ergebnis null (Appwrite antwortet mit 404); eine ID, die keine Appwrite-ID sein
|
* Der Name eines Kontos. Hat es kein Profil, ist das Ergebnis null (Appwrite antwortet mit 404); eine ID, die keine Appwrite-ID sein
|
||||||
* kann, ergibt null ohne Anfrage. Andere Fehler (Netz, Server) gehen durch.
|
* kann, ergibt null ohne Anfrage. Andere Fehler (Netz, Server) gehen durch.
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import { AppwriteException, Query, type Models } from 'appwrite';
|
import { AppwriteException, Query, type Models } from 'appwrite';
|
||||||
import { ID_PATTERN, fetchFile } from '../account/cards';
|
import { ID_PATTERN, fetchFile } from '../account/cards';
|
||||||
import { IDS, tables } from '../account/appwrite';
|
import { IDS, tables } from '../account/appwrite';
|
||||||
|
import { isRowMissing } from '../account/errors';
|
||||||
import { msg } from '../i18n/i18n';
|
import { msg } from '../i18n/i18n';
|
||||||
|
|
||||||
export interface GalleryCard {
|
export interface GalleryCard {
|
||||||
@@ -112,13 +113,6 @@ export async function listGallery(opts: GalleryOptions): Promise<{ cards: Galler
|
|||||||
return { cards, nextCursor: cards.length === PAGE_SIZE ? cards[cards.length - 1].id : null };
|
return { cards, nextCursor: cards.length === PAGE_SIZE ? cards[cards.length - 1].id : null };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** „Die Zeile gibt es nicht (oder nicht für mich)“; fehlende Tabelle oder Datenbank ist dagegen ein echter Fehler. */
|
|
||||||
function isRowMissing(err: unknown): boolean {
|
|
||||||
if (typeof err !== 'object' || err === null) return false;
|
|
||||||
const { code, type } = err as { code?: unknown; type?: unknown };
|
|
||||||
return code === 404 && (type === 'row_not_found' || type === undefined || type === '');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Eine Karte per ID. Gibt es sie nicht oder ist sie für mich ausgeblendet (Appwrite antwortet beides mit 404), ist das
|
* Eine Karte per ID. Gibt es sie nicht oder ist sie für mich ausgeblendet (Appwrite antwortet beides mit 404), ist das
|
||||||
* Ergebnis null. Eine ID, die keine Appwrite-ID sein kann, ergibt null ohne Anfrage.
|
* Ergebnis null. Eine ID, die keine Appwrite-ID sein kann, ergibt null ohne Anfrage.
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import { AppwriteException, Query, type Models } from 'appwrite';
|
import { AppwriteException, Query, type Models } from 'appwrite';
|
||||||
import { ID_PATTERN, fetchFile } from '../account/cards';
|
import { ID_PATTERN, fetchFile } from '../account/cards';
|
||||||
import { IDS, tables } from '../account/appwrite';
|
import { IDS, tables } from '../account/appwrite';
|
||||||
|
import { isRowMissing } from '../account/errors';
|
||||||
|
|
||||||
export interface CommunityLogo {
|
export interface CommunityLogo {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -95,13 +96,6 @@ export async function listLogos(opts: LogoListOptions): Promise<{ logos: Communi
|
|||||||
return { logos, nextCursor: logos.length === LOGO_PAGE_SIZE ? logos[logos.length - 1].id : null };
|
return { logos, nextCursor: logos.length === LOGO_PAGE_SIZE ? logos[logos.length - 1].id : null };
|
||||||
}
|
}
|
||||||
|
|
||||||
/** „Die Zeile gibt es nicht (oder nicht für mich)“; fehlende Tabelle oder Datenbank ist dagegen ein echter Fehler. */
|
|
||||||
function isRowMissing(err: unknown): boolean {
|
|
||||||
if (typeof err !== 'object' || err === null) return false;
|
|
||||||
const { code, type } = err as { code?: unknown; type?: unknown };
|
|
||||||
return code === 404 && (type === 'row_not_found' || type === undefined || type === '');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ein Logo per ID. Gibt es das Logo nicht oder ist es für mich ausgeblendet oder gelöscht (Appwrite antwortet beides mit 404),
|
* Ein Logo per ID. Gibt es das Logo nicht oder ist es für mich ausgeblendet oder gelöscht (Appwrite antwortet beides mit 404),
|
||||||
* ist das Ergebnis null. Eine ID, die keine Appwrite-ID sein kann, ergibt null ohne Anfrage.
|
* ist das Ergebnis null. Eine ID, die keine Appwrite-ID sein kann, ergibt null ohne Anfrage.
|
||||||
|
|||||||
Reference in new issue
Block a user