refactor: enforce enum machine codes #18
Use owner enums for known codes; preserve wire values and unknown fallbacks. Verified npm ci and verify: 379 tests, 5 lint checks, app and Storybook builds.
This commit was merged in pull request #41.
This commit is contained in:
@@ -3,7 +3,7 @@ import createQueryClient from "openapi-react-query";
|
||||
|
||||
import type { paths } from "./generated/schema";
|
||||
|
||||
import { apiErrorCode } from "./problem-details";
|
||||
import { ApiErrorCode, apiErrorCode, isApiErrorCode } from "./problem-details";
|
||||
|
||||
const HTTP_UNAUTHORIZED = 401;
|
||||
|
||||
@@ -101,7 +101,7 @@ async function fetchCurrent(
|
||||
catch {
|
||||
return response;
|
||||
}
|
||||
if (apiErrorCode(problem) !== "UNAUTHORIZED") return response;
|
||||
if (!isApiErrorCode(apiErrorCode(problem), ApiErrorCode.Unauthorized)) return response;
|
||||
if (!isCurrent()) throw new DOMException("Session changed", "AbortError");
|
||||
if (!(await sessionState.handlers.refresh())) {
|
||||
if (isCurrent()) sessionState.handlers.clear();
|
||||
|
||||
@@ -9,7 +9,7 @@ export {
|
||||
invalidateApiRequests,
|
||||
} from "./client";
|
||||
export type { paths } from "./generated/schema";
|
||||
export { apiErrorCode } from "./problem-details";
|
||||
export { ApiErrorCode, apiErrorCode, isApiErrorCode } from "./problem-details";
|
||||
|
||||
export {
|
||||
invalidateResourceAccess,
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
import { ApiErrorCode, apiErrorCode, isApiErrorCode } from "./problem-details";
|
||||
|
||||
it("matches known codes and preserves unknown wire codes for fallback handling", () => {
|
||||
expect(isApiErrorCode(apiErrorCode({ code: "UNAUTHORIZED" }), ApiErrorCode.Unauthorized)).toBe(true);
|
||||
const unknownCode = apiErrorCode({ code: "FUTURE_PROVIDER_ERROR" });
|
||||
expect(unknownCode).toBe("FUTURE_PROVIDER_ERROR");
|
||||
expect(isApiErrorCode(unknownCode, ApiErrorCode.Unauthorized)).toBe(false);
|
||||
expect(isApiErrorCode(undefined, ApiErrorCode.Unauthorized)).toBe(false);
|
||||
expect(apiErrorCode(null)).toBeUndefined();
|
||||
expect(apiErrorCode({})).toBeUndefined();
|
||||
});
|
||||
@@ -1,7 +1,30 @@
|
||||
enum ApiErrorCode {
|
||||
BankDependentRefunds = "BANK_DEPENDENT_REFUNDS",
|
||||
BankIntentConflict = "BANK_INTENT_CONFLICT",
|
||||
BankNotFound = "BANK_NOT_FOUND",
|
||||
BankRefundLimit = "BANK_REFUND_LIMIT",
|
||||
BankVersionConflict = "BANK_VERSION_CONFLICT",
|
||||
BankWriteConflict = "BANK_WRITE_CONFLICT",
|
||||
BondTickerAmbiguous = "BOND_TICKER_AMBIGUOUS",
|
||||
Forbidden = "FORBIDDEN",
|
||||
InvalidOperationsCursor = "INVALID_OPERATIONS_CURSOR",
|
||||
InvalidTbankToken = "INVALID_TBANK_TOKEN",
|
||||
NotFound = "NOT_FOUND",
|
||||
StockTickerAmbiguous = "STOCK_TICKER_AMBIGUOUS",
|
||||
TbankNotConnected = "TBANK_NOT_CONNECTED",
|
||||
TbankUnavailable = "TBANK_UNAVAILABLE",
|
||||
Unauthorized = "UNAUTHORIZED",
|
||||
}
|
||||
|
||||
function apiErrorCode(error: unknown): string | undefined {
|
||||
return typeof error === "object" && error !== null && "code" in error
|
||||
? String(error.code)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
export { apiErrorCode };
|
||||
// Incoming codes are an open wire string; unknown values retain their fallback.
|
||||
function isApiErrorCode(value: unknown, expected: ApiErrorCode): boolean {
|
||||
return value === expected;
|
||||
}
|
||||
|
||||
export { ApiErrorCode, apiErrorCode, isApiErrorCode };
|
||||
|
||||
@@ -10,7 +10,7 @@ import type { components } from "./generated/schema";
|
||||
|
||||
import { useAuthenticatedQuery } from "./authenticated-query";
|
||||
import { getAuthenticatedSessionSnapshot } from "./client";
|
||||
import { apiErrorCode } from "./problem-details";
|
||||
import { ApiErrorCode, apiErrorCode, isApiErrorCode } from "./problem-details";
|
||||
|
||||
const accessEpochs = new WeakMap<QueryClient, Map<string, number>>();
|
||||
|
||||
@@ -23,13 +23,14 @@ function invalidateResourceAccess(
|
||||
group: string,
|
||||
error: unknown,
|
||||
) {
|
||||
const errorCode = apiErrorCode(error);
|
||||
if (
|
||||
![
|
||||
"FORBIDDEN",
|
||||
"INVALID_TBANK_TOKEN",
|
||||
"NOT_FOUND",
|
||||
"TBANK_NOT_CONNECTED",
|
||||
].includes(apiErrorCode(error) ?? "")
|
||||
[
|
||||
ApiErrorCode.Forbidden,
|
||||
ApiErrorCode.InvalidTbankToken,
|
||||
ApiErrorCode.NotFound,
|
||||
ApiErrorCode.TbankNotConnected,
|
||||
].every(code => !isApiErrorCode(errorCode, code))
|
||||
)
|
||||
return;
|
||||
const epochs = accessEpochs.get(client) ?? new Map<string, number>();
|
||||
|
||||
Reference in New Issue
Block a user