@futdevpro/nts-dynamo 1.15.133 → 1.15.135
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dynamo/logs/cicd-pipeline/output.log +1931 -1845
- package/.dynamo/logs/cicd-pipeline/status.json +29 -29
- package/build/_modules/usage/usage.controller.d.ts.map +1 -1
- package/build/_modules/usage/usage.controller.js +14 -4
- package/build/_modules/usage/usage.controller.js.map +1 -1
- package/build/_services/base/data.service.d.ts +11 -0
- package/build/_services/base/data.service.d.ts.map +1 -1
- package/build/_services/base/data.service.js +30 -1
- package/build/_services/base/data.service.js.map +1 -1
- package/package.json +2 -2
- package/src/_modules/usage/usage.controller.spec.ts +25 -0
- package/src/_modules/usage/usage.controller.ts +37 -23
- package/src/_services/base/data.service.spec.ts +55 -0
- package/src/_services/base/data.service.ts +36 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import { Request, Response } from 'express';
|
|
3
3
|
|
|
4
|
-
import { DyFM_HttpCallType, DyFM_RelativeDate } from '@futdevpro/fsm-dynamo';
|
|
4
|
+
import { DyFM_HttpCallType, DyFM_Log, DyFM_RelativeDate } from '@futdevpro/fsm-dynamo';
|
|
5
5
|
import {
|
|
6
6
|
DyFM_usageModule_settings, DyFM_UsageSession
|
|
7
7
|
} from '@futdevpro/fsm-dynamo/usage';
|
|
@@ -36,7 +36,8 @@ export class DyNTS_Usage_Controller extends DyNTS_Controller {
|
|
|
36
36
|
let username: string;
|
|
37
37
|
|
|
38
38
|
try {
|
|
39
|
-
|
|
39
|
+
// BFR-FDPPAL-001: optional-auth módban nincs auth service — a session issuer ilyenkor 'unknown'
|
|
40
|
+
username = this.authService?.getUsernameFromRequest(req) ?? 'unknown';
|
|
40
41
|
} catch {
|
|
41
42
|
username = 'unknown';
|
|
42
43
|
}
|
|
@@ -99,28 +100,41 @@ export class DyNTS_Usage_Controller extends DyNTS_Controller {
|
|
|
99
100
|
],
|
|
100
101
|
}),
|
|
101
102
|
|
|
102
|
-
new DyNTS_Endpoint_Params({
|
|
103
|
-
name: 'getUsageData',
|
|
104
|
-
type: DyFM_HttpCallType.get,
|
|
105
|
-
endpoint: DyFM_usageModule_settings.endPoints.getUsageData,
|
|
106
|
-
preProcesses: [ this.authService.authenticate_tokenPerm_accUsageData ],
|
|
107
|
-
tasks: [
|
|
108
|
-
async (req: Request, res: Response, issuer: string): Promise<void> => {
|
|
109
|
-
const range: DyFM_RelativeDate = req.params.range as DyFM_RelativeDate;
|
|
110
|
-
|
|
111
|
-
const usageService = new DyNTS_Usage_DataService({
|
|
112
|
-
issuer: issuer,
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
await usageService.getUsage(range);
|
|
116
|
-
|
|
117
|
-
res.send(
|
|
118
|
-
usageService.simplifiedDailyUsage
|
|
119
|
-
);
|
|
120
|
-
},
|
|
121
|
-
],
|
|
122
|
-
}),
|
|
123
103
|
];
|
|
104
|
+
|
|
105
|
+
// BFR-FDPPAL-001: optional-auth módban (`auth_settings.optional: true`) a `getAuthService()`
|
|
106
|
+
// `undefined`-ot ad — a védett usage-lekérdezést ilyenkor KIHAGYJUK (az usage-adat auth nélkül
|
|
107
|
+
// nem szolgálható ki), de a boot nem halhat meg TypeError-ral; a session-endpointok élnek.
|
|
108
|
+
if (this.authService) {
|
|
109
|
+
this.endpoints.push(
|
|
110
|
+
new DyNTS_Endpoint_Params({
|
|
111
|
+
name: 'getUsageData',
|
|
112
|
+
type: DyFM_HttpCallType.get,
|
|
113
|
+
endpoint: DyFM_usageModule_settings.endPoints.getUsageData,
|
|
114
|
+
preProcesses: [ this.authService.authenticate_tokenPerm_accUsageData ],
|
|
115
|
+
tasks: [
|
|
116
|
+
async (req: Request, res: Response, issuer: string): Promise<void> => {
|
|
117
|
+
const range: DyFM_RelativeDate = req.params.range as DyFM_RelativeDate;
|
|
118
|
+
|
|
119
|
+
const usageService = new DyNTS_Usage_DataService({
|
|
120
|
+
issuer: issuer,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
await usageService.getUsage(range);
|
|
124
|
+
|
|
125
|
+
res.send(
|
|
126
|
+
usageService.simplifiedDailyUsage
|
|
127
|
+
);
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
}),
|
|
131
|
+
);
|
|
132
|
+
} else {
|
|
133
|
+
DyFM_Log.warn(
|
|
134
|
+
'DyNTS_Usage_Controller: no auth service (optional-auth mode) - ' +
|
|
135
|
+
'the protected "getUsageData" endpoint is SKIPPED; session endpoints remain available.'
|
|
136
|
+
);
|
|
137
|
+
}
|
|
124
138
|
}
|
|
125
139
|
}
|
|
126
140
|
|
|
@@ -725,5 +725,60 @@ describe('| DyNTS_DataService', () => {
|
|
|
725
725
|
expect(r.changedFields).toEqual(['email']);
|
|
726
726
|
});
|
|
727
727
|
});
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* BFR-FDPTOKENSERVICE-011 — a validátor tömb-ága: egy OPCIONÁLIS `X[]`+`subObjectParams` mező
|
|
731
|
+
* `undefined` értéke (a normál „még nincs elem" eset) NEM szállhat el `forEach`-TypeError-ral.
|
|
732
|
+
* A required-hiányt a VP8-ág fogja — az `Array.isArray` őr nem gyengít semmit.
|
|
733
|
+
*/
|
|
734
|
+
describe('| validateForSave — opcionális tömb-mező subObjectParams-szal (BFR-FDPTOKENSERVICE-011)', (): void => {
|
|
735
|
+
class WithOptArr extends DyFM_Metadata {
|
|
736
|
+
name?: string;
|
|
737
|
+
invoices?: { num?: string }[];
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const buildParams = (required: boolean): DyFM_DataModel_Params<WithOptArr> =>
|
|
741
|
+
new DyFM_DataModel_Params<WithOptArr>({
|
|
742
|
+
dataName: 'with_opt_arr',
|
|
743
|
+
properties: {
|
|
744
|
+
invoices: {
|
|
745
|
+
key: 'invoices',
|
|
746
|
+
type: DyFM_BasicProperty_Type.array,
|
|
747
|
+
required: required,
|
|
748
|
+
subObjectParams: {
|
|
749
|
+
num: { key: 'num', type: DyFM_BasicProperty_Type.string, required: true },
|
|
750
|
+
},
|
|
751
|
+
},
|
|
752
|
+
},
|
|
753
|
+
});
|
|
754
|
+
|
|
755
|
+
it('| undefined opcionális tömb → NEM dob (a mentés nem bukhat a hiányzó elemeken)', async (): Promise<void> => {
|
|
756
|
+
const optSvc = new DyNTS_DataService<WithOptArr>(
|
|
757
|
+
Object.assign(new WithOptArr(), { name: 'x' }), buildParams(false), 'test-issuer',
|
|
758
|
+
);
|
|
759
|
+
await expectAsync(optSvc.validateForSave()).toBeResolved();
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
it('| tényleges tömbnél a sub-validáció VÁLTOZATLANUL fut (required sub-mező hiánya → 400)', async (): Promise<void> => {
|
|
763
|
+
const optSvc = new DyNTS_DataService<WithOptArr>(
|
|
764
|
+
Object.assign(new WithOptArr(), { name: 'x', invoices: [ {} ] }), buildParams(false), 'test-issuer',
|
|
765
|
+
);
|
|
766
|
+
let thrown: any = null;
|
|
767
|
+
try { await optSvc.validateForSave(); } catch (e) { thrown = e; }
|
|
768
|
+
expect(thrown).not.toBeNull();
|
|
769
|
+
expect(DyFM_Error.getErrorStatus(thrown)).toBe(400);
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
it('| required tömb hiánya továbbra is validációs 400 (az őr nem gyengít; hiányra a VP1 required-ág fut)', async (): Promise<void> => {
|
|
773
|
+
const reqSvc = new DyNTS_DataService<WithOptArr>(
|
|
774
|
+
Object.assign(new WithOptArr(), { name: 'x' }), buildParams(true), 'test-issuer',
|
|
775
|
+
);
|
|
776
|
+
let thrown: any = null;
|
|
777
|
+
try { await reqSvc.validateForSave(); } catch (e) { thrown = e; }
|
|
778
|
+
expect(thrown).not.toBeNull();
|
|
779
|
+
expect(DyFM_Error.getErrorStatus(thrown)).toBe(400);
|
|
780
|
+
expect(DyFM_Error.getErrorCode(thrown)).toContain('DyNTS-DS0-VP');
|
|
781
|
+
});
|
|
782
|
+
});
|
|
728
783
|
});
|
|
729
784
|
|
|
@@ -47,6 +47,13 @@ import { DyNTS_DBService } from './db.service';
|
|
|
47
47
|
*
|
|
48
48
|
* Explicit `fields: ['_id']`-vel meg vizsgaltathato, ha valamiert kell.
|
|
49
49
|
*/
|
|
50
|
+
/**
|
|
51
|
+
* BFR-OVERSEER-011 (b): e dokumentum-szám fölött a searchData memóriabeli lapozása warn-t ad —
|
|
52
|
+
* a teljes-kollekció-betöltés hívóhelyei incidens ELŐTT láthatóvá válnak (mérve: 5 hívóhely,
|
|
53
|
+
* GC-thrash, ~11 óra akadozó CI).
|
|
54
|
+
*/
|
|
55
|
+
const DyNTS_SEARCHDATA_INMEMORY_WARN_THRESHOLD: number = 1000;
|
|
56
|
+
|
|
50
57
|
const COMPARE_DATA_METADATA_SKIP_FIELDS: ReadonlySet<string> = new Set<string>([
|
|
51
58
|
'_id',
|
|
52
59
|
'__created',
|
|
@@ -1695,7 +1702,10 @@ export class DyNTS_DataService<T extends DyFM_Metadata> {
|
|
|
1695
1702
|
});
|
|
1696
1703
|
}
|
|
1697
1704
|
|
|
1698
|
-
|
|
1705
|
+
// BFR-FDPTOKENSERVICE-011: csak TÉNYLEGES tömbnél szállunk le a sub-objektumokba — egy
|
|
1706
|
+
// OPCIONÁLIS `X[]` mező `undefined` értéke (a normál „még nincs elem" eset) nem dönthet
|
|
1707
|
+
// `forEach`-TypeError-ba; a required-hiányt a fenti VP8-ág fogja, ez az őr nem gyengít.
|
|
1708
|
+
if (propertyParams.subObjectParams && Array.isArray(propertyValue)) {
|
|
1699
1709
|
propertyValue.forEach((item: any): void => {
|
|
1700
1710
|
this.validateDataByPropertyParams(item, propertyParams.subObjectParams, propertyParams);
|
|
1701
1711
|
});
|
|
@@ -1780,9 +1790,20 @@ export class DyNTS_DataService<T extends DyFM_Metadata> {
|
|
|
1780
1790
|
/**
|
|
1781
1791
|
* Search the data list based on the search query
|
|
1782
1792
|
* Sorts and filters the data list based on the search query
|
|
1783
|
-
*
|
|
1793
|
+
*
|
|
1794
|
+
* ⚠️ **BFR-OVERSEER-011 — a lapozás MEMÓRIÁBAN történik, a `pageSize` NEM DB-limit!**
|
|
1795
|
+
* A metódus a TELJES (elő-szűrt) kollekciót betölti (`findDataList`/`getAll` limit nélkül),
|
|
1796
|
+
* memóriában rendez/szűr, és CSAK UTÁNA `slice`-ol — egy `searchData({ pageSize: 1 })` is a
|
|
1797
|
+
* teljes kollekciót húzza a heap-be (mért incidens: GC-thrash + event-loop-megállás nagy
|
|
1798
|
+
* kollekción). **Mikor MIT használj:**
|
|
1799
|
+
* - lapozott lekérdezés nagy kollekción → `DyNTS_DBService.findWithPaging(filter, page, pageSize, sort)`
|
|
1800
|
+
* (DB-szintű `.skip().limit()`);
|
|
1801
|
+
* - „legfrissebb N" → `findDataList(filter, undefined, { projection, sort, limit })`;
|
|
1802
|
+
* - `searchData` CSAK kis/korlátos kollekcióra vagy már memóriában lévő `dataList`-re való
|
|
1803
|
+
* (komplex, memóriabeli szűrés/rendezés).
|
|
1804
|
+
*
|
|
1784
1805
|
* NOTE: For more simple and with better performance and if you don't need sorting, use findDataList instead.
|
|
1785
|
-
*
|
|
1806
|
+
*
|
|
1786
1807
|
* @example
|
|
1787
1808
|
* ```ts
|
|
1788
1809
|
* const searchResult = await this.searchData({
|
|
@@ -1860,6 +1881,18 @@ export class DyNTS_DataService<T extends DyFM_Metadata> {
|
|
|
1860
1881
|
} else {
|
|
1861
1882
|
dataList = await this.getAll();
|
|
1862
1883
|
}
|
|
1884
|
+
|
|
1885
|
+
// BFR-OVERSEER-011 (b) runtime-guard: a memóriabeli lapozás a TELJES kollekciót húzza be —
|
|
1886
|
+
// nagy halmazon ez GC-thrash-t okoz (mért incidens: ~43k dok/host, event-loop-megállás).
|
|
1887
|
+
// A warn a hívóhelyet AZONNAL felszínre hozza, incidens előtt.
|
|
1888
|
+
if (dataList.length > DyNTS_SEARCHDATA_INMEMORY_WARN_THRESHOLD) {
|
|
1889
|
+
DyFM_Log.warn(
|
|
1890
|
+
`searchData('${this.dataParams.dataName}'): ${dataList.length} docs loaded INTO MEMORY for ` +
|
|
1891
|
+
`in-memory paging (pageSize is NOT a DB-limit!). For paged queries use ` +
|
|
1892
|
+
`DyNTS_DBService.findWithPaging(); for "latest N" use findDataList(filter, undefined, ` +
|
|
1893
|
+
`{ projection, sort, limit }).`
|
|
1894
|
+
);
|
|
1895
|
+
}
|
|
1863
1896
|
} else {
|
|
1864
1897
|
if (Object.keys(preFilter).length) {
|
|
1865
1898
|
DyFM_Log.H_warn(
|