feat: version 4.12.0 -- Conexiones externas: pacientes de SAGA, empleados de nomina y medicos
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -73,6 +73,7 @@ function load() {
|
||||
cacheMaxRecordsPerTable: Number(limits.cacheMaxRecordsPerTable || 100000),
|
||||
cacheMaxTables: Number(limits.cacheMaxTables || 10),
|
||||
requestTimeoutMs: Number(limits.requestTimeoutMs || 30000),
|
||||
minReloadSeconds: Number(limits.minReloadSeconds ?? 60),
|
||||
},
|
||||
logDir: path.resolve(ROOT, raw.logDir || 'logs'),
|
||||
};
|
||||
|
||||
@@ -98,7 +98,8 @@ async function withSnapshot(ref, fn) {
|
||||
function toPlain(value, type) {
|
||||
if (value === null || value === undefined) return null;
|
||||
if (value instanceof Date) {
|
||||
if (Number.isNaN(value.getTime())) return null;
|
||||
// Fecha vacía en FoxPro: la librería la convierte en el día juliano 0 (año -4713).
|
||||
if (Number.isNaN(value.getTime()) || value.getUTCFullYear() < 1000) return null;
|
||||
return type === 'D' ? value.toISOString().slice(0, 10) : value.toISOString();
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
@@ -122,7 +123,10 @@ async function loadTable(source, ref, limits) {
|
||||
const key = `${source.id}/${ref.table}`;
|
||||
const sig = await signature(ref);
|
||||
const hit = cache.get(key);
|
||||
if (hit && hit.version === sig.version) {
|
||||
// Tablas que el sistema origen escribe todo el día: no se vuelve a copiar más de una vez cada
|
||||
// minReloadSeconds aunque la firma haya cambiado.
|
||||
const fresh = hit && Date.now() - hit.loadedAt < limits.minReloadSeconds * 1000;
|
||||
if (hit && (hit.version === sig.version || fresh)) {
|
||||
cache.delete(key);
|
||||
cache.set(key, hit); // LRU
|
||||
return hit;
|
||||
@@ -138,7 +142,7 @@ async function loadTable(source, ref, limits) {
|
||||
for (const f of fields) row[f.name] = toPlain(r[f.name], f.type);
|
||||
records.push(row);
|
||||
}
|
||||
return { version: sig.version, modifiedAt: sig.modifiedAt, fields, records };
|
||||
return { version: sig.version, modifiedAt: sig.modifiedAt, loadedAt: Date.now(), fields, records };
|
||||
});
|
||||
if (entry.records.length <= limits.cacheMaxRecordsPerTable) cachePut(key, entry, limits);
|
||||
return entry;
|
||||
@@ -206,9 +210,17 @@ async function readRows(source, tableName, q, limits) {
|
||||
.filter(([k]) => k.startsWith('eq.'))
|
||||
.map(([k, v]) => [pick(k.slice(3), 'eq')[0], fold(v).trim()]);
|
||||
const text = q.q ? fold(q.q).trim() : '';
|
||||
// Sincronización incremental: solo lo agregado (afterRecno) y/o lo modificado desde una fecha en
|
||||
// un campo de fecha/hora del origen (changedField + changedSince). Si se dan ambos, cualquiera basta.
|
||||
const afterRecno = q.afterRecno !== undefined ? Math.max(parseInt(q.afterRecno, 10) || 0, 0) : null;
|
||||
const changedField = q.changedField ? pick(q.changedField, 'changedField')[0] : null;
|
||||
const changedSince = q.changedSince ? String(q.changedSince) : null;
|
||||
if (!!changedField !== !!changedSince) throw new HttpError(400, 'changedField y changedSince van juntos');
|
||||
const incremental = afterRecno !== null || changedField;
|
||||
const qFields = text ? (q.qFields ? pick(q.qFields, 'qFields') : fields) : [];
|
||||
|
||||
const matches = data.records.filter((r) => {
|
||||
if (incremental && !((afterRecno !== null && r._recno > afterRecno) || (changedField && r[changedField] !== null && String(r[changedField]) > changedSince))) return false;
|
||||
if (!includeDeleted && r._deleted) return false;
|
||||
for (const [f, v] of eq) if (r[f] === null || fold(r[f]).trim() !== v) return false;
|
||||
if (text && !qFields.some((f) => r[f] !== null && fold(r[f]).includes(text))) return false;
|
||||
@@ -221,7 +233,7 @@ async function readRows(source, tableName, q, limits) {
|
||||
for (const f of fields) o[f] = r[f];
|
||||
return o;
|
||||
});
|
||||
return { table: ref.table, version: data.version, modifiedAt: data.modifiedAt, total: matches.length, offset, limit, fields, rows };
|
||||
return { table: ref.table, version: data.version, modifiedAt: data.modifiedAt, maxRecno: data.records.length, total: matches.length, offset, limit, fields, rows };
|
||||
}
|
||||
|
||||
module.exports = { listTables, getSchema, readRows, HttpError };
|
||||
|
||||
Reference in New Issue
Block a user