@jmlq/logger 0.1.0-alpha.11 → 0.1.0-alpha.13
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/dist/{src/application → application}/factory/logger.factory.d.ts +2 -2
- package/dist/{src/domain → domain}/ports/index.d.ts +0 -1
- package/dist/{src/domain → domain}/ports/index.js +1 -1
- package/dist/{src/domain/ports/logger-service.port.d.ts → domain/ports/logger.port.d.ts} +1 -5
- package/dist/index.d.ts +3 -0
- package/dist/index.js +20 -0
- package/package.json +5 -2
- package/dist/examples/data-source-service.example.d.ts +0 -3
- package/dist/examples/data-source-service.example.js +0 -174
- package/dist/examples/flush-buffers-use-case.example.d.ts +0 -3
- package/dist/examples/flush-buffers-use-case.example.js +0 -60
- package/dist/examples/get-logs-use-case.example.d.ts +0 -3
- package/dist/examples/get-logs-use-case.example.js +0 -110
- package/dist/examples/index.example.d.ts +0 -8
- package/dist/examples/index.example.js +0 -116
- package/dist/examples/logger-factory.example.d.ts +0 -39
- package/dist/examples/logger-factory.example.js +0 -158
- package/dist/examples/normalize-message.example.d.ts +0 -3
- package/dist/examples/normalize-message.example.js +0 -80
- package/dist/examples/pii-redactor.example.d.ts +0 -3
- package/dist/examples/pii-redactor.example.js +0 -129
- package/dist/examples/save-log-use-case.example.d.ts +0 -3
- package/dist/examples/save-log-use-case.example.js +0 -150
- package/dist/examples/to-log-level.example.d.ts +0 -3
- package/dist/examples/to-log-level.example.js +0 -49
- package/dist/examples/to-pii-regex.example.d.ts +0 -3
- package/dist/examples/to-pii-regex.example.js +0 -75
- package/dist/src/application/factory/create-logger.d.ts +0 -2
- package/dist/src/application/factory/create-logger.js +0 -29
- package/dist/src/domain/ports/logger-service.port.js +0 -2
- package/dist/src/domain/ports/logger.port.d.ts +0 -10
- package/dist/src/domain/services/pii-redactor.d.ts +0 -10
- package/dist/src/domain/services/pii-redactor.js +0 -68
- package/dist/src/index.d.ts +0 -6
- package/dist/src/index.js +0 -22
- package/dist/src/infrastructure/datasources/in-memory-log.datasource.d.ts +0 -1
- package/dist/src/infrastructure/datasources/in-memory-log.datasource.js +0 -2
- package/dist/src/infrastructure/datasources/index.d.ts +0 -1
- package/dist/src/infrastructure/datasources/index.js +0 -17
- package/dist/tests/application/factory/logger-factory.spec.d.ts +0 -1
- package/dist/tests/application/factory/logger-factory.spec.js +0 -161
- package/dist/tests/application/use-cases/flush-buffers.use-case.spec.d.ts +0 -1
- package/dist/tests/application/use-cases/flush-buffers.use-case.spec.js +0 -38
- package/dist/tests/application/use-cases/get-logs.use-case.spec.d.ts +0 -1
- package/dist/tests/application/use-cases/get-logs.use-case.spec.js +0 -114
- package/dist/tests/application/use-cases/save-log.use-case.spec.d.ts +0 -1
- package/dist/tests/application/use-cases/save-log.use-case.spec.js +0 -138
- package/dist/tests/domain/services/log-level.service.spec.d.ts +0 -1
- package/dist/tests/domain/services/log-level.service.spec.js +0 -68
- package/dist/tests/domain/services/normalize-message.spec.d.ts +0 -1
- package/dist/tests/domain/services/normalize-message.spec.js +0 -83
- package/dist/tests/domain/services/pii-redactor.spec.d.ts +0 -1
- package/dist/tests/domain/services/pii-redactor.spec.js +0 -170
- package/dist/tests/domain/services/to-pii-regex.spec.d.ts +0 -1
- package/dist/tests/domain/services/to-pii-regex.spec.js +0 -82
- package/dist/tests/infrastructure/services/datasource.service.spec.d.ts +0 -1
- package/dist/tests/infrastructure/services/datasource.service.spec.js +0 -128
- package/dist/tests/test-utils/create-pii-redactor-mock.d.ts +0 -5
- package/dist/tests/test-utils/create-pii-redactor-mock.js +0 -10
- /package/dist/{src/application → application}/factory/index.d.ts +0 -0
- /package/dist/{src/application → application}/factory/index.js +0 -0
- /package/dist/{src/application → application}/factory/logger.factory.js +0 -0
- /package/dist/{src/application → application}/index.d.ts +0 -0
- /package/dist/{src/application → application}/index.js +0 -0
- /package/dist/{src/application → application}/use-cases/flush-buffers.use-case.d.ts +0 -0
- /package/dist/{src/application → application}/use-cases/flush-buffers.use-case.js +0 -0
- /package/dist/{src/application → application}/use-cases/get-logs.use-case.d.ts +0 -0
- /package/dist/{src/application → application}/use-cases/get-logs.use-case.js +0 -0
- /package/dist/{src/application → application}/use-cases/index.d.ts +0 -0
- /package/dist/{src/application → application}/use-cases/index.js +0 -0
- /package/dist/{src/application → application}/use-cases/save-log.use-case.d.ts +0 -0
- /package/dist/{src/application → application}/use-cases/save-log.use-case.js +0 -0
- /package/dist/{src/domain → domain}/index.d.ts +0 -0
- /package/dist/{src/domain → domain}/index.js +0 -0
- /package/dist/{src/domain → domain}/ports/create-logger-options.port.d.ts +0 -0
- /package/dist/{src/domain → domain}/ports/create-logger-options.port.js +0 -0
- /package/dist/{src/domain → domain}/ports/log-datasource.port.d.ts +0 -0
- /package/dist/{src/domain → domain}/ports/log-datasource.port.js +0 -0
- /package/dist/{src/domain → domain}/ports/logger-factory-config.port.d.ts +0 -0
- /package/dist/{src/domain → domain}/ports/logger-factory-config.port.js +0 -0
- /package/dist/{src/domain → domain}/ports/logger.port.js +0 -0
- /package/dist/{src/domain → domain}/ports/pii-redactor.port.d.ts +0 -0
- /package/dist/{src/domain → domain}/ports/pii-redactor.port.js +0 -0
- /package/dist/{src/domain → domain}/request/get-logs-filter.props.d.ts +0 -0
- /package/dist/{src/domain → domain}/request/get-logs-filter.props.js +0 -0
- /package/dist/{src/domain → domain}/request/index.d.ts +0 -0
- /package/dist/{src/domain → domain}/request/index.js +0 -0
- /package/dist/{src/domain → domain}/request/log.props.d.ts +0 -0
- /package/dist/{src/domain → domain}/request/log.props.js +0 -0
- /package/dist/{src/domain → domain}/request/pii-options.props.d.ts +0 -0
- /package/dist/{src/domain → domain}/request/pii-options.props.js +0 -0
- /package/dist/{src/domain → domain}/request/pii-replacement.props.d.ts +0 -0
- /package/dist/{src/domain → domain}/request/pii-replacement.props.js +0 -0
- /package/dist/{src/domain → domain}/request/save-log.props.d.ts +0 -0
- /package/dist/{src/domain → domain}/request/save-log.props.js +0 -0
- /package/dist/{src/domain → domain}/response/index.d.ts +0 -0
- /package/dist/{src/domain → domain}/response/index.js +0 -0
- /package/dist/{src/domain → domain}/response/log.response.d.ts +0 -0
- /package/dist/{src/domain → domain}/response/log.response.js +0 -0
- /package/dist/{src/domain → domain}/services/index.d.ts +0 -0
- /package/dist/{src/domain → domain}/services/index.js +0 -0
- /package/dist/{src/domain → domain}/services/log-level.service.d.ts +0 -0
- /package/dist/{src/domain → domain}/services/log-level.service.js +0 -0
- /package/dist/{src/domain → domain}/services/message-normalizer.service.d.ts +0 -0
- /package/dist/{src/domain → domain}/services/message-normalizer.service.js +0 -0
- /package/dist/{src/domain → domain}/services/pii-pattern.service.d.ts +0 -0
- /package/dist/{src/domain → domain}/services/pii-pattern.service.js +0 -0
- /package/dist/{src/domain → domain}/services/pii-redactor.service.d.ts +0 -0
- /package/dist/{src/domain → domain}/services/pii-redactor.service.js +0 -0
- /package/dist/{src/domain → domain}/types/index.d.ts +0 -0
- /package/dist/{src/domain → domain}/types/index.js +0 -0
- /package/dist/{src/domain → domain}/types/log-message.type.d.ts +0 -0
- /package/dist/{src/domain → domain}/types/log-message.type.js +0 -0
- /package/dist/{src/domain → domain}/value-objects/index.d.ts +0 -0
- /package/dist/{src/domain → domain}/value-objects/index.js +0 -0
- /package/dist/{src/domain → domain}/value-objects/log-level.vo.d.ts +0 -0
- /package/dist/{src/domain → domain}/value-objects/log-level.vo.js +0 -0
- /package/dist/{src/infrastructure → infrastructure}/index.d.ts +0 -0
- /package/dist/{src/infrastructure → infrastructure}/index.js +0 -0
- /package/dist/{src/infrastructure → infrastructure}/services/data-source-error-handler.type.d.ts +0 -0
- /package/dist/{src/infrastructure → infrastructure}/services/data-source-error-handler.type.js +0 -0
- /package/dist/{src/infrastructure → infrastructure}/services/datasource.service.d.ts +0 -0
- /package/dist/{src/infrastructure → infrastructure}/services/datasource.service.js +0 -0
- /package/dist/{src/infrastructure → infrastructure}/services/index.d.ts +0 -0
- /package/dist/{src/infrastructure → infrastructure}/services/index.js +0 -0
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createLogger = createLogger;
|
|
4
|
-
const services_1 = require("../../domain/services");
|
|
5
|
-
const value_objects_1 = require("../../domain/value-objects");
|
|
6
|
-
const use_cases_1 = require("../use-cases");
|
|
7
|
-
// Factoría de logger. Encapsula composición de casos de uso y servicios
|
|
8
|
-
function createLogger(ds, opts = {}) {
|
|
9
|
-
// Resolve minLevel con un default razonable (INFO)
|
|
10
|
-
const minLevel = opts.minLevel ?? value_objects_1.LogLevel.INFO;
|
|
11
|
-
// Mantén compatibilidad con redactPII
|
|
12
|
-
const piiEnabled = opts.pii?.enabled ?? opts.redactPII ?? false;
|
|
13
|
-
const redactor = new services_1.PiiRedactor({ enabled: piiEnabled, ...opts.pii });
|
|
14
|
-
const save = new use_cases_1.SaveLogUseCase({ ds, minLevel, redactor });
|
|
15
|
-
const flushUC = new use_cases_1.FlushBuffersUseCase(ds);
|
|
16
|
-
// Helper emisor; si en el futuro se necesita encolar, aquí es el lugar
|
|
17
|
-
const emit = (lvl, message, meta) => save.execute(lvl, message, meta);
|
|
18
|
-
return {
|
|
19
|
-
trace: (m, meta) => emit(value_objects_1.LogLevel.TRACE, m, meta),
|
|
20
|
-
debug: (m, meta) => emit(value_objects_1.LogLevel.DEBUG, m, meta),
|
|
21
|
-
info: (m, meta) => emit(value_objects_1.LogLevel.INFO, m, meta),
|
|
22
|
-
warn: (m, meta) => emit(value_objects_1.LogLevel.WARN, m, meta),
|
|
23
|
-
error: (m, meta) => emit(value_objects_1.LogLevel.ERROR, m, meta),
|
|
24
|
-
fatal: (m, meta) => emit(value_objects_1.LogLevel.FATAL, m, meta),
|
|
25
|
-
// Gestión de ciclo de vida → delega en caso de uso / puerto
|
|
26
|
-
flush: () => flushUC.execute(),
|
|
27
|
-
dispose: () => ds.dispose?.() ?? Promise.resolve(),
|
|
28
|
-
};
|
|
29
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export interface ILogger {
|
|
2
|
-
trace: (message: unknown, meta?: unknown) => void | Promise<void>;
|
|
3
|
-
debug: (message: unknown, meta?: unknown) => void | Promise<void>;
|
|
4
|
-
info: (message: unknown, meta?: unknown) => void | Promise<void>;
|
|
5
|
-
warn: (message: unknown, meta?: unknown) => void | Promise<void>;
|
|
6
|
-
error: (message: unknown, meta?: unknown) => void | Promise<void>;
|
|
7
|
-
fatal: (message: unknown, meta?: unknown) => void | Promise<void>;
|
|
8
|
-
flush?: () => Promise<void>;
|
|
9
|
-
dispose?: () => Promise<void>;
|
|
10
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { IPiiRedactor } from "../ports";
|
|
2
|
-
import { IPiiOptionsProps } from "../request";
|
|
3
|
-
export declare class PiiRedactor implements IPiiRedactor {
|
|
4
|
-
private props;
|
|
5
|
-
constructor(props?: IPiiOptionsProps);
|
|
6
|
-
updateOptions(opts: IPiiOptionsProps): void;
|
|
7
|
-
redact<T = unknown>(value: T): T;
|
|
8
|
-
private applyPatterns;
|
|
9
|
-
private redactDeep;
|
|
10
|
-
}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PiiRedactor = void 0;
|
|
4
|
-
const pii_pattern_service_1 = require("./pii-pattern.service");
|
|
5
|
-
class PiiRedactor {
|
|
6
|
-
constructor(props = {}) {
|
|
7
|
-
this.props = props;
|
|
8
|
-
}
|
|
9
|
-
updateOptions(opts) {
|
|
10
|
-
// merge superficial de opciones; no muta referencias externas
|
|
11
|
-
this.props = { ...this.props, ...opts };
|
|
12
|
-
}
|
|
13
|
-
redact(value) {
|
|
14
|
-
if (!this.props.enabled)
|
|
15
|
-
return value; // early exit si PII apagado
|
|
16
|
-
if (value == null)
|
|
17
|
-
return value;
|
|
18
|
-
// Si es string → aplicar patrones
|
|
19
|
-
if (typeof value === "string") {
|
|
20
|
-
return this.applyPatterns(value);
|
|
21
|
-
}
|
|
22
|
-
// Si es objeto/array → redacción profunda si corresponde
|
|
23
|
-
if (typeof value === "object") {
|
|
24
|
-
return this.redactDeep(value);
|
|
25
|
-
}
|
|
26
|
-
// Otros tipos (number, boolean, function) no se redactan
|
|
27
|
-
return value;
|
|
28
|
-
}
|
|
29
|
-
applyPatterns(input) {
|
|
30
|
-
const patterns = this.props.patterns ?? [];
|
|
31
|
-
let out = input;
|
|
32
|
-
for (const p of patterns) {
|
|
33
|
-
const rx = (0, pii_pattern_service_1.toPiiRegex)(p);
|
|
34
|
-
// replace con patrón RegExp
|
|
35
|
-
out = out.replace(rx, p.replaceWith);
|
|
36
|
-
}
|
|
37
|
-
return out;
|
|
38
|
-
}
|
|
39
|
-
redactDeep(obj) {
|
|
40
|
-
// Evita mutar el objeto original → copia superficial
|
|
41
|
-
const clone = Array.isArray(obj)
|
|
42
|
-
? [...obj]
|
|
43
|
-
: { ...obj };
|
|
44
|
-
const { whitelistKeys = [], blacklistKeys = [], deep = true } = this.props;
|
|
45
|
-
for (const key of Object.keys(clone)) {
|
|
46
|
-
const val = clone[key];
|
|
47
|
-
// Si está en whitelist, se respeta siempre
|
|
48
|
-
if (whitelistKeys.includes(key))
|
|
49
|
-
continue;
|
|
50
|
-
// Si está en blacklist y es string → reemplazar completamente
|
|
51
|
-
if (blacklistKeys.includes(key)) {
|
|
52
|
-
clone[key] = "[REDACTED]"; // fuerza redacción total de claves sensibles
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
// Para strings comunes → aplicar patrones
|
|
56
|
-
if (typeof val === "string") {
|
|
57
|
-
clone[key] = this.applyPatterns(val);
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
// Para objetos/arrays y deep===true → recursión
|
|
61
|
-
if (deep && val && typeof val === "object") {
|
|
62
|
-
clone[key] = this.redactDeep(val);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return clone;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
exports.PiiRedactor = PiiRedactor;
|
package/dist/src/index.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export * from "./application/factory/logger.factory";
|
|
2
|
-
export type { ILoggerService, ILoggerFactoryConfig, ILogDatasource, } from "./domain/ports";
|
|
3
|
-
export type { LogMessage } from "./domain/types";
|
|
4
|
-
export type { IGetLogsFilterProps } from "./domain/request";
|
|
5
|
-
export type { ILogResponse } from "./domain/response";
|
|
6
|
-
export { LogLevel } from "./domain/value-objects";
|
package/dist/src/index.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.LogLevel = void 0;
|
|
18
|
-
// API principal de @jmlq/logger
|
|
19
|
-
__exportStar(require("./application/factory/logger.factory"), exports);
|
|
20
|
-
// Enums
|
|
21
|
-
var value_objects_1 = require("./domain/value-objects");
|
|
22
|
-
Object.defineProperty(exports, "LogLevel", { enumerable: true, get: function () { return value_objects_1.LogLevel; } });
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./in-memory-log.datasource";
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./in-memory-log.datasource"), exports);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const logger_factory_1 = require("../../../src/application/factory/logger.factory");
|
|
4
|
-
const value_objects_1 = require("../../../src/domain/value-objects");
|
|
5
|
-
describe("LoggerFactory (behavioral)", () => {
|
|
6
|
-
it("debe respetar el minLevel por defecto (INFO): no guardar DEBUG, sí INFO y ERROR", async () => {
|
|
7
|
-
const savedLogs = [];
|
|
8
|
-
const ds = {
|
|
9
|
-
name: "single-ds",
|
|
10
|
-
save: jest.fn(async (log) => {
|
|
11
|
-
savedLogs.push(log);
|
|
12
|
-
}),
|
|
13
|
-
find: jest.fn(),
|
|
14
|
-
flush: jest.fn(),
|
|
15
|
-
dispose: jest.fn(),
|
|
16
|
-
};
|
|
17
|
-
// No pasamos minLevel → debe usar LogLevel.INFO
|
|
18
|
-
const logger = logger_factory_1.LoggerFactory.create({
|
|
19
|
-
datasources: ds,
|
|
20
|
-
// sin redactor ni opciones, usa PiiRedactor por defecto
|
|
21
|
-
});
|
|
22
|
-
await logger.debug("debug ignorado");
|
|
23
|
-
await logger.info("info guardado");
|
|
24
|
-
await logger.error("error guardado");
|
|
25
|
-
// Se debe haber llamado save solo 2 veces (INFO y ERROR)
|
|
26
|
-
expect(ds.save.mock.calls.length).toBe(2);
|
|
27
|
-
expect(savedLogs[0].level).toBe(value_objects_1.LogLevel.INFO);
|
|
28
|
-
expect(savedLogs[0].message).toBe("info guardado");
|
|
29
|
-
expect(savedLogs[1].level).toBe(value_objects_1.LogLevel.ERROR);
|
|
30
|
-
expect(savedLogs[1].message).toBe("error guardado");
|
|
31
|
-
});
|
|
32
|
-
it("debe respetar un minLevel personalizado (por ejemplo ERROR)", async () => {
|
|
33
|
-
const savedLogs = [];
|
|
34
|
-
const ds = {
|
|
35
|
-
name: "single-ds",
|
|
36
|
-
save: jest.fn(async (log) => {
|
|
37
|
-
savedLogs.push(log);
|
|
38
|
-
}),
|
|
39
|
-
find: jest.fn(),
|
|
40
|
-
flush: jest.fn(),
|
|
41
|
-
dispose: jest.fn(),
|
|
42
|
-
};
|
|
43
|
-
const logger = logger_factory_1.LoggerFactory.create({
|
|
44
|
-
datasources: ds,
|
|
45
|
-
minLevel: value_objects_1.LogLevel.ERROR,
|
|
46
|
-
});
|
|
47
|
-
await logger.info("esto NO debería loggearse");
|
|
48
|
-
await logger.error("esto SÍ debería loggearse");
|
|
49
|
-
expect(ds.save.mock.calls.length).toBe(1);
|
|
50
|
-
expect(savedLogs[0].level).toBe(value_objects_1.LogLevel.ERROR);
|
|
51
|
-
expect(savedLogs[0].message).toBe("esto SÍ debería loggearse");
|
|
52
|
-
});
|
|
53
|
-
it("debe componer múltiples datasources (fan-out) y enviar el log a todos", async () => {
|
|
54
|
-
const dsA = {
|
|
55
|
-
name: "A",
|
|
56
|
-
save: jest.fn(),
|
|
57
|
-
find: jest.fn(),
|
|
58
|
-
flush: jest.fn(),
|
|
59
|
-
dispose: jest.fn(),
|
|
60
|
-
};
|
|
61
|
-
const dsB = {
|
|
62
|
-
name: "B",
|
|
63
|
-
save: jest.fn(),
|
|
64
|
-
find: jest.fn(),
|
|
65
|
-
flush: jest.fn(),
|
|
66
|
-
dispose: jest.fn(),
|
|
67
|
-
};
|
|
68
|
-
const logger = logger_factory_1.LoggerFactory.create({
|
|
69
|
-
datasources: [dsA, dsB],
|
|
70
|
-
minLevel: value_objects_1.LogLevel.TRACE, // para que no filtre nada
|
|
71
|
-
});
|
|
72
|
-
await logger.info("mensaje fan-out");
|
|
73
|
-
expect(dsA.save).toHaveBeenCalledTimes(1);
|
|
74
|
-
expect(dsB.save).toHaveBeenCalledTimes(1);
|
|
75
|
-
const logA = dsA.save.mock.calls[0][0];
|
|
76
|
-
const logB = dsB.save.mock.calls[0][0];
|
|
77
|
-
expect(logA.message).toBe("mensaje fan-out");
|
|
78
|
-
expect(logB.message).toBe("mensaje fan-out");
|
|
79
|
-
});
|
|
80
|
-
it("debe usar el redactor proporcionado en config para procesar meta", async () => {
|
|
81
|
-
const savedLogs = [];
|
|
82
|
-
const ds = {
|
|
83
|
-
name: "ds",
|
|
84
|
-
save: jest.fn(async (log) => {
|
|
85
|
-
savedLogs.push(log);
|
|
86
|
-
}),
|
|
87
|
-
find: jest.fn(),
|
|
88
|
-
flush: jest.fn(),
|
|
89
|
-
dispose: jest.fn(),
|
|
90
|
-
};
|
|
91
|
-
// Redactor custom que marca la meta
|
|
92
|
-
const redactor = {
|
|
93
|
-
redact: jest.fn((value) => {
|
|
94
|
-
if (value && typeof value === "object") {
|
|
95
|
-
return {
|
|
96
|
-
...value,
|
|
97
|
-
__redacted: true,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
return value;
|
|
101
|
-
}),
|
|
102
|
-
updateOptions: jest.fn(),
|
|
103
|
-
};
|
|
104
|
-
const logger = logger_factory_1.LoggerFactory.create({
|
|
105
|
-
datasources: ds,
|
|
106
|
-
minLevel: value_objects_1.LogLevel.INFO,
|
|
107
|
-
redactor,
|
|
108
|
-
});
|
|
109
|
-
const metaOriginal = { userId: "123" };
|
|
110
|
-
await logger.info("mensaje con meta", metaOriginal);
|
|
111
|
-
expect(redactor.redact).toHaveBeenCalledWith(metaOriginal);
|
|
112
|
-
const logGuardado = savedLogs[0];
|
|
113
|
-
expect(logGuardado.meta).toEqual({
|
|
114
|
-
userId: "123",
|
|
115
|
-
__redacted: true,
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
it("getLogs debe delegar a find del datasource con el filtro adecuado y retornar el resultado", async () => {
|
|
119
|
-
const logsSimulados = [
|
|
120
|
-
{
|
|
121
|
-
level: value_objects_1.LogLevel.ERROR,
|
|
122
|
-
message: "falló algo",
|
|
123
|
-
timestamp: Date.now(),
|
|
124
|
-
},
|
|
125
|
-
];
|
|
126
|
-
const ds = {
|
|
127
|
-
name: "ds",
|
|
128
|
-
save: jest.fn(),
|
|
129
|
-
find: jest.fn().mockResolvedValue(logsSimulados),
|
|
130
|
-
flush: jest.fn(),
|
|
131
|
-
dispose: jest.fn(),
|
|
132
|
-
};
|
|
133
|
-
const logger = logger_factory_1.LoggerFactory.create({
|
|
134
|
-
datasources: ds,
|
|
135
|
-
minLevel: value_objects_1.LogLevel.INFO,
|
|
136
|
-
});
|
|
137
|
-
const filter = {
|
|
138
|
-
levelMin: value_objects_1.LogLevel.ERROR,
|
|
139
|
-
query: "falló",
|
|
140
|
-
};
|
|
141
|
-
const result = await logger.getLogs(filter);
|
|
142
|
-
expect(ds.find).toHaveBeenCalledTimes(1);
|
|
143
|
-
expect(ds.find).toHaveBeenCalledWith(filter);
|
|
144
|
-
expect(result).toBe(logsSimulados);
|
|
145
|
-
});
|
|
146
|
-
it("flush debe delegar al flush del datasource (o composite) si existe", async () => {
|
|
147
|
-
const ds = {
|
|
148
|
-
name: "ds",
|
|
149
|
-
save: jest.fn(),
|
|
150
|
-
find: jest.fn(),
|
|
151
|
-
flush: jest.fn(),
|
|
152
|
-
dispose: jest.fn(),
|
|
153
|
-
};
|
|
154
|
-
const logger = logger_factory_1.LoggerFactory.create({
|
|
155
|
-
datasources: ds,
|
|
156
|
-
minLevel: value_objects_1.LogLevel.INFO,
|
|
157
|
-
});
|
|
158
|
-
await logger.flush();
|
|
159
|
-
expect(ds.flush).toHaveBeenCalledTimes(1);
|
|
160
|
-
});
|
|
161
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const use_cases_1 = require("../../../src/application/use-cases");
|
|
4
|
-
describe("FlushBuffersUseCase", () => {
|
|
5
|
-
it("debe llamar a ds.flush cuando está implementado", async () => {
|
|
6
|
-
const flushMock = jest.fn().mockResolvedValue(undefined);
|
|
7
|
-
const ds = {
|
|
8
|
-
name: "memory",
|
|
9
|
-
save: jest.fn().mockResolvedValue(undefined),
|
|
10
|
-
flush: flushMock,
|
|
11
|
-
};
|
|
12
|
-
const useCase = new use_cases_1.FlushBuffersUseCase(ds);
|
|
13
|
-
await useCase.execute();
|
|
14
|
-
expect(flushMock).toHaveBeenCalledTimes(1);
|
|
15
|
-
expect(flushMock).toHaveBeenCalledWith(); // sin argumentos
|
|
16
|
-
});
|
|
17
|
-
it("no debe fallar cuando el datasource no implementa flush", async () => {
|
|
18
|
-
const ds = {
|
|
19
|
-
name: "memory",
|
|
20
|
-
save: jest.fn().mockResolvedValue(undefined),
|
|
21
|
-
// flush NO definido
|
|
22
|
-
};
|
|
23
|
-
const useCase = new use_cases_1.FlushBuffersUseCase(ds);
|
|
24
|
-
// execute simplemente debería resolverse sin lanzar error
|
|
25
|
-
await expect(useCase.execute()).resolves.toBeUndefined();
|
|
26
|
-
});
|
|
27
|
-
it("debe propagar el error si ds.flush rechaza", async () => {
|
|
28
|
-
const error = new Error("flush failed");
|
|
29
|
-
const flushMock = jest.fn().mockRejectedValue(error);
|
|
30
|
-
const ds = {
|
|
31
|
-
name: "memory",
|
|
32
|
-
save: jest.fn().mockResolvedValue(undefined),
|
|
33
|
-
flush: flushMock,
|
|
34
|
-
};
|
|
35
|
-
const useCase = new use_cases_1.FlushBuffersUseCase(ds);
|
|
36
|
-
await expect(useCase.execute()).rejects.toBe(error);
|
|
37
|
-
});
|
|
38
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const get_logs_use_case_1 = require("../../../src/application/use-cases/get-logs.use-case");
|
|
4
|
-
const value_objects_1 = require("../../../src/domain/value-objects");
|
|
5
|
-
describe("GetLogsUseCase", () => {
|
|
6
|
-
it("debe retornar [] si el datasource no implementa find", async () => {
|
|
7
|
-
const dsSinFind = {
|
|
8
|
-
save: jest.fn().mockResolvedValue(undefined),
|
|
9
|
-
name: "no-find-ds",
|
|
10
|
-
// sin find
|
|
11
|
-
};
|
|
12
|
-
const useCase = new get_logs_use_case_1.GetLogsUseCase(dsSinFind);
|
|
13
|
-
const result = await useCase.execute();
|
|
14
|
-
expect(result).toEqual([]);
|
|
15
|
-
});
|
|
16
|
-
it("debe llamar a ds.find con undefined cuando no se pasa filtro y find está implementado", async () => {
|
|
17
|
-
const logsSimulados = [
|
|
18
|
-
{
|
|
19
|
-
level: value_objects_1.LogLevel.INFO,
|
|
20
|
-
message: "log 1",
|
|
21
|
-
timestamp: Date.now(),
|
|
22
|
-
},
|
|
23
|
-
];
|
|
24
|
-
const findMock = jest.fn(async () => logsSimulados);
|
|
25
|
-
const dsMock = {
|
|
26
|
-
save: jest.fn().mockResolvedValue(undefined),
|
|
27
|
-
find: findMock,
|
|
28
|
-
name: "memory",
|
|
29
|
-
};
|
|
30
|
-
const useCase = new get_logs_use_case_1.GetLogsUseCase(dsMock);
|
|
31
|
-
const result = await useCase.execute();
|
|
32
|
-
expect(findMock).toHaveBeenCalledTimes(1);
|
|
33
|
-
expect(findMock).toHaveBeenCalledWith(undefined);
|
|
34
|
-
expect(result).toBe(logsSimulados);
|
|
35
|
-
});
|
|
36
|
-
it("debe sanitizar limit (tope 5000, ignora <= 0) y offset (solo >= 0) antes de llamar a ds.find", async () => {
|
|
37
|
-
let capturedFilter;
|
|
38
|
-
const findMock = jest.fn(async (filter) => {
|
|
39
|
-
capturedFilter = filter;
|
|
40
|
-
return [];
|
|
41
|
-
});
|
|
42
|
-
const dsMock = {
|
|
43
|
-
save: jest.fn().mockResolvedValue(undefined),
|
|
44
|
-
find: findMock,
|
|
45
|
-
name: "memory",
|
|
46
|
-
};
|
|
47
|
-
const useCase = new get_logs_use_case_1.GetLogsUseCase(dsMock);
|
|
48
|
-
const rawFilter = {
|
|
49
|
-
levelMin: value_objects_1.LogLevel.WARN,
|
|
50
|
-
since: 1000,
|
|
51
|
-
until: 2000,
|
|
52
|
-
limit: 10000, // debería ser recortado a 5000
|
|
53
|
-
offset: -5, // debería convertirse en undefined
|
|
54
|
-
query: "error",
|
|
55
|
-
};
|
|
56
|
-
await useCase.execute(rawFilter);
|
|
57
|
-
expect(findMock).toHaveBeenCalledTimes(1);
|
|
58
|
-
expect(capturedFilter).toBeDefined();
|
|
59
|
-
const safe = capturedFilter;
|
|
60
|
-
expect(safe.levelMin).toBe(value_objects_1.LogLevel.WARN);
|
|
61
|
-
expect(safe.since).toBe(1000);
|
|
62
|
-
expect(safe.until).toBe(2000);
|
|
63
|
-
expect(safe.query).toBe("error");
|
|
64
|
-
// Sanitización:
|
|
65
|
-
expect(safe.limit).toBe(5000);
|
|
66
|
-
expect(safe.offset).toBeUndefined();
|
|
67
|
-
});
|
|
68
|
-
it("debe eliminar limit cuando es <= 0 y offset cuando es negativo o 0 (según implementación actual)", async () => {
|
|
69
|
-
let capturedFilter;
|
|
70
|
-
const findMock = jest.fn(async (filter) => {
|
|
71
|
-
capturedFilter = filter;
|
|
72
|
-
return [];
|
|
73
|
-
});
|
|
74
|
-
const dsMock = {
|
|
75
|
-
save: jest.fn().mockResolvedValue(undefined),
|
|
76
|
-
find: findMock,
|
|
77
|
-
name: "memory",
|
|
78
|
-
};
|
|
79
|
-
const useCase = new get_logs_use_case_1.GetLogsUseCase(dsMock);
|
|
80
|
-
const filter = {
|
|
81
|
-
limit: 0, // <= 0 → undefined
|
|
82
|
-
offset: 0, // por la condición actual, también termina como undefined
|
|
83
|
-
};
|
|
84
|
-
await useCase.execute(filter);
|
|
85
|
-
expect(findMock).toHaveBeenCalledTimes(1);
|
|
86
|
-
expect(capturedFilter).toBeDefined();
|
|
87
|
-
const safe = capturedFilter;
|
|
88
|
-
expect(safe.limit).toBeUndefined();
|
|
89
|
-
expect(safe.offset).toBeUndefined();
|
|
90
|
-
});
|
|
91
|
-
it("debe mantener limit y offset válidos cuando están en rango aceptable", async () => {
|
|
92
|
-
let capturedFilter;
|
|
93
|
-
const findMock = jest.fn(async (filter) => {
|
|
94
|
-
capturedFilter = filter;
|
|
95
|
-
return [];
|
|
96
|
-
});
|
|
97
|
-
const dsMock = {
|
|
98
|
-
save: jest.fn().mockResolvedValue(undefined),
|
|
99
|
-
find: findMock,
|
|
100
|
-
name: "memory",
|
|
101
|
-
};
|
|
102
|
-
const useCase = new get_logs_use_case_1.GetLogsUseCase(dsMock);
|
|
103
|
-
const filter = {
|
|
104
|
-
limit: 100,
|
|
105
|
-
offset: 10,
|
|
106
|
-
};
|
|
107
|
-
await useCase.execute(filter);
|
|
108
|
-
expect(findMock).toHaveBeenCalledTimes(1);
|
|
109
|
-
expect(capturedFilter).toBeDefined();
|
|
110
|
-
const safe = capturedFilter;
|
|
111
|
-
expect(safe.limit).toBe(100);
|
|
112
|
-
expect(safe.offset).toBe(10);
|
|
113
|
-
});
|
|
114
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const use_cases_1 = require("../../../src/application/use-cases");
|
|
4
|
-
const value_objects_1 = require("../../../src/domain/value-objects");
|
|
5
|
-
const services_1 = require("../../../src/domain/services");
|
|
6
|
-
const create_pii_redactor_mock_1 = require("../../test-utils/create-pii-redactor-mock");
|
|
7
|
-
// Mock explícito del servicio de normalización/PII del mensaje
|
|
8
|
-
jest.mock("../../../src/domain/services", () => ({
|
|
9
|
-
normalizeMessage: jest.fn(),
|
|
10
|
-
}));
|
|
11
|
-
describe("SaveLogUseCase", () => {
|
|
12
|
-
let dsMock;
|
|
13
|
-
let redactorMock;
|
|
14
|
-
let props;
|
|
15
|
-
let useCase;
|
|
16
|
-
let fixedNow;
|
|
17
|
-
beforeEach(() => {
|
|
18
|
-
jest.clearAllMocks();
|
|
19
|
-
fixedNow = 1700000000000; // número fijo para timestamp
|
|
20
|
-
jest.spyOn(Date, "now").mockReturnValue(fixedNow);
|
|
21
|
-
dsMock = {
|
|
22
|
-
save: jest.fn().mockResolvedValue(undefined),
|
|
23
|
-
find: jest.fn(),
|
|
24
|
-
flush: jest.fn(),
|
|
25
|
-
dispose: jest.fn(),
|
|
26
|
-
name: "test-datasource",
|
|
27
|
-
};
|
|
28
|
-
redactorMock = (0, create_pii_redactor_mock_1.createPiiRedactorMock)();
|
|
29
|
-
props = {
|
|
30
|
-
ds: dsMock,
|
|
31
|
-
minLevel: value_objects_1.LogLevel.INFO,
|
|
32
|
-
redactor: redactorMock,
|
|
33
|
-
};
|
|
34
|
-
useCase = new use_cases_1.SaveLogUseCase(props);
|
|
35
|
-
});
|
|
36
|
-
afterEach(() => {
|
|
37
|
-
// restaurar Date.now si hay otros tests en el proyecto
|
|
38
|
-
Date.now.mockRestore?.();
|
|
39
|
-
});
|
|
40
|
-
it("no debe guardar el log si el nivel es menor que el minLevel (filtro por nivel)", async () => {
|
|
41
|
-
const level = value_objects_1.LogLevel.DEBUG; // < INFO
|
|
42
|
-
const message = "Mensaje de debug que no debería guardarse";
|
|
43
|
-
await useCase.execute(level, message);
|
|
44
|
-
// No se normaliza ni se redacta nada
|
|
45
|
-
expect(services_1.normalizeMessage).not.toHaveBeenCalled();
|
|
46
|
-
expect(redactorMock.redact).not.toHaveBeenCalled();
|
|
47
|
-
// No se persiste el log
|
|
48
|
-
expect(dsMock.save).not.toHaveBeenCalled();
|
|
49
|
-
});
|
|
50
|
-
it("debe guardar el log si el nivel es igual al minLevel", async () => {
|
|
51
|
-
const level = value_objects_1.LogLevel.INFO; // == minLevel
|
|
52
|
-
const message = "Mensaje de info";
|
|
53
|
-
services_1.normalizeMessage.mockReturnValue("mensaje-normalizado");
|
|
54
|
-
await useCase.execute(level, message);
|
|
55
|
-
expect(services_1.normalizeMessage).toHaveBeenCalledTimes(1);
|
|
56
|
-
expect(services_1.normalizeMessage).toHaveBeenCalledWith(message, redactorMock);
|
|
57
|
-
expect(dsMock.save).toHaveBeenCalledTimes(1);
|
|
58
|
-
const savedLog = dsMock.save.mock.calls[0][0];
|
|
59
|
-
expect(savedLog.level).toBe(level);
|
|
60
|
-
expect(savedLog.message).toBe("mensaje-normalizado");
|
|
61
|
-
expect(savedLog.meta).toBeUndefined();
|
|
62
|
-
expect(savedLog.timestamp).toBe(fixedNow);
|
|
63
|
-
});
|
|
64
|
-
it("debe normalizar el mensaje, redactar meta y guardar el log cuando level >= minLevel (ejemplo de PII)", async () => {
|
|
65
|
-
const level = value_objects_1.LogLevel.ERROR;
|
|
66
|
-
const rawMessage = "Error al procesar tarjeta 4111-1111-1111-1111 del usuario john.doe@example.com";
|
|
67
|
-
const metaConPii = {
|
|
68
|
-
user: {
|
|
69
|
-
email: "john.doe@example.com",
|
|
70
|
-
card: "4111-1111-1111-1111",
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
// Simulamos que normalizeMessage aplica cierta redacción básica de PII
|
|
74
|
-
services_1.normalizeMessage.mockImplementation((msg, _redactor) => {
|
|
75
|
-
if (typeof msg === "string") {
|
|
76
|
-
return msg
|
|
77
|
-
.replace(/\d{4}-\d{4}-\d{4}-\d{4}/g, "****-****-****-****")
|
|
78
|
-
.replace(/[\w.-]+@[\w.-]+/g, "***@***");
|
|
79
|
-
}
|
|
80
|
-
return msg;
|
|
81
|
-
});
|
|
82
|
-
// Simulamos que el redactor también actúa sobre meta (ejemplo PII)
|
|
83
|
-
redactorMock.redact.mockImplementation((value) => {
|
|
84
|
-
if (value?.user) {
|
|
85
|
-
return {
|
|
86
|
-
...value,
|
|
87
|
-
user: {
|
|
88
|
-
...value.user,
|
|
89
|
-
email: "***@***",
|
|
90
|
-
card: "****-****-****-****",
|
|
91
|
-
},
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
return value;
|
|
95
|
-
});
|
|
96
|
-
await useCase.execute(level, rawMessage, metaConPii);
|
|
97
|
-
// 1) Se debe normalizar el mensaje con el redactor
|
|
98
|
-
expect(services_1.normalizeMessage).toHaveBeenCalledTimes(1);
|
|
99
|
-
expect(services_1.normalizeMessage).toHaveBeenCalledWith(rawMessage, redactorMock);
|
|
100
|
-
// 2) Se debe redactar la meta
|
|
101
|
-
expect(redactorMock.redact).toHaveBeenCalledTimes(1);
|
|
102
|
-
expect(redactorMock.redact).toHaveBeenCalledWith(metaConPii);
|
|
103
|
-
// 3) Se debe llamar a save con el log ya procesado
|
|
104
|
-
expect(dsMock.save).toHaveBeenCalledTimes(1);
|
|
105
|
-
const savedLog = dsMock.save.mock.calls[0][0];
|
|
106
|
-
expect(savedLog.level).toBe(level);
|
|
107
|
-
expect(savedLog.timestamp).toBe(fixedNow);
|
|
108
|
-
// Mensaje con PII de tarjeta + email redactado por normalizeMessage simulado
|
|
109
|
-
expect(savedLog.message).toBe("Error al procesar tarjeta ****-****-****-**** del usuario ***@***");
|
|
110
|
-
// Meta con PII redactado por redactorMock
|
|
111
|
-
expect(savedLog.meta).toEqual({
|
|
112
|
-
user: {
|
|
113
|
-
email: "***@***",
|
|
114
|
-
card: "****-****-****-****",
|
|
115
|
-
},
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
it("debe llamar a redact(meta) solo cuando meta está definido", async () => {
|
|
119
|
-
const level = value_objects_1.LogLevel.WARN;
|
|
120
|
-
const message = "Advertencia con y sin meta";
|
|
121
|
-
services_1.normalizeMessage.mockReturnValue("msg-warn");
|
|
122
|
-
// 1️⃣ Llamada SIN meta
|
|
123
|
-
await useCase.execute(level, message);
|
|
124
|
-
expect(redactorMock.redact).not.toHaveBeenCalled();
|
|
125
|
-
expect(dsMock.save).toHaveBeenCalledTimes(1);
|
|
126
|
-
const logSinMeta = dsMock.save.mock.calls[0][0];
|
|
127
|
-
expect(logSinMeta.meta).toBeUndefined();
|
|
128
|
-
// 2️⃣ Llamada CON meta
|
|
129
|
-
const meta = { foo: "bar" };
|
|
130
|
-
await useCase.execute(level, message, meta);
|
|
131
|
-
// Ahora sí debe haber sido llamada para la segunda ejecución
|
|
132
|
-
expect(redactorMock.redact).toHaveBeenCalledTimes(1);
|
|
133
|
-
expect(redactorMock.redact).toHaveBeenCalledWith(meta);
|
|
134
|
-
expect(dsMock.save).toHaveBeenCalledTimes(2);
|
|
135
|
-
const logConMeta = dsMock.save.mock.calls[1][0];
|
|
136
|
-
expect(logConMeta.meta).toBe(meta); // como redactorMock.redact devuelve el mismo valor
|
|
137
|
-
});
|
|
138
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|