Skip to content

Commit 82f29d9

Browse files
committed
feat: add DAL
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
1 parent 7e6dc18 commit 82f29d9

7 files changed

Lines changed: 810 additions & 0 deletions

File tree

Lines changed: 397 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,397 @@
1+
import { QueryExecutor } from '../queryExecutor'
2+
import { prepareSelectColumns } from '../utils'
3+
4+
import {
5+
EvaluationStatus,
6+
IDbEvaluatedProject,
7+
IDbEvaluatedProjectCreate,
8+
IDbEvaluatedProjectUpdate,
9+
} from './types'
10+
11+
const EVALUATED_PROJECT_COLUMNS = [
12+
'id',
13+
'projectCatalogId',
14+
'evaluationStatus',
15+
'evaluationScore',
16+
'evaluation',
17+
'evaluationReason',
18+
'evaluatedAt',
19+
'starsCount',
20+
'forksCount',
21+
'commitsCount',
22+
'pullRequestsCount',
23+
'issuesCount',
24+
'onboarded',
25+
'onboardedAt',
26+
'createdAt',
27+
'updatedAt',
28+
]
29+
30+
export async function findEvaluatedProjectById(
31+
qx: QueryExecutor,
32+
id: string,
33+
): Promise<IDbEvaluatedProject | null> {
34+
return qx.selectOneOrNone(
35+
`
36+
SELECT ${prepareSelectColumns(EVALUATED_PROJECT_COLUMNS)}
37+
FROM "evaluatedProjects"
38+
WHERE id = $(id)
39+
`,
40+
{ id },
41+
)
42+
}
43+
44+
export async function findEvaluatedProjectByProjectCatalogId(
45+
qx: QueryExecutor,
46+
projectCatalogId: string,
47+
): Promise<IDbEvaluatedProject | null> {
48+
return qx.selectOneOrNone(
49+
`
50+
SELECT ${prepareSelectColumns(EVALUATED_PROJECT_COLUMNS)}
51+
FROM "evaluatedProjects"
52+
WHERE "projectCatalogId" = $(projectCatalogId)
53+
`,
54+
{ projectCatalogId },
55+
)
56+
}
57+
58+
export async function findEvaluatedProjectsByStatus(
59+
qx: QueryExecutor,
60+
evaluationStatus: EvaluationStatus,
61+
options: { limit?: number; offset?: number } = {},
62+
): Promise<IDbEvaluatedProject[]> {
63+
const { limit, offset } = options
64+
65+
return qx.select(
66+
`
67+
SELECT ${prepareSelectColumns(EVALUATED_PROJECT_COLUMNS)}
68+
FROM "evaluatedProjects"
69+
WHERE "evaluationStatus" = $(evaluationStatus)
70+
ORDER BY "createdAt" ASC
71+
${limit !== undefined ? 'LIMIT $(limit)' : ''}
72+
${offset !== undefined ? 'OFFSET $(offset)' : ''}
73+
`,
74+
{ evaluationStatus, limit, offset },
75+
)
76+
}
77+
78+
export async function findAllEvaluatedProjects(
79+
qx: QueryExecutor,
80+
options: { limit?: number; offset?: number } = {},
81+
): Promise<IDbEvaluatedProject[]> {
82+
const { limit, offset } = options
83+
84+
return qx.select(
85+
`
86+
SELECT ${prepareSelectColumns(EVALUATED_PROJECT_COLUMNS)}
87+
FROM "evaluatedProjects"
88+
ORDER BY "createdAt" DESC
89+
${limit !== undefined ? 'LIMIT $(limit)' : ''}
90+
${offset !== undefined ? 'OFFSET $(offset)' : ''}
91+
`,
92+
{ limit, offset },
93+
)
94+
}
95+
96+
export async function countEvaluatedProjects(
97+
qx: QueryExecutor,
98+
evaluationStatus?: EvaluationStatus,
99+
): Promise<number> {
100+
const statusFilter = evaluationStatus ? 'WHERE "evaluationStatus" = $(evaluationStatus)' : ''
101+
102+
const result = await qx.selectOne(
103+
`
104+
SELECT COUNT(*) AS count
105+
FROM "evaluatedProjects"
106+
${statusFilter}
107+
`,
108+
{ evaluationStatus },
109+
)
110+
return parseInt(result.count, 10)
111+
}
112+
113+
export async function insertEvaluatedProject(
114+
qx: QueryExecutor,
115+
data: IDbEvaluatedProjectCreate,
116+
): Promise<IDbEvaluatedProject> {
117+
return qx.selectOne(
118+
`
119+
INSERT INTO "evaluatedProjects" (
120+
"projectCatalogId",
121+
"evaluationStatus",
122+
"evaluationScore",
123+
evaluation,
124+
"evaluationReason",
125+
"starsCount",
126+
"forksCount",
127+
"commitsCount",
128+
"pullRequestsCount",
129+
"issuesCount",
130+
"createdAt",
131+
"updatedAt"
132+
)
133+
VALUES (
134+
$(projectCatalogId),
135+
$(evaluationStatus),
136+
$(evaluationScore),
137+
$(evaluation),
138+
$(evaluationReason),
139+
$(starsCount),
140+
$(forksCount),
141+
$(commitsCount),
142+
$(pullRequestsCount),
143+
$(issuesCount),
144+
NOW(),
145+
NOW()
146+
)
147+
RETURNING ${prepareSelectColumns(EVALUATED_PROJECT_COLUMNS)}
148+
`,
149+
{
150+
projectCatalogId: data.projectCatalogId,
151+
evaluationStatus: data.evaluationStatus ?? 'pending',
152+
evaluationScore: data.evaluationScore ?? null,
153+
evaluation: data.evaluation ? JSON.stringify(data.evaluation) : null,
154+
evaluationReason: data.evaluationReason ?? null,
155+
starsCount: data.starsCount ?? null,
156+
forksCount: data.forksCount ?? null,
157+
commitsCount: data.commitsCount ?? null,
158+
pullRequestsCount: data.pullRequestsCount ?? null,
159+
issuesCount: data.issuesCount ?? null,
160+
},
161+
)
162+
}
163+
164+
export async function bulkInsertEvaluatedProjects(
165+
qx: QueryExecutor,
166+
items: IDbEvaluatedProjectCreate[],
167+
): Promise<void> {
168+
if (items.length === 0) {
169+
return
170+
}
171+
172+
const values = items.map((item) => ({
173+
projectCatalogId: item.projectCatalogId,
174+
evaluationStatus: item.evaluationStatus ?? 'pending',
175+
evaluationScore: item.evaluationScore ?? null,
176+
evaluation: item.evaluation ? JSON.stringify(item.evaluation) : null,
177+
evaluationReason: item.evaluationReason ?? null,
178+
starsCount: item.starsCount ?? null,
179+
forksCount: item.forksCount ?? null,
180+
commitsCount: item.commitsCount ?? null,
181+
pullRequestsCount: item.pullRequestsCount ?? null,
182+
issuesCount: item.issuesCount ?? null,
183+
}))
184+
185+
await qx.result(
186+
`
187+
INSERT INTO "evaluatedProjects" (
188+
"projectCatalogId",
189+
"evaluationStatus",
190+
"evaluationScore",
191+
evaluation,
192+
"evaluationReason",
193+
"starsCount",
194+
"forksCount",
195+
"commitsCount",
196+
"pullRequestsCount",
197+
"issuesCount",
198+
"createdAt",
199+
"updatedAt"
200+
)
201+
SELECT
202+
v."projectCatalogId"::uuid,
203+
v."evaluationStatus",
204+
v."evaluationScore"::double precision,
205+
v.evaluation::jsonb,
206+
v."evaluationReason",
207+
v."starsCount"::integer,
208+
v."forksCount"::integer,
209+
v."commitsCount"::integer,
210+
v."pullRequestsCount"::integer,
211+
v."issuesCount"::integer,
212+
NOW(),
213+
NOW()
214+
FROM jsonb_to_recordset($(values)::jsonb) AS v(
215+
"projectCatalogId" text,
216+
"evaluationStatus" text,
217+
"evaluationScore" double precision,
218+
evaluation jsonb,
219+
"evaluationReason" text,
220+
"starsCount" integer,
221+
"forksCount" integer,
222+
"commitsCount" integer,
223+
"pullRequestsCount" integer,
224+
"issuesCount" integer
225+
)
226+
`,
227+
{ values: JSON.stringify(values) },
228+
)
229+
}
230+
231+
export async function updateEvaluatedProject(
232+
qx: QueryExecutor,
233+
id: string,
234+
data: IDbEvaluatedProjectUpdate,
235+
): Promise<IDbEvaluatedProject | null> {
236+
const setClauses: string[] = []
237+
const params: Record<string, unknown> = { id }
238+
239+
if (data.evaluationStatus !== undefined) {
240+
setClauses.push('"evaluationStatus" = $(evaluationStatus)')
241+
params.evaluationStatus = data.evaluationStatus
242+
}
243+
if (data.evaluationScore !== undefined) {
244+
setClauses.push('"evaluationScore" = $(evaluationScore)')
245+
params.evaluationScore = data.evaluationScore
246+
}
247+
if (data.evaluation !== undefined) {
248+
setClauses.push('evaluation = $(evaluation)')
249+
params.evaluation = data.evaluation ? JSON.stringify(data.evaluation) : null
250+
}
251+
if (data.evaluationReason !== undefined) {
252+
setClauses.push('"evaluationReason" = $(evaluationReason)')
253+
params.evaluationReason = data.evaluationReason
254+
}
255+
if (data.evaluatedAt !== undefined) {
256+
setClauses.push('"evaluatedAt" = $(evaluatedAt)')
257+
params.evaluatedAt = data.evaluatedAt
258+
}
259+
if (data.starsCount !== undefined) {
260+
setClauses.push('"starsCount" = $(starsCount)')
261+
params.starsCount = data.starsCount
262+
}
263+
if (data.forksCount !== undefined) {
264+
setClauses.push('"forksCount" = $(forksCount)')
265+
params.forksCount = data.forksCount
266+
}
267+
if (data.commitsCount !== undefined) {
268+
setClauses.push('"commitsCount" = $(commitsCount)')
269+
params.commitsCount = data.commitsCount
270+
}
271+
if (data.pullRequestsCount !== undefined) {
272+
setClauses.push('"pullRequestsCount" = $(pullRequestsCount)')
273+
params.pullRequestsCount = data.pullRequestsCount
274+
}
275+
if (data.issuesCount !== undefined) {
276+
setClauses.push('"issuesCount" = $(issuesCount)')
277+
params.issuesCount = data.issuesCount
278+
}
279+
if (data.onboarded !== undefined) {
280+
setClauses.push('onboarded = $(onboarded)')
281+
params.onboarded = data.onboarded
282+
}
283+
if (data.onboardedAt !== undefined) {
284+
setClauses.push('"onboardedAt" = $(onboardedAt)')
285+
params.onboardedAt = data.onboardedAt
286+
}
287+
288+
if (setClauses.length === 0) {
289+
return findEvaluatedProjectById(qx, id)
290+
}
291+
292+
return qx.selectOneOrNone(
293+
`
294+
UPDATE "evaluatedProjects"
295+
SET
296+
${setClauses.join(',\n ')},
297+
"updatedAt" = NOW()
298+
WHERE id = $(id)
299+
RETURNING ${prepareSelectColumns(EVALUATED_PROJECT_COLUMNS)}
300+
`,
301+
params,
302+
)
303+
}
304+
305+
export async function markEvaluatedProjectAsEvaluated(
306+
qx: QueryExecutor,
307+
id: string,
308+
data: {
309+
evaluationScore: number
310+
evaluation: Record<string, unknown>
311+
evaluationReason?: string
312+
},
313+
): Promise<IDbEvaluatedProject | null> {
314+
return qx.selectOneOrNone(
315+
`
316+
UPDATE "evaluatedProjects"
317+
SET
318+
"evaluationStatus" = 'evaluated',
319+
"evaluationScore" = $(evaluationScore),
320+
evaluation = $(evaluation),
321+
"evaluationReason" = $(evaluationReason),
322+
"evaluatedAt" = NOW(),
323+
"updatedAt" = NOW()
324+
WHERE id = $(id)
325+
RETURNING ${prepareSelectColumns(EVALUATED_PROJECT_COLUMNS)}
326+
`,
327+
{
328+
id,
329+
evaluationScore: data.evaluationScore,
330+
evaluation: JSON.stringify(data.evaluation),
331+
evaluationReason: data.evaluationReason ?? null,
332+
},
333+
)
334+
}
335+
336+
export async function markEvaluatedProjectAsOnboarded(
337+
qx: QueryExecutor,
338+
id: string,
339+
): Promise<void> {
340+
await qx.selectNone(
341+
`
342+
UPDATE "evaluatedProjects"
343+
SET
344+
onboarded = true,
345+
"onboardedAt" = NOW(),
346+
"updatedAt" = NOW()
347+
WHERE id = $(id)
348+
`,
349+
{ id },
350+
)
351+
}
352+
353+
export async function deleteEvaluatedProject(qx: QueryExecutor, id: string): Promise<number> {
354+
return qx.result(
355+
`
356+
DELETE FROM "evaluatedProjects"
357+
WHERE id = $(id)
358+
`,
359+
{ id },
360+
)
361+
}
362+
363+
export async function deleteEvaluatedProjectByProjectCatalogId(
364+
qx: QueryExecutor,
365+
projectCatalogId: string,
366+
): Promise<number> {
367+
return qx.result(
368+
`
369+
DELETE FROM "evaluatedProjects"
370+
WHERE "projectCatalogId" = $(projectCatalogId)
371+
`,
372+
{ projectCatalogId },
373+
)
374+
}
375+
376+
export async function findPendingEvaluatedProjectsWithCatalog(
377+
qx: QueryExecutor,
378+
options: { limit?: number } = {},
379+
): Promise<(IDbEvaluatedProject & { projectSlug: string; repoName: string; repoUrl: string })[]> {
380+
const { limit } = options
381+
382+
return qx.select(
383+
`
384+
SELECT
385+
${prepareSelectColumns(EVALUATED_PROJECT_COLUMNS, 'ep')},
386+
pc."projectSlug",
387+
pc."repoName",
388+
pc."repoUrl"
389+
FROM "evaluatedProjects" ep
390+
JOIN "projectCatalog" pc ON pc.id = ep."projectCatalogId"
391+
WHERE ep."evaluationStatus" = 'pending'
392+
ORDER BY ep."createdAt" ASC
393+
${limit !== undefined ? 'LIMIT $(limit)' : ''}
394+
`,
395+
{ limit },
396+
)
397+
}

0 commit comments

Comments
 (0)