refactor: enforce enum machine codes

Use owner enums for error and T-Bank protocol values with ESLint regression checks.

Preserve open wire fallbacks and keep protocol imports independent of DI and Prisma.

Verified npm ci, verify, 226 unit tests, 262 isolated e2e tests and unchanged OpenAPI.
This commit is contained in:
2026-09-28 16:37:01 +03:00
committed by ksv741
parent 183b7f1efd
commit 33592c1d5f
47 changed files with 432 additions and 164 deletions
+23 -15
View File
@@ -29,6 +29,14 @@ import type {
import { StructuredLogger } from '../../common/logging/structured-logger.service';
import { MILLISECONDS_PER_SECOND } from '../../common/time';
import { Environment } from '../../config/env.validation';
import { TbankErrorCode } from './tbank-error-code';
import {
InstrumentIdType,
TbankAccountStatus,
TbankCandleInterval,
TbankInstrumentType,
TbankOperationState,
} from './tbank-protocol';
const INITIAL_REQUEST_COUNT = 0;
const MIN_RATE_DELAY_MS = 1;
@@ -115,7 +123,7 @@ export type TbankOperationsByCursorRequest = {
from?: TbankTimestamp;
instrumentId?: string;
limit?: number;
state: 'OPERATION_STATE_EXECUTED';
state: TbankOperationState.Executed;
to?: TbankTimestamp;
};
export type TbankOperationsByCursorResponse = {
@@ -256,7 +264,7 @@ export class TbankClient {
token,
this.users,
'getAccounts',
{ status: 'ACCOUNT_STATUS_OPEN' },
{ status: TbankAccountStatus.Open },
).then((response) => response.accounts ?? []);
}
@@ -279,14 +287,14 @@ export class TbankClient {
token: string,
figi: string,
): Promise<null | TbankInstrument> {
return this.getInstrument(token, 'INSTRUMENT_ID_TYPE_FIGI', figi);
return this.getInstrument(token, InstrumentIdType.Figi, figi);
}
getInstrumentByUid(
token: string,
instrumentUid: string,
): Promise<null | TbankInstrument> {
return this.getInstrument(token, 'INSTRUMENT_ID_TYPE_UID', instrumentUid);
return this.getInstrument(token, InstrumentIdType.Uid, instrumentUid);
}
getDividends(
@@ -314,7 +322,7 @@ export class TbankClient {
'findInstrument',
{
query,
instrumentKind: 'INSTRUMENT_TYPE_SHARE',
instrumentKind: TbankInstrumentType.Share,
},
).then((response) => response.instruments ?? []);
}
@@ -326,7 +334,7 @@ export class TbankClient {
'findInstrument',
{
query,
instrumentKind: 'INSTRUMENT_TYPE_BOND',
instrumentKind: TbankInstrumentType.Bond,
},
).then((response) => response.instruments ?? []);
}
@@ -337,7 +345,7 @@ export class TbankClient {
this.sparseInstruments,
'bondBy',
{
idType: 'INSTRUMENT_ID_TYPE_UID',
idType: InstrumentIdType.Uid,
id: uid,
},
).then((response) => response.instrument ?? null);
@@ -367,7 +375,7 @@ export class TbankClient {
this.instruments,
'shareBy',
{
idType: 'INSTRUMENT_ID_TYPE_UID',
idType: InstrumentIdType.Uid,
id: uid,
},
).then((response) => response.instrument ?? null);
@@ -446,7 +454,7 @@ export class TbankClient {
to: {
seconds: String(Math.floor(to.valueOf() / MILLISECONDS_PER_SECOND)),
},
interval: 'CANDLE_INTERVAL_DAY',
interval: TbankCandleInterval.Day,
limit: 2400,
},
).then((response) => response.candles ?? []);
@@ -454,7 +462,7 @@ export class TbankClient {
private async getInstrument(
token: string,
idType: 'INSTRUMENT_ID_TYPE_FIGI' | 'INSTRUMENT_ID_TYPE_UID',
idType: InstrumentIdType,
id: string,
): Promise<null | TbankInstrument> {
try {
@@ -484,7 +492,7 @@ export class TbankClient {
const queueFull = this.queue.size >= MAX_QUEUE_SIZE;
try {
if (queueFull) {
throw new ServiceUnavailableException({ code: 'TBANK_UNAVAILABLE' });
throw new ServiceUnavailableException({ code: TbankErrorCode.Unavailable });
}
const response = await this.queue.run(() =>
this.request<Response>(token, client, method, request),
@@ -498,7 +506,7 @@ export class TbankClient {
} catch (error) {
const grpcCode = (error as ServiceError).code;
const failureContext = queueFull
? { code: 'TBANK_UNAVAILABLE' }
? { code: TbankErrorCode.Unavailable }
: { grpcCode };
const retryable = queueFull
? metadataCall
@@ -534,7 +542,7 @@ export class TbankClient {
[status.PERMISSION_DENIED, status.UNAUTHENTICATED].includes(grpcCode)
) {
// eslint-disable-next-line preserve-caught-error -- Discard raw upstream authentication errors at the integration boundary.
throw new Error('INVALID_TBANK_TOKEN');
throw new Error(TbankErrorCode.InvalidToken);
}
if (
grpcCode === status.NOT_FOUND &&
@@ -542,10 +550,10 @@ export class TbankClient {
) {
throw new NotFoundException();
}
throw new ServiceUnavailableException({ code: 'TBANK_UNAVAILABLE' });
throw new ServiceUnavailableException({ code: TbankErrorCode.Unavailable });
}
}
throw new ServiceUnavailableException({ code: 'TBANK_UNAVAILABLE' });
throw new ServiceUnavailableException({ code: TbankErrorCode.Unavailable });
}
private request<Response>(