refactor: centralize domain values and enforce numeric lint
This commit is contained in:
@@ -2,6 +2,12 @@ import { Injectable, ServiceUnavailableException } from '@nestjs/common';
|
||||
import { load } from 'cheerio';
|
||||
import { LRUCache } from 'lru-cache';
|
||||
|
||||
const REGISTRY_CACHE_TTL_MS = 21_600_000;
|
||||
const REQUEST_TIMEOUT_MS = 10_000;
|
||||
const COLUMN_COUNT = 6;
|
||||
const ISIN_COLUMN = 3;
|
||||
const DATE_COLUMN = 5;
|
||||
|
||||
const registryUrl = 'https://ratings.ru/ratings/issues/';
|
||||
export type NcrRating = {
|
||||
agency: 'NCR';
|
||||
@@ -18,7 +24,7 @@ export class NcrRatingsClient {
|
||||
private readonly cache = new LRUCache<
|
||||
string,
|
||||
Promise<Map<string, NcrRating>>
|
||||
>({ max: 1, ttl: 6 * 60 * 60_000 });
|
||||
>({ max: 1, ttl: REGISTRY_CACHE_TTL_MS });
|
||||
|
||||
async rating(isin: string): Promise<NcrRating | null> {
|
||||
const key = isin.trim().toUpperCase();
|
||||
@@ -38,7 +44,7 @@ export class NcrRatingsClient {
|
||||
|
||||
private async registry(): Promise<Map<string, NcrRating>> {
|
||||
const response = await fetch(registryUrl, {
|
||||
signal: AbortSignal.timeout(10_000),
|
||||
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
||||
});
|
||||
if (!response.ok) throw new Error('NCR unavailable');
|
||||
const $ = load(await response.text());
|
||||
@@ -49,18 +55,18 @@ export class NcrRatingsClient {
|
||||
.get();
|
||||
if (
|
||||
table.length !== 1 ||
|
||||
headings.length !== 6 ||
|
||||
headings.length !== COLUMN_COUNT ||
|
||||
headings[2] !== 'Рейтинг' ||
|
||||
headings[3] !== 'ISIN' ||
|
||||
headings[5] !== 'Дата'
|
||||
headings[ISIN_COLUMN] !== 'ISIN' ||
|
||||
headings[DATE_COLUMN] !== 'Дата'
|
||||
)
|
||||
throw new Error('NCR registry changed');
|
||||
const observedAt = new Date().toISOString();
|
||||
const result = new Map<string, NcrRating>();
|
||||
table.find('tbody tr').each((_, row) => {
|
||||
const cells = $(row).find('td');
|
||||
if (cells.length !== 6) throw new Error('NCR row changed');
|
||||
const isin = cells.eq(3).text().trim().toUpperCase();
|
||||
if (cells.length !== COLUMN_COUNT) throw new Error('NCR row changed');
|
||||
const isin = cells.eq(ISIN_COLUMN).text().trim().toUpperCase();
|
||||
// Unassigned issues cannot safely be matched to a held instrument.
|
||||
if (!/^[A-Z]{2}[A-Z0-9]{9}\d$/.test(isin)) return;
|
||||
const raw = cells.eq(2).text().replace(/\s+/g, ' ').trim();
|
||||
@@ -73,9 +79,9 @@ export class NcrRatingsClient {
|
||||
)
|
||||
throw new Error('NCR rating changed');
|
||||
const dateParts = /^(\d{2})\.(\d{2})\.(\d{4})$/.exec(
|
||||
cells.eq(5).text().trim(),
|
||||
cells.eq(DATE_COLUMN).text().trim(),
|
||||
);
|
||||
const href = cells.eq(3).find('a').attr('href');
|
||||
const href = cells.eq(ISIN_COLUMN).find('a').attr('href');
|
||||
if (!dateParts || !href || !/^\/ratings\/issues\/\d+\/$/.test(href))
|
||||
throw new Error('NCR identity missing');
|
||||
const date = `${dateParts[3]}-${dateParts[2]}-${dateParts[1]}`;
|
||||
|
||||
Reference in New Issue
Block a user