UNPKG

@compas/store

Version:

Postgres & S3-compatible wrappers for common things

631 lines (613 loc) 16.7 kB
// Generated by @compas/code-gen import { AppError, isNil } from "@compas/stdlib"; import { wrapQueryPart } from "../common/database-helpers.js"; import { validateQueryResultStoreJob } from "../queryResult/validators.js"; import { validateStoreJob, validateStoreJobInsertValidated, validateStoreJobOrderBy, validateStoreJobOrderBySpec, validateStoreJobQueryBuilderValidated, validateStoreJobUpdateValidated, validateStoreJobWhereValidated, } from "../store/validators.js"; import { generatedQueryBuilderHelper, generatedUpdateHelper, generatedWhereBuilderHelper, isQueryPart, query, } from "@compas/store"; export const jobQueries = { jobCount, jobInsert, jobUpdate, jobDelete, jobUpsertOnId, }; /** @type {any} */ export const jobWhereSpec = { fieldSpecification: [ { tableKey: "id", keyType: "int", matchers: [ { matcherKey: "id", matcherType: "equal", }, { matcherKey: "idNotEqual", matcherType: "notEqual", }, { matcherKey: "idIn", matcherType: "in", }, { matcherKey: "idNotIn", matcherType: "notIn", }, { matcherKey: "idGreaterThan", matcherType: "greaterThan", }, { matcherKey: "idLowerThan", matcherType: "lowerThan", }, ], }, { tableKey: "isComplete", keyType: "uuid", matchers: [ { matcherKey: "isComplete", matcherType: "equal", }, { matcherKey: "isCompleteIsNull", matcherType: "isNull", }, { matcherKey: "isCompleteIsNotNull", matcherType: "isNotNull", }, ], }, { tableKey: "name", keyType: "varchar", matchers: [ { matcherKey: "name", matcherType: "equal", }, { matcherKey: "nameNotEqual", matcherType: "notEqual", }, { matcherKey: "nameIn", matcherType: "in", }, { matcherKey: "nameNotIn", matcherType: "notIn", }, { matcherKey: "nameLike", matcherType: "like", }, { matcherKey: "nameILike", matcherType: "iLike", }, { matcherKey: "nameNotLike", matcherType: "notLike", }, ], }, { tableKey: "scheduledAt", keyType: "timestamptz", matchers: [ { matcherKey: "scheduledAt", matcherType: "equal", }, { matcherKey: "scheduledAtNotEqual", matcherType: "notEqual", }, { matcherKey: "scheduledAtIn", matcherType: "in", }, { matcherKey: "scheduledAtNotIn", matcherType: "notIn", }, { matcherKey: "scheduledAtGreaterThan", matcherType: "greaterThan", }, { matcherKey: "scheduledAtLowerThan", matcherType: "lowerThan", }, { matcherKey: "scheduledAtIsNull", matcherType: "isNull", }, { matcherKey: "scheduledAtIsNotNull", matcherType: "isNotNull", }, ], }, { tableKey: "createdAt", keyType: "timestamptz", matchers: [ { matcherKey: "createdAt", matcherType: "equal", }, { matcherKey: "createdAtNotEqual", matcherType: "notEqual", }, { matcherKey: "createdAtIn", matcherType: "in", }, { matcherKey: "createdAtNotIn", matcherType: "notIn", }, { matcherKey: "createdAtGreaterThan", matcherType: "greaterThan", }, { matcherKey: "createdAtLowerThan", matcherType: "lowerThan", }, ], }, { tableKey: "updatedAt", keyType: "timestamptz", matchers: [ { matcherKey: "updatedAt", matcherType: "equal", }, { matcherKey: "updatedAtNotEqual", matcherType: "notEqual", }, { matcherKey: "updatedAtIn", matcherType: "in", }, { matcherKey: "updatedAtNotIn", matcherType: "notIn", }, { matcherKey: "updatedAtGreaterThan", matcherType: "greaterThan", }, { matcherKey: "updatedAtLowerThan", matcherType: "lowerThan", }, ], }, ], }; /** * Reusable where clause generator. This is used by other generated queries, and can be used inline * in custom queries. * * @param {import("../common/types.js").StoreJobWhere} [where] * @param {{ skipValidator?: boolean, shortName?: string }} [options] * @returns {import("@compas/store").QueryPart<any>} */ export function jobWhere(where, options = {}) { options.shortName ??= "j."; if (!options.shortName.endsWith(".")) { options.shortName += "."; } if (!options.skipValidator) { const { error, value } = validateStoreJobWhereValidated(where ?? {}); if (error) { throw AppError.serverError({ message: "Invalid where object", error }); } where = value; } return generatedWhereBuilderHelper( jobWhereSpec, where ?? {}, options.shortName, ); } /** * Reusable ORDER BY clause generator. This is used by other generated queries, and can be used * inline in custom queries. * * @param {import("../common/types.js").StoreJobOrderBy} [orderBy] * @param {import("../common/types.js").StoreJobOrderBySpec} [orderBySpec] * @param {{ skipValidator?: boolean, shortName?: string }} [options] * @returns {import("@compas/store").QueryPart<any>} */ export function jobOrderBy(orderBy, orderBySpec, options = {}) { options.shortName ??= "j."; if (!options.shortName.endsWith(".")) { options.shortName += "."; } orderBy ??= ["createdAt", "updatedAt", "id"]; orderBySpec ??= {}; if (!options.skipValidator) { const validatedOrderBy = validateStoreJobOrderBy(orderBy); if (validatedOrderBy.error) { throw AppError.serverError({ message: "Invalid orderBy array", error: validatedOrderBy.error, }); } orderBy = validatedOrderBy.value; const validatedOrderBySpec = validateStoreJobOrderBySpec(orderBySpec); if (validatedOrderBySpec.error) { throw AppError.serverError({ message: "Invalid orderBySpec object", error: validatedOrderBySpec.error, }); } orderBySpec = validatedOrderBySpec.value; } if (isQueryPart(orderBy)) { return orderBy; } let str = ""; for (const value of orderBy) { if (str.length !== 0) { str += ", "; } str += `${options.shortName}"${value}" ${orderBySpec[value] ?? "ASC"}`; } return query([str]); } /** * Count the records in the 'job' table * * @param {import("@compas/store").Postgres} sql * @param {import("../common/types.js").StoreJobWhere} where * @returns {Promise<number>} */ async function jobCount(sql, where) { const [result] = await query`select count(j."id") as "recordCount" FROM "job" j WHERE ${jobWhere(where)}`.exec( sql, ); return Number(result?.recordCount ?? "0"); } /** * Insert a record in the 'job' table * * @param {import("@compas/store").Postgres} sql * @param {import("../common/types.js").StoreJobInsert["insert"]} insert * @param {{ withPrimaryKey?: boolean }} [_options={}] * @returns {Promise<Array<import("../common/types.js").StoreJob>>} */ function jobInsert(sql, insert, _options = {}) { if (insert === undefined || (Array.isArray(insert) && insert.length === 0)) { return Promise.resolve([]); } return jobInsertInternal({ insert, returning: "*" }).exec(sql); } /** * Insert a record in the 'job' table * * @param {import("../common/types.js").StoreJobInsert} input * @returns {import("@compas/store").WrappedQueryPart<import("../common/types.js").StoreJob>} */ function jobInsertInternal(input) { const { error, value: validatedInput } = validateStoreJobInsertValidated(input); if (error) { throw AppError.serverError({ message: "Insert input validation failed", error, }); } const qb = query` INSERT INTO "job" ("id","isComplete","handlerTimeout","priority","retryCount","name","scheduledAt","data","createdAt","updatedAt") VALUES `; /** @type {Array<string>} */ const str = []; const args = []; for (const insert of validatedInput.insert) { if (str.length) { str.push(", ("); } else { str.push("("); } if (isNil(insert.id)) { args.push(undefined); str.push("DEFAULT, "); } else { args.push(insert.id); str.push(", "); } args.push(insert.isComplete ?? null); str.push(", "); args.push(insert.handlerTimeout ?? null); str.push(", "); args.push(insert.priority ?? null); str.push(", "); args.push(insert.retryCount ?? null); str.push(", "); args.push(insert.name ?? null); str.push(", "); args.push(insert.scheduledAt ?? null); str.push(", "); if (!isNil(insert.data)) { args.push(JSON.stringify(insert.data)); } else { args.push(null); } str.push(", "); if (isNil(insert.createdAt)) { args.push(undefined); str.push("DEFAULT, "); } else { args.push(insert.createdAt); str.push(", "); } if (isNil(insert.updatedAt)) { args.push(undefined); str.push("DEFAULT, "); } else { args.push(insert.updatedAt); str.push(", "); } // We have added an extra comma, so remove it. str[str.length - 1] = str.at(-1)?.slice(0, -2) ?? ""; args.push(undefined); str.push(")"); args.push(undefined); } if (validatedInput.returning === "*") { str.push( ` RETURNING "id","isComplete","handlerTimeout","priority","retryCount","name","scheduledAt","data","createdAt","updatedAt"`, ); args.push(undefined); } else if (Array.isArray(validatedInput.returning)) { str.push( ` RETURNING ${JSON.stringify(validatedInput.returning).slice(1, -1)}`, ); args.push(undefined); } qb.append(query(str, ...args)); return wrapQueryPart(qb, validateStoreJob, { hasCustomReturning: Array.isArray(validatedInput.returning), }); } /** * Upsert a record in the 'job' table * * @param {import("@compas/store").Postgres} sql * @param {import("../common/types.js").StoreJobInsert["insert"]} insert * @returns {Promise<Array<import("../common/types.js").StoreJob>>} */ function jobUpsertOnId(sql, insert) { return jobUpsertOnIdInternal({ insert, returning: "*" }).exec(sql); } /** * Upsert a record in the 'job' table based on the primary key. * * @param {import("../common/types.js").StoreJobInsert} input * @returns {import("@compas/store").WrappedQueryPart<import("../common/types.js").StoreJob>} */ function jobUpsertOnIdInternal(input) { const { error, value: validatedInput } = validateStoreJobInsertValidated(input); if (error) { throw AppError.serverError({ message: "Insert input validation failed", error, }); } const { queryPart } = jobInsertInternal({ insert: input.insert }); /** @type {Array<string>} */ const str = []; const args = []; str.push(`ON CONFLICT ("id") DO UPDATE SET "isComplete" = COALESCE(EXCLUDED."isComplete", "job"."isComplete"), "handlerTimeout" = COALESCE(EXCLUDED."handlerTimeout", "job"."handlerTimeout"), "priority" = COALESCE(EXCLUDED."priority", "job"."priority"), "retryCount" = COALESCE(EXCLUDED."retryCount", "job"."retryCount"), "name" = COALESCE(EXCLUDED."name", "job"."name"), "scheduledAt" = COALESCE(EXCLUDED."scheduledAt", "job"."scheduledAt"), "data" = COALESCE(EXCLUDED."data", "job"."data"), "updatedAt" = COALESCE(EXCLUDED."updatedAt", "job"."updatedAt")`); if (validatedInput.returning === "*") { str.push( ` RETURNING "id","isComplete","handlerTimeout","priority","retryCount","name","scheduledAt","data","createdAt","updatedAt"`, ); args.push(undefined); } else if (Array.isArray(validatedInput.returning)) { str.push( ` RETURNING ${JSON.stringify(validatedInput.returning).slice(1, -1)}`, ); args.push(undefined); } queryPart.append(query(str, ...args)); return wrapQueryPart(queryPart, validateStoreJob, { hasCustomReturning: Array.isArray(validatedInput.returning), }); } /** @type {any} */ const jobUpdateSpec = { schemaName: "", name: "job", shortName: "j", columns: [ "id", "isComplete", "handlerTimeout", "priority", "retryCount", "name", "scheduledAt", "data", "createdAt", "updatedAt", ], where: jobWhereSpec, injectUpdatedAt: true, fields: { id: { type: "number", atomicUpdates: ["$add", "$subtract", "$multiply", "$divide"], }, isComplete: { type: "boolean", atomicUpdates: ["$negate"], }, handlerTimeout: { type: "number", atomicUpdates: ["$add", "$subtract", "$multiply", "$divide"], }, priority: { type: "number", atomicUpdates: ["$add", "$subtract", "$multiply", "$divide"], }, retryCount: { type: "number", atomicUpdates: ["$add", "$subtract", "$multiply", "$divide"], }, name: { type: "string", atomicUpdates: ["$append"], }, scheduledAt: { type: "date", atomicUpdates: ["$add", "$subtract"], }, data: { type: "jsonb", atomicUpdates: ["$set", "$remove"], }, createdAt: { type: "date", atomicUpdates: ["$add", "$subtract"], }, updatedAt: { type: "date", atomicUpdates: ["$add", "$subtract"], }, }, }; /** * Insert a record in the 'job' table * * @param {import("@compas/store").Postgres} sql * @param {import("../common/types.js").StoreJobUpdate} update * @returns {Promise<Array<import("../common/types.js").StoreJob>>} */ function jobUpdate(sql, update) { if (update?.returning === "*" || !update?.returning) { return jobUpdateInternal(update).exec(sql); } // @ts-expect-error return jobUpdateInternal(update).execRaw(sql); } /** * Update records in the 'job' table * * @param {import("../common/types.js").StoreJobUpdate} input * @returns {import("@compas/store").WrappedQueryPart<import("../common/types.js").StoreJob>} */ function jobUpdateInternal(input) { const { error, value: validatedInput } = validateStoreJobUpdateValidated(input); if (error) { throw AppError.serverError({ message: "Update input validation failed", error, }); } return wrapQueryPart( generatedUpdateHelper(jobUpdateSpec, validatedInput), validateStoreJob, { hasCustomReturning: Array.isArray(validatedInput.returning) }, ); } /** * Insert a record in the 'job' table * * @param {import("@compas/store").Postgres} sql * @param {import("../common/types.js").StoreJobWhere} [where] * @returns {Promise<void>} */ function jobDelete(sql, where = {}) { return jobDeleteInternal(where).exec(sql); } /** * Remove records from the 'job' table * * @param {import("../common/types.js").StoreJobWhere} [where] * @returns {import("@compas/store").QueryPart<any>} */ function jobDeleteInternal(where = {}) { return query`DELETE FROM "job" j WHERE ${jobWhere(where)}`; } /** @type {any} */ export const jobQueryBuilderSpec = { name: "job", shortName: "j", orderByExperimental: jobOrderBy, where: jobWhereSpec, columns: [ "id", "isComplete", "handlerTimeout", "priority", "retryCount", "name", "scheduledAt", "data", "createdAt", "updatedAt", ], relations: [], }; /** * Query records in the 'job' table, optionally joining related tables. * * @param {import("../common/types.js").StoreJobQueryBuilder} [input] * @returns {import("@compas/store").WrappedQueryPart<import("../common/types.js").QueryResultStoreJob>} */ export function queryJob(input = {}) { const { error, value: validatedInput } = validateStoreJobQueryBuilderValidated(input); if (error) { throw AppError.serverError({ message: "Query builder input validation failed", error, }); } return wrapQueryPart( generatedQueryBuilderHelper(jobQueryBuilderSpec, validatedInput, {}), validateQueryResultStoreJob, { hasCustomReturning: validatedInput.select?.length !== 10 }, ); }