UNPKG

@compas/store

Version:

Postgres & S3-compatible wrappers for common things

549 lines (531 loc) 14.7 kB
// Generated by @compas/code-gen import { AppError, isNil } from "@compas/stdlib"; import { wrapQueryPart } from "../common/database-helpers.js"; import { validateQueryResultStoreFile } from "../queryResult/validators.js"; import { validateStoreFile, validateStoreFileInsertValidated, validateStoreFileOrderBy, validateStoreFileOrderBySpec, validateStoreFileQueryBuilderValidated, validateStoreFileUpdateValidated, validateStoreFileWhereValidated, } from "../store/validators.js"; import { generatedQueryBuilderHelper, generatedUpdateHelper, generatedWhereBuilderHelper, isQueryPart, query, } from "@compas/store"; export const fileQueries = { fileCount, fileInsert, fileUpdate, fileDelete, fileUpsertOnId, }; /** @type {any} */ export const fileWhereSpec = { fieldSpecification: [ { tableKey: "id", keyType: "uuid", matchers: [ { matcherKey: "id", matcherType: "equal", }, { matcherKey: "idNotEqual", matcherType: "notEqual", }, { matcherKey: "idIn", matcherType: "in", }, { matcherKey: "idNotIn", matcherType: "notIn", }, ], }, { tableKey: "bucketName", keyType: "varchar", matchers: [ { matcherKey: "bucketName", matcherType: "equal", }, { matcherKey: "bucketNameNotEqual", matcherType: "notEqual", }, { matcherKey: "bucketNameIn", matcherType: "in", }, { matcherKey: "bucketNameNotIn", matcherType: "notIn", }, { matcherKey: "bucketNameLike", matcherType: "like", }, { matcherKey: "bucketNameILike", matcherType: "iLike", }, { matcherKey: "bucketNameNotLike", matcherType: "notLike", }, ], }, { 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").StoreFileWhere} [where] * @param {{ skipValidator?: boolean, shortName?: string }} [options] * @returns {import("@compas/store").QueryPart<any>} */ export function fileWhere(where, options = {}) { options.shortName ??= "f."; if (!options.shortName.endsWith(".")) { options.shortName += "."; } if (!options.skipValidator) { const { error, value } = validateStoreFileWhereValidated(where ?? {}); if (error) { throw AppError.serverError({ message: "Invalid where object", error }); } where = value; } return generatedWhereBuilderHelper( fileWhereSpec, 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").StoreFileOrderBy} [orderBy] * @param {import("../common/types.js").StoreFileOrderBySpec} [orderBySpec] * @param {{ skipValidator?: boolean, shortName?: string }} [options] * @returns {import("@compas/store").QueryPart<any>} */ export function fileOrderBy(orderBy, orderBySpec, options = {}) { options.shortName ??= "f."; if (!options.shortName.endsWith(".")) { options.shortName += "."; } orderBy ??= ["createdAt", "updatedAt", "id"]; orderBySpec ??= {}; if (!options.skipValidator) { const validatedOrderBy = validateStoreFileOrderBy(orderBy); if (validatedOrderBy.error) { throw AppError.serverError({ message: "Invalid orderBy array", error: validatedOrderBy.error, }); } orderBy = validatedOrderBy.value; const validatedOrderBySpec = validateStoreFileOrderBySpec(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 'file' table * * @param {import("@compas/store").Postgres} sql * @param {import("../common/types.js").StoreFileWhere} where * @returns {Promise<number>} */ async function fileCount(sql, where) { const [result] = await query`select count(f."id") as "recordCount" FROM "file" f WHERE ${fileWhere(where)}`.exec( sql, ); return Number(result?.recordCount ?? "0"); } /** * Insert a record in the 'file' table * * @param {import("@compas/store").Postgres} sql * @param {import("../common/types.js").StoreFileInsert["insert"]} insert * @param {{ withPrimaryKey?: boolean }} [_options={}] * @returns {Promise<Array<import("../common/types.js").StoreFile>>} */ function fileInsert(sql, insert, _options = {}) { if (insert === undefined || (Array.isArray(insert) && insert.length === 0)) { return Promise.resolve([]); } return fileInsertInternal({ insert, returning: "*" }).exec(sql); } /** * Insert a record in the 'file' table * * @param {import("../common/types.js").StoreFileInsert} input * @returns {import("@compas/store").WrappedQueryPart<import("../common/types.js").StoreFile>} */ function fileInsertInternal(input) { const { error, value: validatedInput } = validateStoreFileInsertValidated(input); if (error) { throw AppError.serverError({ message: "Insert input validation failed", error, }); } const qb = query` INSERT INTO "file" ("id","contentLength","bucketName","contentType","name","meta","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.contentLength ?? null); str.push(", "); args.push(insert.bucketName ?? null); str.push(", "); args.push(insert.contentType ?? null); str.push(", "); args.push(insert.name ?? null); str.push(", "); if (!isNil(insert.meta)) { args.push(JSON.stringify(insert.meta)); } 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","contentLength","bucketName","contentType","name","meta","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, validateStoreFile, { hasCustomReturning: Array.isArray(validatedInput.returning), }); } /** * Upsert a record in the 'file' table * * @param {import("@compas/store").Postgres} sql * @param {import("../common/types.js").StoreFileInsert["insert"]} insert * @returns {Promise<Array<import("../common/types.js").StoreFile>>} */ function fileUpsertOnId(sql, insert) { return fileUpsertOnIdInternal({ insert, returning: "*" }).exec(sql); } /** * Upsert a record in the 'file' table based on the primary key. * * @param {import("../common/types.js").StoreFileInsert} input * @returns {import("@compas/store").WrappedQueryPart<import("../common/types.js").StoreFile>} */ function fileUpsertOnIdInternal(input) { const { error, value: validatedInput } = validateStoreFileInsertValidated(input); if (error) { throw AppError.serverError({ message: "Insert input validation failed", error, }); } const { queryPart } = fileInsertInternal({ insert: input.insert }); /** @type {Array<string>} */ const str = []; const args = []; str.push(`ON CONFLICT ("id") DO UPDATE SET "contentLength" = COALESCE(EXCLUDED."contentLength", "file"."contentLength"), "bucketName" = COALESCE(EXCLUDED."bucketName", "file"."bucketName"), "contentType" = COALESCE(EXCLUDED."contentType", "file"."contentType"), "name" = COALESCE(EXCLUDED."name", "file"."name"), "meta" = COALESCE(EXCLUDED."meta", "file"."meta"), "updatedAt" = COALESCE(EXCLUDED."updatedAt", "file"."updatedAt")`); if (validatedInput.returning === "*") { str.push( ` RETURNING "id","contentLength","bucketName","contentType","name","meta","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, validateStoreFile, { hasCustomReturning: Array.isArray(validatedInput.returning), }); } /** @type {any} */ const fileUpdateSpec = { schemaName: "", name: "file", shortName: "f", columns: [ "id", "contentLength", "bucketName", "contentType", "name", "meta", "createdAt", "updatedAt", ], where: fileWhereSpec, injectUpdatedAt: true, fields: { id: { type: "uuid", atomicUpdates: [], }, contentLength: { type: "number", atomicUpdates: ["$add", "$subtract", "$multiply", "$divide"], }, bucketName: { type: "string", atomicUpdates: ["$append"], }, contentType: { type: "string", atomicUpdates: ["$append"], }, name: { type: "string", atomicUpdates: ["$append"], }, meta: { type: "jsonb", atomicUpdates: ["$set", "$remove"], }, createdAt: { type: "date", atomicUpdates: ["$add", "$subtract"], }, updatedAt: { type: "date", atomicUpdates: ["$add", "$subtract"], }, }, }; /** * Insert a record in the 'file' table * * @param {import("@compas/store").Postgres} sql * @param {import("../common/types.js").StoreFileUpdate} update * @returns {Promise<Array<import("../common/types.js").StoreFile>>} */ function fileUpdate(sql, update) { if (update?.returning === "*" || !update?.returning) { return fileUpdateInternal(update).exec(sql); } // @ts-expect-error return fileUpdateInternal(update).execRaw(sql); } /** * Update records in the 'file' table * * @param {import("../common/types.js").StoreFileUpdate} input * @returns {import("@compas/store").WrappedQueryPart<import("../common/types.js").StoreFile>} */ function fileUpdateInternal(input) { const { error, value: validatedInput } = validateStoreFileUpdateValidated(input); if (error) { throw AppError.serverError({ message: "Update input validation failed", error, }); } return wrapQueryPart( generatedUpdateHelper(fileUpdateSpec, validatedInput), validateStoreFile, { hasCustomReturning: Array.isArray(validatedInput.returning) }, ); } /** * Insert a record in the 'file' table * * @param {import("@compas/store").Postgres} sql * @param {import("../common/types.js").StoreFileWhere} [where] * @returns {Promise<void>} */ function fileDelete(sql, where = {}) { return fileDeleteInternal(where).exec(sql); } /** * Remove records from the 'file' table * * @param {import("../common/types.js").StoreFileWhere} [where] * @returns {import("@compas/store").QueryPart<any>} */ function fileDeleteInternal(where = {}) { return query`DELETE FROM "file" f WHERE ${fileWhere(where)}`; } /** @type {any} */ export const fileQueryBuilderSpec = { name: "file", shortName: "f", orderByExperimental: fileOrderBy, where: fileWhereSpec, columns: [ "id", "contentLength", "bucketName", "contentType", "name", "meta", "createdAt", "updatedAt", ], relations: [], }; /** * Query records in the 'file' table, optionally joining related tables. * * @param {import("../common/types.js").StoreFileQueryBuilder} [input] * @returns {import("@compas/store").WrappedQueryPart<import("../common/types.js").QueryResultStoreFile>} */ export function queryFile(input = {}) { const { error, value: validatedInput } = validateStoreFileQueryBuilderValidated(input); if (error) { throw AppError.serverError({ message: "Query builder input validation failed", error, }); } return wrapQueryPart( generatedQueryBuilderHelper(fileQueryBuilderSpec, validatedInput, {}), validateQueryResultStoreFile, { hasCustomReturning: validatedInput.select?.length !== 8 }, ); }