@furvester/upstream-sync 1.1.0 → 2.0.0
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/LICENSE +1 -1
- package/dist/index.d.ts +13 -14
- package/dist/index.js +13 -31
- package/package.json +16 -17
package/LICENSE
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
/// <reference types="node" resolution-mode="require"/>
|
|
2
1
|
import type { EntityClass, EntityManager, MikroORM } from "@mikro-orm/postgresql";
|
|
3
|
-
import { type CollectionPageParams, type Selector } from "jsonapi-zod-query";
|
|
4
2
|
import type { Logger } from "logforth";
|
|
5
3
|
import { type ConnectionOptions } from "rabbitmq-client";
|
|
6
4
|
import type { z } from "zod";
|
|
5
|
+
import { PaginationPageParams } from "@jsonapi-serde/client";
|
|
7
6
|
export type SyncManagerConfig = {
|
|
8
7
|
rabbitmq: ConnectionOptions;
|
|
9
8
|
orm: MikroORM;
|
|
@@ -16,13 +15,13 @@ export type SyncableEntityClass = EntityClass<{
|
|
|
16
15
|
id: string;
|
|
17
16
|
upstreamVersion: number;
|
|
18
17
|
}>;
|
|
19
|
-
export type
|
|
18
|
+
export type ResyncResource = {
|
|
20
19
|
id: string;
|
|
21
20
|
version: number;
|
|
22
21
|
};
|
|
23
|
-
export type
|
|
22
|
+
export type ResyncDocument<T extends ResyncResource> = {
|
|
24
23
|
data: T[];
|
|
25
|
-
pageParams:
|
|
24
|
+
pageParams: PaginationPageParams<string>;
|
|
26
25
|
};
|
|
27
26
|
export type MessageEventHandler<T = unknown> = (em: EntityManager, data: T) => Promise<void> | void;
|
|
28
27
|
export type MessageEvent<T extends z.ZodTypeAny> = {
|
|
@@ -30,19 +29,19 @@ export type MessageEvent<T extends z.ZodTypeAny> = {
|
|
|
30
29
|
schema: T;
|
|
31
30
|
handler: MessageEventHandler<ReturnType<T["parse"]>>;
|
|
32
31
|
};
|
|
33
|
-
export type
|
|
34
|
-
export type
|
|
32
|
+
export type ResyncHandler<T> = (em: EntityManager, resource: T) => Promise<void> | void;
|
|
33
|
+
export type ResyncDeserializer<T extends ResyncResource> = (data: unknown) => ResyncDocument<T>;
|
|
34
|
+
export type Resync<TResource extends ResyncResource, TDeserializer extends ResyncDeserializer<TResource>> = {
|
|
35
35
|
basePath: string;
|
|
36
36
|
searchParams?: URLSearchParams;
|
|
37
|
-
|
|
37
|
+
deserializer: TDeserializer;
|
|
38
38
|
entityClass: SyncableEntityClass;
|
|
39
|
-
|
|
40
|
-
update: PreSyncHandler<ReturnType<TSelector>["data"][number]>;
|
|
39
|
+
upsert: ResyncHandler<TResource>;
|
|
41
40
|
};
|
|
42
41
|
export type UpstreamEntity = {
|
|
43
42
|
routingKeyPrefix: string;
|
|
44
43
|
events: MessageEvent<z.ZodTypeAny>[];
|
|
45
|
-
|
|
44
|
+
resync?: Resync<ResyncResource, ResyncDeserializer<ResyncResource>>;
|
|
46
45
|
};
|
|
47
46
|
export type UpstreamService = {
|
|
48
47
|
serviceName: string;
|
|
@@ -50,7 +49,7 @@ export type UpstreamService = {
|
|
|
50
49
|
disableWaitReady?: boolean;
|
|
51
50
|
};
|
|
52
51
|
export declare const createMessageEvent: <T extends z.ZodTypeAny>(event: MessageEvent<T>) => MessageEvent<T>;
|
|
53
|
-
export declare const
|
|
52
|
+
export declare const createResync: <TResource extends ResyncResource, TDeserializer extends ResyncDeserializer<TResource>>(preSync: Resync<TResource, TDeserializer>) => Resync<TResource, TDeserializer>;
|
|
54
53
|
export declare const createUpstreamEntity: (entity: UpstreamEntity) => UpstreamEntity;
|
|
55
54
|
export declare const createUpstreamService: (service: UpstreamService) => UpstreamService;
|
|
56
55
|
export declare class SyncManager {
|
|
@@ -62,7 +61,7 @@ export declare class SyncManager {
|
|
|
62
61
|
addUpstreamService(service: UpstreamService): this;
|
|
63
62
|
run(): Promise<void>;
|
|
64
63
|
private createConsumer;
|
|
65
|
-
private
|
|
66
|
-
private
|
|
64
|
+
private runResync;
|
|
65
|
+
private resyncEntity;
|
|
67
66
|
private waitReady;
|
|
68
67
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Mutex } from "async-mutex";
|
|
2
|
-
import { handleJsonApiError, injectPageParams, } from "jsonapi-zod-query";
|
|
3
2
|
import { Connection, } from "rabbitmq-client";
|
|
3
|
+
import { handleJsonApiError, injectPageParams } from "@jsonapi-serde/client";
|
|
4
4
|
export const createMessageEvent = (event) => event;
|
|
5
|
-
export const
|
|
5
|
+
export const createResync = (preSync) => preSync;
|
|
6
6
|
export const createUpstreamEntity = (entity) => entity;
|
|
7
7
|
export const createUpstreamService = (service) => service;
|
|
8
8
|
export class SyncManager {
|
|
@@ -34,7 +34,7 @@ export class SyncManager {
|
|
|
34
34
|
};
|
|
35
35
|
process.on("SIGINT", handleShutdown);
|
|
36
36
|
process.on("SIGTERM", handleShutdown);
|
|
37
|
-
await this.
|
|
37
|
+
await this.runResync();
|
|
38
38
|
this.initialSyncMutex.release();
|
|
39
39
|
}
|
|
40
40
|
createConsumer() {
|
|
@@ -83,30 +83,26 @@ export class SyncManager {
|
|
|
83
83
|
});
|
|
84
84
|
});
|
|
85
85
|
}
|
|
86
|
-
async
|
|
86
|
+
async runResync() {
|
|
87
87
|
for (const service of this.upstreamServices) {
|
|
88
88
|
if (!service.disableWaitReady) {
|
|
89
89
|
await this.waitReady(`/${service.serviceName}/health`, service.serviceName);
|
|
90
90
|
}
|
|
91
91
|
for (const entity of service.entities) {
|
|
92
92
|
await this.config.orm.em.fork().transactional(async (em) => {
|
|
93
|
-
await this.
|
|
93
|
+
await this.resyncEntity(em, service, entity);
|
|
94
94
|
});
|
|
95
|
-
this.config.logger.info(`Entity with routing key ${entity.routingKeyPrefix}
|
|
95
|
+
this.config.logger.info(`Entity with routing key ${entity.routingKeyPrefix} resynced`);
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
-
async
|
|
100
|
-
if (!entity.
|
|
99
|
+
async resyncEntity(em, service, entity) {
|
|
100
|
+
if (!entity.resync) {
|
|
101
101
|
return;
|
|
102
102
|
}
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
.
|
|
106
|
-
.execute("all", false)).map(({ id, upstream_version }) => [id, upstream_version]));
|
|
107
|
-
const baseUrl = new URL(`/${service.serviceName}${entity.preSync.basePath}`, this.config.apiGatewayUrl);
|
|
108
|
-
if (entity.preSync.searchParams) {
|
|
109
|
-
baseUrl.search = entity.preSync.searchParams.toString();
|
|
103
|
+
const baseUrl = new URL(`/${service.serviceName}${entity.resync.basePath}`, this.config.apiGatewayUrl);
|
|
104
|
+
if (entity.resync.searchParams) {
|
|
105
|
+
baseUrl.search = entity.resync.searchParams.toString();
|
|
110
106
|
}
|
|
111
107
|
let url = baseUrl;
|
|
112
108
|
do {
|
|
@@ -117,18 +113,9 @@ export class SyncManager {
|
|
|
117
113
|
},
|
|
118
114
|
});
|
|
119
115
|
await handleJsonApiError(response);
|
|
120
|
-
const document = entity.
|
|
116
|
+
const document = entity.resync.deserializer(await response.json());
|
|
121
117
|
for (const resource of document.data) {
|
|
122
|
-
|
|
123
|
-
if (version === undefined) {
|
|
124
|
-
await entity.preSync.create(em, resource);
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
versions.delete(resource.id);
|
|
128
|
-
if (version === resource.version) {
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
await entity.preSync.update(em, resource);
|
|
118
|
+
await entity.resync.upsert(em, resource);
|
|
132
119
|
}
|
|
133
120
|
if (document.pageParams.next) {
|
|
134
121
|
url = new URL(baseUrl);
|
|
@@ -137,11 +124,6 @@ export class SyncManager {
|
|
|
137
124
|
}
|
|
138
125
|
url = null;
|
|
139
126
|
} while (url !== null);
|
|
140
|
-
if (versions.size > 0) {
|
|
141
|
-
await em.nativeDelete(entity.preSync.entityClass, {
|
|
142
|
-
id: { $in: [...versions.keys()] },
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
127
|
}
|
|
146
128
|
async waitReady(path, serviceName) {
|
|
147
129
|
const healthUrl = new URL(path, this.config.apiGatewayUrl);
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@furvester/upstream-sync",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Sync from upstream via RabbitMQ",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Ben Scholzen 'DASPRiD'",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/
|
|
10
|
+
"url": "git+https://github.com/Furvester/upstream-sync.git"
|
|
11
11
|
},
|
|
12
12
|
"publishConfig": {
|
|
13
13
|
"access": "public"
|
|
@@ -32,28 +32,27 @@
|
|
|
32
32
|
"check": "biome check . --apply"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@biomejs/biome": "
|
|
36
|
-
"@commitlint/cli": "^
|
|
37
|
-
"@commitlint/config-conventional": "^
|
|
38
|
-
"@mikro-orm/postgresql": "^6.
|
|
39
|
-
"@tsconfig/
|
|
40
|
-
"@types/node": "^
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"zod": "^3.23.4"
|
|
35
|
+
"@biomejs/biome": "2.3.14",
|
|
36
|
+
"@commitlint/cli": "^20.4.1",
|
|
37
|
+
"@commitlint/config-conventional": "^20.4.1",
|
|
38
|
+
"@mikro-orm/postgresql": "^6.6.6",
|
|
39
|
+
"@tsconfig/node24": "^24.0.4",
|
|
40
|
+
"@types/node": "^25.2.2",
|
|
41
|
+
"lefthook": "^2.1.0",
|
|
42
|
+
"logforth": "^1.3.0",
|
|
43
|
+
"rabbitmq-client": "^5.0.8",
|
|
44
|
+
"typescript": "^5.9.3",
|
|
45
|
+
"zod": "^4.3.6"
|
|
47
46
|
},
|
|
48
47
|
"peerDependencies": {
|
|
49
48
|
"@mikro-orm/postgresql": "^6.3.13",
|
|
50
|
-
"jsonapi-zod-query": "^2.1.1",
|
|
51
49
|
"logforth": "^1.2.2",
|
|
52
50
|
"rabbitmq-client": "^5.0.0",
|
|
53
51
|
"zod": "^3.22.4"
|
|
54
52
|
},
|
|
55
|
-
"packageManager": "pnpm@9.9.0+sha512.60c18acd138bff695d339be6ad13f7e936eea6745660d4cc4a776d5247c540d0edee1a563695c183a66eb917ef88f2b4feb1fc25f32a7adcadc7aaf3438e99c1",
|
|
56
53
|
"dependencies": {
|
|
54
|
+
"@jsonapi-serde/client": "^1.2.1",
|
|
57
55
|
"async-mutex": "^0.5.0"
|
|
58
|
-
}
|
|
56
|
+
},
|
|
57
|
+
"packageManager": "pnpm@10.29.1+sha512.48dae233635a645768a3028d19545cacc1688639eeb1f3734e42d6d6b971afbf22aa1ac9af52a173d9c3a20c15857cfa400f19994d79a2f626fcc73fccda9bbc"
|
|
59
58
|
}
|