refactor: replace ignored numeric literals with constants
Backend security / security (push) Successful in 8s
Backend verify / verify (push) Failing after 2m17s
Conventional commits / commits (pull_request) Successful in 31s
Backend security / security (pull_request) Successful in 8s
Backend verify / verify (pull_request) Failing after 1m56s
Backend security / security (push) Successful in 8s
Backend verify / verify (push) Failing after 2m17s
Conventional commits / commits (pull_request) Successful in 31s
Backend security / security (pull_request) Successful in 8s
Backend verify / verify (pull_request) Failing after 1m56s
This commit is contained in:
@@ -9,6 +9,9 @@ import {
|
||||
type ForecastSourceVersion,
|
||||
} from '../infrastructure/forecast.repository';
|
||||
|
||||
const MISSING_DATA_REVISION = 0;
|
||||
const STALE_FRESHNESS_MS = 0;
|
||||
|
||||
@Injectable()
|
||||
export class ForecastService {
|
||||
constructor(private readonly repository: ForecastRepository) {}
|
||||
@@ -89,7 +92,7 @@ export class ForecastService {
|
||||
data,
|
||||
meta: investmentResourceMeta({
|
||||
resourceKey: input.scopeKey,
|
||||
dataRevision: String(stored?.dataRevision ?? 0),
|
||||
dataRevision: String(stored?.dataRevision ?? MISSING_DATA_REVISION),
|
||||
availability: stored ?
|
||||
'complete' :
|
||||
dependencies.some(
|
||||
@@ -100,7 +103,7 @@ export class ForecastService {
|
||||
fetchedAt: first ? new Date(first) : null,
|
||||
freshForMs:
|
||||
data && 'positionIsStale' in data && data.positionIsStale === true ?
|
||||
0 :
|
||||
STALE_FRESHNESS_MS :
|
||||
Number.POSITIVE_INFINITY,
|
||||
work: work?.id ? { ...work, id: work.id } : null,
|
||||
dependencies,
|
||||
|
||||
@@ -43,6 +43,8 @@ import {
|
||||
PortfolioPositionDto,
|
||||
} from '../presentation/portfolio.dto';
|
||||
|
||||
const MISSING_DATA_REVISION = 0;
|
||||
|
||||
const membershipFreshForMs = tbankSourceTtlMs.membership;
|
||||
const types: Record<string, string> = {
|
||||
share: 'STOCK',
|
||||
@@ -167,7 +169,7 @@ export class PortfolioService {
|
||||
}
|
||||
const membershipMeta = investmentResourceMeta({
|
||||
resourceKey: 'accounts/membership',
|
||||
dataRevision: String(saved?.membershipRevision ?? 0),
|
||||
dataRevision: String(saved?.membershipRevision ?? MISSING_DATA_REVISION),
|
||||
availability: saved?.membershipAt ? 'complete' : 'missing',
|
||||
fetchedAt: saved?.membershipAt ?? null,
|
||||
freshForMs: membershipFreshForMs,
|
||||
@@ -195,7 +197,7 @@ export class PortfolioService {
|
||||
null,
|
||||
meta: investmentResourceMeta({
|
||||
resourceKey: `portfolio/${encodeURIComponent(account.accountId)}`,
|
||||
dataRevision: String(account.portfolio?.dataRevision ?? 0),
|
||||
dataRevision: String(account.portfolio?.dataRevision ?? MISSING_DATA_REVISION),
|
||||
availability: account.portfolio ? 'complete' : 'missing',
|
||||
fetchedAt: account.portfolio?.fetchedAt ?? null,
|
||||
freshForMs: portfolioFreshForMs,
|
||||
|
||||
@@ -2,10 +2,16 @@ import type { Money } from '../../../../common/money.types';
|
||||
|
||||
import {
|
||||
add,
|
||||
DECIMAL_RADIX,
|
||||
decimalFromString,
|
||||
formatDecimal,
|
||||
} from '../../../../common/decimal';
|
||||
|
||||
const ZERO_AMOUNT = 0n;
|
||||
const INITIAL_UNVALUED_COUNT = 0;
|
||||
const POSITION_INCREMENT = 1;
|
||||
const ROUNDING_DIVISOR = 2n;
|
||||
|
||||
const SHARE_PERCENT_DECIMAL_PLACES = 6;
|
||||
const PERCENT_SCALED_FACTOR = 100_000_000n;
|
||||
|
||||
@@ -33,7 +39,7 @@ export function portfolioStructure(
|
||||
const value = decimalFromString(money.value);
|
||||
// eslint-disable-next-line security/detect-object-injection -- The numeric index belongs to the fixed portfolio category array.
|
||||
group.values[index] = add(group.values[index], value);
|
||||
group.hasNegativeValues ||= value.value < 0n;
|
||||
group.hasNegativeValues ||= value.value < ZERO_AMOUNT;
|
||||
currencies.set(money.currency, group);
|
||||
};
|
||||
for (const position of positions) {
|
||||
@@ -41,7 +47,7 @@ export function portfolioStructure(
|
||||
position.type :
|
||||
'OTHER';
|
||||
if (position.currentValue) include(type, position.currentValue);
|
||||
else unvalued.set(type, (unvalued.get(type) ?? 0) + 1);
|
||||
else unvalued.set(type, (unvalued.get(type) ?? INITIAL_UNVALUED_COUNT) + POSITION_INCREMENT);
|
||||
}
|
||||
for (const amount of cash) include('CASH', amount);
|
||||
|
||||
@@ -52,7 +58,7 @@ export function portfolioStructure(
|
||||
.map(([currency, group]) => {
|
||||
const total = group.values.reduce(add, decimalFromString('0'));
|
||||
const canCalculateShares =
|
||||
!unvalued.size && !group.hasNegativeValues && total.value > 0n;
|
||||
!unvalued.size && !group.hasNegativeValues && total.value > ZERO_AMOUNT;
|
||||
const categories = group.values.map((value, index) => ({
|
||||
// eslint-disable-next-line security/detect-object-injection -- The numeric index belongs to the fixed portfolio category array.
|
||||
type: categoryTypes[index],
|
||||
@@ -63,9 +69,9 @@ export function portfolioStructure(
|
||||
formatDecimal({
|
||||
value:
|
||||
(value.value *
|
||||
10n ** BigInt(total.scale - value.scale) *
|
||||
DECIMAL_RADIX ** BigInt(total.scale - value.scale) *
|
||||
PERCENT_SCALED_FACTOR +
|
||||
total.value / 2n) /
|
||||
total.value / ROUNDING_DIVISOR) /
|
||||
total.value,
|
||||
scale: SHARE_PERCENT_DECIMAL_PLACES,
|
||||
}) :
|
||||
|
||||
@@ -5,6 +5,8 @@ import type { Prisma } from '../../../../../generated/prisma/client';
|
||||
|
||||
import { PrismaService } from '../../../../prisma/prisma.service';
|
||||
|
||||
const FIRST_BATCH_OFFSET = 0;
|
||||
|
||||
export type ForecastSourceVersion = {
|
||||
dataRevision: number;
|
||||
fetchedAt: Date;
|
||||
@@ -82,7 +84,7 @@ export class ForecastRepository {
|
||||
// Four identity parameters per source: stay below SQLite's parameter limit.
|
||||
const sources: ForecastSourceVersion[] = [];
|
||||
for (
|
||||
let offset = 0;
|
||||
let offset = FIRST_BATCH_OFFSET;
|
||||
offset < input.sources.length;
|
||||
offset += forecastSourceReadBatchSize
|
||||
) {
|
||||
|
||||
@@ -10,6 +10,8 @@ import {
|
||||
} from '../../../../integrations/tbank/tbank.api';
|
||||
import { PrismaService } from '../../../../prisma/prisma.service';
|
||||
|
||||
const EXPECTED_UPDATED_ACCOUNTS = 1;
|
||||
|
||||
type PortfolioAttempt = {
|
||||
attemptId: string;
|
||||
id: string;
|
||||
@@ -152,7 +154,7 @@ export class PortfolioRepository {
|
||||
},
|
||||
data: { state: 'failed', errorCode: 'TBANK_UNAVAILABLE' },
|
||||
});
|
||||
return result.count === 1;
|
||||
return result.count === EXPECTED_UPDATED_ACCOUNTS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user