chore: strengthen backend eslint and value policies #40
@@ -123,11 +123,23 @@ export class CatalogDiscoveryRepository {
|
||||
}
|
||||
|
||||
readSources(inputs: CatalogSource[]) {
|
||||
// Share identity predicates across UIDs instead of preparing one OR per row.
|
||||
const groups = new Map<string, CatalogSource[]>();
|
||||
for (const input of inputs) {
|
||||
const key = JSON.stringify([
|
||||
input.kind,
|
||||
sourceProvider(input),
|
||||
input.kind === 'detail' ? input.instrumentType : (input.rangeKey ?? ''),
|
||||
]);
|
||||
const group = groups.get(key);
|
||||
if (group) group.push(input);
|
||||
else groups.set(key, [input]);
|
||||
}
|
||||
return this.prisma.tbankInstrumentSource.findMany({
|
||||
where: {
|
||||
provider,
|
||||
OR: inputs.map((input) => ({
|
||||
instrumentUid: input.uid,
|
||||
OR: [...groups.values()].map(([input, ...rest]) => ({
|
||||
instrumentUid: { in: [input, ...rest].map((entry) => entry.uid) },
|
||||
kind: input.kind,
|
||||
source: sourceProvider(input),
|
||||
rangeKey:
|
||||
|
||||
@@ -178,6 +178,50 @@ describe('local stock/bond search (SQLite HTTP)', () => {
|
||||
});
|
||||
afterAll(async () => app?.close());
|
||||
|
||||
it('reads only requested source identities when batching UIDs, types and periods', async () => {
|
||||
const repository = app.get(CatalogDiscoveryRepository);
|
||||
const fetchedAt = new Date();
|
||||
for (const uid of ['a', 'b']) {
|
||||
await prisma.tbankInstrument.create({
|
||||
data: { provider: 'tbank', uid, instrumentType: 'share', metadata: {}, fetchedAt },
|
||||
});
|
||||
}
|
||||
const expected = [
|
||||
{ instrumentUid: 'a', source: 'tbank', kind: 'detail', rangeKey: 'share' },
|
||||
{ instrumentUid: 'b', source: 'tbank', kind: 'detail', rangeKey: 'share' },
|
||||
{ instrumentUid: 'a', source: 'tbank', kind: 'dividends', rangeKey: '2026' },
|
||||
{ instrumentUid: 'b', source: 'tbank', kind: 'dividends', rangeKey: '2027' },
|
||||
{ instrumentUid: 'b', source: 'moex', kind: 'bond-events', rangeKey: '' },
|
||||
{ instrumentUid: 'a', source: 'ncr', kind: 'rating', rangeKey: '' },
|
||||
];
|
||||
const distractors = [
|
||||
{ instrumentUid: 'a', source: 'tbank', kind: 'detail', rangeKey: 'bond' },
|
||||
{ instrumentUid: 'b', source: 'tbank', kind: 'dividends', rangeKey: '2026' },
|
||||
{ instrumentUid: 'a', source: 'tbank', kind: 'dividends', rangeKey: '2027' },
|
||||
{ instrumentUid: 'a', source: 'moex', kind: 'bond-events', rangeKey: '' },
|
||||
{ instrumentUid: 'b', source: 'tbank', kind: 'bond-events', rangeKey: '' },
|
||||
{ instrumentUid: 'a', source: 'tbank', kind: 'rating', rangeKey: '' },
|
||||
];
|
||||
await prisma.tbankInstrumentSource.createMany({
|
||||
data: [...expected, ...distractors].map((identity) => ({
|
||||
...identity, provider: 'tbank', fetchedAt, payload: identity,
|
||||
})),
|
||||
});
|
||||
const rows = await repository.readSources([
|
||||
{ uid: 'a', instrumentType: 'share', kind: 'detail' },
|
||||
{ uid: 'b', instrumentType: 'share', kind: 'detail' },
|
||||
{ uid: 'a', instrumentType: 'share', kind: 'detail' },
|
||||
{ uid: 'a', instrumentType: 'share', kind: 'dividends', rangeKey: '2026' },
|
||||
{ uid: 'b', instrumentType: 'share', kind: 'dividends', rangeKey: '2027' },
|
||||
{ uid: 'b', instrumentType: 'bond', kind: 'bond-events' },
|
||||
{ uid: 'a', instrumentType: 'bond', kind: 'rating' },
|
||||
{ uid: 'missing', instrumentType: 'share', kind: 'detail' },
|
||||
]);
|
||||
expect(rows.map((row) => row.payload)).toEqual(expect.arrayContaining(expected));
|
||||
expect(rows).toHaveLength(expected.length);
|
||||
expect(await repository.readSources([])).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns missing without waiting for discovery, then partial while enrichment is blocked; observe never launches work', async () => {
|
||||
const found = deferred<(typeof share)[]>();
|
||||
const detail = deferred<typeof share>();
|
||||
|
||||
Reference in New Issue
Block a user