Skip to content

Commit 128b91c

Browse files
committed
feat: add reconcilers to db package and refactor reconcile routes to use enqueue functions
1 parent c444d7c commit 128b91c

3 files changed

Lines changed: 295 additions & 139 deletions

File tree

packages/db/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
"./queries": {
2020
"types": "./src/queries/index.ts",
2121
"default": "./dist/src/queries/index.js"
22+
},
23+
"./reconcilers": {
24+
"types": "./src/reconcilers/index.ts",
25+
"default": "./dist/src/reconcilers/index.js"
2226
}
2327
},
2428
"license": "MIT",
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
import type { Tx } from "../common.js";
2+
import type { ReconcileWorkScope } from "../schema/reconcile.js";
3+
import {
4+
reconcileWorkPayload,
5+
reconcileWorkScope,
6+
} from "../schema/reconcile.js";
7+
8+
const scopeConflictTarget = [
9+
reconcileWorkScope.workspaceId,
10+
reconcileWorkScope.kind,
11+
reconcileWorkScope.scopeType,
12+
reconcileWorkScope.scopeId,
13+
];
14+
15+
const payloadConflictTarget = [
16+
reconcileWorkPayload.scopeRef,
17+
reconcileWorkPayload.payloadType,
18+
reconcileWorkPayload.payloadKey,
19+
];
20+
21+
// ---------------------------------------------------------------------------
22+
// Core enqueue
23+
// ---------------------------------------------------------------------------
24+
25+
interface EnqueuePayload {
26+
payloadType?: string;
27+
payloadKey?: string;
28+
payload?: Record<string, any>;
29+
}
30+
31+
interface EnqueueParams {
32+
workspaceId: string;
33+
kind: string;
34+
scopeType?: string;
35+
scopeId?: string;
36+
priority?: number;
37+
notBefore?: Date;
38+
payload?: EnqueuePayload;
39+
}
40+
41+
export async function enqueue(
42+
db: Tx,
43+
params: EnqueueParams,
44+
): Promise<ReconcileWorkScope> {
45+
const now = new Date();
46+
const [scope] = await db
47+
.insert(reconcileWorkScope)
48+
.values({
49+
workspaceId: params.workspaceId,
50+
kind: params.kind,
51+
scopeType: params.scopeType ?? "",
52+
scopeId: params.scopeId ?? "",
53+
priority: params.priority ?? 100,
54+
notBefore: params.notBefore ?? now,
55+
})
56+
.onConflictDoUpdate({
57+
target: scopeConflictTarget,
58+
set: {
59+
eventTs: now,
60+
priority: params.priority ?? 100,
61+
notBefore: params.notBefore ?? now,
62+
},
63+
})
64+
.returning();
65+
66+
if (params.payload && scope) {
67+
await db
68+
.insert(reconcileWorkPayload)
69+
.values({
70+
scopeRef: scope.id,
71+
payloadType: params.payload.payloadType ?? "",
72+
payloadKey: params.payload.payloadKey ?? "",
73+
payload: params.payload.payload ?? {},
74+
})
75+
.onConflictDoUpdate({
76+
target: payloadConflictTarget,
77+
set: { payload: params.payload.payload ?? {} },
78+
});
79+
}
80+
81+
return scope!;
82+
}
83+
84+
interface EnqueueScopeParams {
85+
workspaceId: string;
86+
kind: string;
87+
scopeType?: string;
88+
scopeId?: string;
89+
priority?: number;
90+
notBefore?: Date;
91+
}
92+
93+
export async function enqueueMany(
94+
db: Tx,
95+
items: EnqueueScopeParams[],
96+
): Promise<void> {
97+
if (items.length === 0) return;
98+
99+
const now = new Date();
100+
const values = items.map((item) => ({
101+
workspaceId: item.workspaceId,
102+
kind: item.kind,
103+
scopeType: item.scopeType ?? "",
104+
scopeId: item.scopeId ?? "",
105+
priority: item.priority ?? 100,
106+
notBefore: item.notBefore ?? now,
107+
}));
108+
109+
await db
110+
.insert(reconcileWorkScope)
111+
.values(values)
112+
.onConflictDoUpdate({
113+
target: scopeConflictTarget,
114+
set: { eventTs: now, notBefore: now },
115+
});
116+
}
117+
118+
// ---------------------------------------------------------------------------
119+
// Deployment resource-selector eval
120+
// ---------------------------------------------------------------------------
121+
122+
const DEPLOYMENT_SELECTOR_EVAL_KIND = "deployment-resource-selector-eval";
123+
124+
export async function enqueueDeploymentSelectorEval(
125+
db: Tx,
126+
params: { workspaceId: string; deploymentId: string },
127+
): Promise<ReconcileWorkScope> {
128+
return enqueue(db, {
129+
workspaceId: params.workspaceId,
130+
kind: DEPLOYMENT_SELECTOR_EVAL_KIND,
131+
scopeType: "deployment",
132+
scopeId: params.deploymentId,
133+
});
134+
}
135+
136+
export async function enqueueManyDeploymentSelectorEval(
137+
db: Tx,
138+
items: Array<{ workspaceId: string; deploymentId: string }>,
139+
): Promise<void> {
140+
return enqueueMany(
141+
db,
142+
items.map((item) => ({
143+
workspaceId: item.workspaceId,
144+
kind: DEPLOYMENT_SELECTOR_EVAL_KIND,
145+
scopeType: "deployment",
146+
scopeId: item.deploymentId,
147+
})),
148+
);
149+
}
150+
151+
// ---------------------------------------------------------------------------
152+
// Environment resource-selector eval
153+
// ---------------------------------------------------------------------------
154+
155+
const ENVIRONMENT_SELECTOR_EVAL_KIND = "environment-resource-selector-eval";
156+
157+
export async function enqueueEnvironmentSelectorEval(
158+
db: Tx,
159+
params: { workspaceId: string; environmentId: string },
160+
): Promise<ReconcileWorkScope> {
161+
return enqueue(db, {
162+
workspaceId: params.workspaceId,
163+
kind: ENVIRONMENT_SELECTOR_EVAL_KIND,
164+
scopeType: "environment",
165+
scopeId: params.environmentId,
166+
});
167+
}
168+
169+
export async function enqueueManyEnvironmentSelectorEval(
170+
db: Tx,
171+
items: Array<{ workspaceId: string; environmentId: string }>,
172+
): Promise<void> {
173+
return enqueueMany(
174+
db,
175+
items.map((item) => ({
176+
workspaceId: item.workspaceId,
177+
kind: ENVIRONMENT_SELECTOR_EVAL_KIND,
178+
scopeType: "environment",
179+
scopeId: item.environmentId,
180+
})),
181+
);
182+
}
183+
184+
// ---------------------------------------------------------------------------
185+
// Relationship eval
186+
// ---------------------------------------------------------------------------
187+
188+
const RELATIONSHIP_EVAL_KIND = "relationship-eval";
189+
190+
type EntityType = "resource" | "deployment" | "environment";
191+
192+
export async function enqueueRelationshipEval(
193+
db: Tx,
194+
params: { workspaceId: string; entityType: EntityType; entityId: string },
195+
): Promise<ReconcileWorkScope> {
196+
return enqueue(db, {
197+
workspaceId: params.workspaceId,
198+
kind: RELATIONSHIP_EVAL_KIND,
199+
scopeType: "entity",
200+
scopeId: `${params.entityType}:${params.entityId}`,
201+
});
202+
}
203+
204+
export async function enqueueManyRelationshipEval(
205+
db: Tx,
206+
items: Array<{
207+
workspaceId: string;
208+
entityType: EntityType;
209+
entityId: string;
210+
}>,
211+
): Promise<void> {
212+
return enqueueMany(
213+
db,
214+
items.map((item) => ({
215+
workspaceId: item.workspaceId,
216+
kind: RELATIONSHIP_EVAL_KIND,
217+
scopeType: "entity",
218+
scopeId: `${item.entityType}:${item.entityId}`,
219+
})),
220+
);
221+
}
222+
223+
// ---------------------------------------------------------------------------
224+
// Desired release
225+
// ---------------------------------------------------------------------------
226+
227+
const DESIRED_RELEASE_KIND = "desired-release";
228+
229+
export async function enqueueDesiredRelease(
230+
db: Tx,
231+
params: {
232+
workspaceId: string;
233+
deploymentId: string;
234+
environmentId: string;
235+
resourceId: string;
236+
},
237+
): Promise<ReconcileWorkScope> {
238+
return enqueue(db, {
239+
workspaceId: params.workspaceId,
240+
kind: DESIRED_RELEASE_KIND,
241+
scopeType: "release-target",
242+
scopeId: `${params.deploymentId}:${params.environmentId}:${params.resourceId}`,
243+
});
244+
}
245+
246+
export async function enqueueManyDesiredRelease(
247+
db: Tx,
248+
items: Array<{
249+
workspaceId: string;
250+
deploymentId: string;
251+
environmentId: string;
252+
resourceId: string;
253+
}>,
254+
): Promise<void> {
255+
return enqueueMany(
256+
db,
257+
items.map((item) => ({
258+
workspaceId: item.workspaceId,
259+
kind: DESIRED_RELEASE_KIND,
260+
scopeType: "release-target",
261+
scopeId: `${item.deploymentId}:${item.environmentId}:${item.resourceId}`,
262+
})),
263+
);
264+
}

0 commit comments

Comments
 (0)