mirror of
https://github.com/morten-olsen/reservoir.git
synced 2026-02-08 01:46:24 +01:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
daa816ac61 | ||
|
|
75d24c31c2 | ||
|
|
2f542c3066 |
@@ -34,6 +34,7 @@
|
|||||||
"fastify": "^5.6.1",
|
"fastify": "^5.6.1",
|
||||||
"fastify-type-provider-zod": "^6.1.0",
|
"fastify-type-provider-zod": "^6.1.0",
|
||||||
"knex": "^3.1.0",
|
"knex": "^3.1.0",
|
||||||
|
"p-queue": "^9.0.0",
|
||||||
"pg": "^8.16.3",
|
"pg": "^8.16.3",
|
||||||
"pino": "^10.1.0",
|
"pino": "^10.1.0",
|
||||||
"pino-pretty": "^13.1.2",
|
"pino-pretty": "^13.1.2",
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { z } from 'zod';
|
|
||||||
import type { FastifyPluginAsyncZod } from 'fastify-type-provider-zod';
|
import type { FastifyPluginAsyncZod } from 'fastify-type-provider-zod';
|
||||||
|
|
||||||
import { DocumentsService } from '#root/services/documents/documents.ts';
|
import { DocumentsService } from '#root/services/documents/documents.ts';
|
||||||
@@ -12,23 +11,18 @@ const documentsPlugin: FastifyPluginAsyncZod = async (app) => {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: '',
|
url: '',
|
||||||
schema: {
|
schema: {
|
||||||
operationId: 'v1.documents.put',
|
operationId: 'v1.documents.post',
|
||||||
tags: ['documents'],
|
tags: ['documents'],
|
||||||
summary: 'Upsert documents',
|
summary: 'Upsert documents',
|
||||||
body: z.object({
|
body: upsertDocumentRequestSchema,
|
||||||
items: z.array(upsertDocumentRequestSchema),
|
|
||||||
}),
|
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: upsertDocumentResponseSchema,
|
||||||
items: z.array(upsertDocumentResponseSchema),
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
handler: async (req, reply) => {
|
handler: async (req, reply) => {
|
||||||
const documentsService = app.services.get(DocumentsService);
|
const documentsService = app.services.get(DocumentsService);
|
||||||
const { items } = req.body;
|
const result = await documentsService.upsert(req.body);
|
||||||
const results = await Promise.all(items.map((item) => documentsService.upsert(item)));
|
return reply.send(result);
|
||||||
return reply.send({ items: results });
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,10 +27,10 @@ type DocumentRow = {
|
|||||||
id: string;
|
id: string;
|
||||||
type: string;
|
type: string;
|
||||||
source: string | null;
|
source: string | null;
|
||||||
data: string;
|
data: string | unknown;
|
||||||
createdAt: string;
|
createdAt: string | Date;
|
||||||
updatedAt: string;
|
updatedAt: string | Date;
|
||||||
deletedAt: string | null;
|
deletedAt: string | Date | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Tables = {
|
type Tables = {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import equal from 'fast-deep-equal';
|
import equal from 'fast-deep-equal';
|
||||||
|
import Queue from 'p-queue';
|
||||||
|
|
||||||
import type { UpsertDocumentRequest, UpsertDocumentResponse } from './documents.schemas.ts';
|
import type { UpsertDocumentRequest, UpsertDocumentResponse } from './documents.schemas.ts';
|
||||||
|
|
||||||
@@ -7,12 +8,17 @@ import type { Services } from '#root/utils/utils.services.ts';
|
|||||||
|
|
||||||
class DocumentsService {
|
class DocumentsService {
|
||||||
#services: Services;
|
#services: Services;
|
||||||
|
#queue: Queue;
|
||||||
|
|
||||||
constructor(services: Services) {
|
constructor(services: Services) {
|
||||||
this.#services = services;
|
this.#services = services;
|
||||||
|
this.#queue = new Queue({
|
||||||
|
concurrency: 10,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public upsert = async (document: UpsertDocumentRequest): Promise<UpsertDocumentResponse> => {
|
public upsert = (document: UpsertDocumentRequest): Promise<UpsertDocumentResponse> =>
|
||||||
|
this.#queue.add(async () => {
|
||||||
const dbService = this.#services.get(DatabaseService);
|
const dbService = this.#services.get(DatabaseService);
|
||||||
const db = await dbService.getInstance();
|
const db = await dbService.getInstance();
|
||||||
|
|
||||||
@@ -43,15 +49,15 @@ class DocumentsService {
|
|||||||
action: 'inserted',
|
action: 'inserted',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const currentData = JSON.parse(current.data);
|
const currentData = typeof current.data === 'string' ? JSON.parse(current.data) : current.data;
|
||||||
if (equal(currentData, document.data)) {
|
if (equal(currentData, document.data)) {
|
||||||
return {
|
return {
|
||||||
...current,
|
...current,
|
||||||
data: currentData,
|
data: currentData,
|
||||||
id,
|
id,
|
||||||
createdAt: current.createdAt,
|
createdAt: new Date(current.createdAt).toISOString(),
|
||||||
updatedAt: current.updatedAt,
|
updatedAt: new Date(current.updatedAt).toISOString(),
|
||||||
deletedAt: current.deletedAt || null,
|
deletedAt: current.deletedAt ? new Date(current.deletedAt).toISOString() : null,
|
||||||
action: 'skipped',
|
action: 'skipped',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -66,12 +72,12 @@ class DocumentsService {
|
|||||||
...current,
|
...current,
|
||||||
id,
|
id,
|
||||||
data: document.data,
|
data: document.data,
|
||||||
createdAt: current.createdAt,
|
createdAt: new Date(current.createdAt).toISOString(),
|
||||||
updatedAt: now.toISOString(),
|
updatedAt: now.toISOString(),
|
||||||
deletedAt: current.deletedAt || null,
|
deletedAt: current.deletedAt ? new Date(current.deletedAt).toISOString() : null,
|
||||||
action: 'updated',
|
action: 'updated',
|
||||||
};
|
};
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export { DocumentsService };
|
export { DocumentsService };
|
||||||
|
|||||||
23
pnpm-lock.yaml
generated
23
pnpm-lock.yaml
generated
@@ -73,6 +73,9 @@ importers:
|
|||||||
knex:
|
knex:
|
||||||
specifier: ^3.1.0
|
specifier: ^3.1.0
|
||||||
version: 3.1.0(better-sqlite3@12.4.1)(pg@8.16.3)
|
version: 3.1.0(better-sqlite3@12.4.1)(pg@8.16.3)
|
||||||
|
p-queue:
|
||||||
|
specifier: ^9.0.0
|
||||||
|
version: 9.0.0
|
||||||
pg:
|
pg:
|
||||||
specifier: ^8.16.3
|
specifier: ^8.16.3
|
||||||
version: 8.16.3
|
version: 8.16.3
|
||||||
@@ -1310,6 +1313,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
eventemitter3@5.0.1:
|
||||||
|
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
|
||||||
|
|
||||||
execa@5.1.1:
|
execa@5.1.1:
|
||||||
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
|
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -2005,6 +2011,14 @@ packages:
|
|||||||
resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==}
|
resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
p-queue@9.0.0:
|
||||||
|
resolution: {integrity: sha512-KO1RyxstL9g1mK76530TExamZC/S2Glm080Nx8PE5sTd7nlduDQsAfEl4uXX+qZjLiwvDauvzXavufy3+rJ9zQ==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
|
p-timeout@7.0.1:
|
||||||
|
resolution: {integrity: sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
|
||||||
parent-module@1.0.1:
|
parent-module@1.0.1:
|
||||||
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -4113,6 +4127,8 @@ snapshots:
|
|||||||
|
|
||||||
esutils@2.0.3: {}
|
esutils@2.0.3: {}
|
||||||
|
|
||||||
|
eventemitter3@5.0.1: {}
|
||||||
|
|
||||||
execa@5.1.1:
|
execa@5.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
cross-spawn: 7.0.6
|
cross-spawn: 7.0.6
|
||||||
@@ -4784,6 +4800,13 @@ snapshots:
|
|||||||
|
|
||||||
p-map@2.1.0: {}
|
p-map@2.1.0: {}
|
||||||
|
|
||||||
|
p-queue@9.0.0:
|
||||||
|
dependencies:
|
||||||
|
eventemitter3: 5.0.1
|
||||||
|
p-timeout: 7.0.1
|
||||||
|
|
||||||
|
p-timeout@7.0.1: {}
|
||||||
|
|
||||||
parent-module@1.0.1:
|
parent-module@1.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
callsites: 3.1.0
|
callsites: 3.1.0
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ packages:
|
|||||||
- ./apps/*
|
- ./apps/*
|
||||||
onlyBuiltDependencies:
|
onlyBuiltDependencies:
|
||||||
- better-sqlite3
|
- better-sqlite3
|
||||||
|
- esbuild
|
||||||
|
|||||||
Reference in New Issue
Block a user