From eb2bc3e421d40b9a76c53c42328f7ce25342e2b6 Mon Sep 17 00:00:00 2001 From: Anton Date: Sun, 8 Sep 2024 19:04:48 +0300 Subject: [PATCH] feat: updated pg-core domain methods --- src/lib/mysql/domain/domain.ts | 8 +- src/lib/pg/domain/domain.ts | 26 +- src/lib/pg/domain/materialized-view.ts | 45 ++- src/lib/pg/domain/sequence.ts | 1 + src/lib/pg/domain/table.ts | 68 +++- src/lib/pg/domain/types.ts | 12 +- src/lib/pg/domain/view.ts | 31 +- src/lib/pg/model/materialized-view.ts | 2 +- src/lib/pg/model/table.ts | 316 +++++++++++++++++- src/lib/pg/query-builder/query-builder.ts | 46 ++- src/lib/pg/query-builder/query-handler.ts | 5 +- src/test/PG-01/index.ts | 15 +- src/test/PG-02/index.ts | 5 +- src/test/PG-03/index.ts | 41 +-- src/test/PG-03/user-role/domain.ts | 20 +- src/test/PG-03/user-role/index.ts | 2 +- src/test/PG-03/user-role/model.ts | 19 ++ src/test/PG-03/user-role/model/index.ts | 2 - src/test/PG-03/user-role/model/model.ts | 38 --- src/test/PG-03/user-role/{model => }/types.ts | 8 +- src/test/PG-03/user/domain.ts | 67 +++- src/test/PG-03/user/index.ts | 2 +- src/test/PG-03/user/model.ts | 23 ++ src/test/PG-03/user/model/index.ts | 2 - src/test/PG-03/user/model/model.ts | 82 ----- src/test/PG-03/user/{model => }/types.ts | 19 +- src/test/PG-04/index.ts | 5 +- src/test/PG-04/user/model/types.ts | 2 +- src/test/PG-05/file-system/domain.ts | 34 +- src/test/PG-05/file-system/model/types.ts | 5 + src/test/PG-05/index.ts | 5 +- src/test/PG-06/index.ts | 13 +- 32 files changed, 669 insertions(+), 300 deletions(-) create mode 100644 src/test/PG-03/user-role/model.ts delete mode 100644 src/test/PG-03/user-role/model/index.ts delete mode 100644 src/test/PG-03/user-role/model/model.ts rename src/test/PG-03/user-role/{model => }/types.ts (66%) create mode 100644 src/test/PG-03/user/model.ts delete mode 100644 src/test/PG-03/user/model/index.ts delete mode 100644 src/test/PG-03/user/model/model.ts rename src/test/PG-03/user/{model => }/types.ts (59%) diff --git a/src/lib/mysql/domain/domain.ts b/src/lib/mysql/domain/domain.ts index 47c3db7..494186d 100644 --- a/src/lib/mysql/domain/domain.ts +++ b/src/lib/mysql/domain/domain.ts @@ -75,7 +75,7 @@ export class BaseDomain(options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; selected?: [T, ...T[]]; pagination?: SharedTypes.TPagination; order?: { @@ -93,7 +93,7 @@ export class BaseDomain>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; }): Promise { return this.model.getCountByParams({ $and: options.params, $or: options.paramsOr }); } @@ -103,7 +103,7 @@ export class BaseDomain(options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; selected?: [T, ...T[]]; }): Promise> { return this.model.getOneByParams( @@ -114,7 +114,7 @@ export class BaseDomain(options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; selected?: [T, ...T[]]; }): Promise<{ message?: string; one?: Pick; }> { const one = await this.model.getOneByParams( diff --git a/src/lib/pg/domain/domain.ts b/src/lib/pg/domain/domain.ts index e1d0e61..4a9a0ad 100644 --- a/src/lib/pg/domain/domain.ts +++ b/src/lib/pg/domain/domain.ts @@ -127,7 +127,7 @@ export class BaseDomain>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; }): Types.TCompareQueryResult => this.model.compareQuery.deleteByParams({ $and: options.params, $or: options.paramsOr }), /** @@ -153,7 +153,7 @@ export class BaseDomain(options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; selected?: [T, ...T[]]; pagination?: SharedTypes.TPagination; order?: { @@ -173,7 +173,7 @@ export class BaseDomain>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; }): Types.TCompareQueryResult => this.model.compareQuery.getCountByParams({ $and: options.params, $or: options.paramsOr }), /** @@ -199,7 +199,7 @@ export class BaseDomain>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; }, ): Types.TCompareQueryResult => this.model.compareQuery.getCountByPksAndParams(pks, { $and: options.params, $or: options.paramsOr }), @@ -215,7 +215,7 @@ export class BaseDomain(options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; selected?: [T, ...T[]]; }): Types.TCompareQueryResult => this.model.compareQuery.getOneByParams({ $and: options.params, $or: options.paramsOr }, options.selected as string[]), @@ -242,7 +242,7 @@ export class BaseDomain[] = Extract[]>( queryConditions: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; returningFields?: T; }, updateFields: Types.TConditionalRawParamsType, @@ -307,7 +307,7 @@ export class BaseDomain>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; }): Promise { return this.model.deleteByParams( { $and: options.params, $or: options.paramsOr }, @@ -341,7 +341,7 @@ export class BaseDomain(options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; selected?: [T, ...T[]]; pagination?: SharedTypes.TPagination; order?: { @@ -382,7 +382,7 @@ export class BaseDomain>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; }, ): Promise { return this.model.getCountByPksAndParams( @@ -402,7 +402,7 @@ export class BaseDomain>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; }): Promise { return this.model.getCountByParams({ $and: options.params, $or: options.paramsOr }); } @@ -412,7 +412,7 @@ export class BaseDomain(options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; selected?: [T, ...T[]]; }): Promise> { const one = await this.model.getOneByParams>( @@ -437,7 +437,7 @@ export class BaseDomain(options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; selected?: [T, ...T[]]; }): Promise<{ message?: string; one?: Pick; }> { const one = await this.model.getOneByParams>( @@ -482,7 +482,7 @@ export class BaseDomain[] = Extract[]>( queryConditions: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; returningFields?: T; }, updateFields: Types.TConditionalRawParamsType, diff --git a/src/lib/pg/domain/materialized-view.ts b/src/lib/pg/domain/materialized-view.ts index a67c6c5..ebf7c6b 100644 --- a/src/lib/pg/domain/materialized-view.ts +++ b/src/lib/pg/domain/materialized-view.ts @@ -9,6 +9,8 @@ export type BaseMaterializedViewGeneric = { }; /** + * A class representing a base materialized view with generic type parameters for handling database operations. + * * @experimental */ export class BaseMaterializedView< @@ -18,8 +20,18 @@ export class BaseMaterializedView< #name; #coreFields; + /** + * The model associated with this domain. + */ model; + /** + * Initializes a new instance of the `BaseMaterializedView` class. + * + * @param data - The domain data object containing the model. + * + * @throws {Error} If `data.model` is not an instance of `Model`. + */ constructor(data: Types.TDomain) { if (!(data.model instanceof Model)) { throw new Error("You need pass data.model extended of PG.Model.BaseMaterializedView"); @@ -31,10 +43,20 @@ export class BaseMaterializedView< this.#coreFields = this.model.coreFields; } + /** + * Gets the name of the database materialized view. + * + * @returns The name of the materialized view. + */ get name() { return this.#name; } + /** + * Gets the fields of the database materialized view. + * + * @returns An array of field names in the materialized view. + */ get coreFields() { return this.#coreFields; } @@ -58,7 +80,7 @@ export class BaseMaterializedView< */ getArrByParams: (options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; selected?: [T, ...T[]]; pagination?: SharedTypes.TPagination; order?: { @@ -78,7 +100,7 @@ export class BaseMaterializedView< */ getCountByParams: (options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; }): Types.TCompareQueryResult => this.model.compareQuery.getCountByParams({ $and: options.params, $or: options.paramsOr }), /** @@ -93,7 +115,7 @@ export class BaseMaterializedView< */ getOneByParams: (options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; selected?: [T, ...T[]]; }): Types.TCompareQueryResult => this.model.compareQuery.getOneByParams({ $and: options.params, $or: options.paramsOr }, options.selected as string[]), }; @@ -114,7 +136,7 @@ export class BaseMaterializedView< */ async getArrByParams(options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; selected?: [T, ...T[]]; pagination?: SharedTypes.TPagination; order?: { @@ -141,7 +163,7 @@ export class BaseMaterializedView< */ async getCountByParams(options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; }): Promise { return this.model.getCountByParams({ $and: options.params, $or: options.paramsOr }); } @@ -158,7 +180,7 @@ export class BaseMaterializedView< */ async getOneByParams(options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; selected?: [T, ...T[]]; }): Promise<{ message?: string; one?: Pick; }> { const one = await this.model.getOneByParams>( @@ -171,7 +193,14 @@ export class BaseMaterializedView< return { one }; } - async refresh(): Promise { - return this.model.refresh(); + /** + * Refreshes the materialized view. + * + * @param [concurrently=false] - Whether to refresh the view concurrently. + * + * @returns A promise that resolves when the view is refreshed. + */ + async refresh(concurrently: boolean = false): Promise { + return this.model.refresh(concurrently); } } diff --git a/src/lib/pg/domain/sequence.ts b/src/lib/pg/domain/sequence.ts index fd5dfd6..0fb7015 100644 --- a/src/lib/pg/domain/sequence.ts +++ b/src/lib/pg/domain/sequence.ts @@ -90,6 +90,7 @@ export class BaseSequence< * Sets the sequence to a specific value. * * @param value - The value to set the sequence to. + * * @returns */ async setValue(value: BSG): Promise { diff --git a/src/lib/pg/domain/table.ts b/src/lib/pg/domain/table.ts index b8993e7..4e7ccae 100644 --- a/src/lib/pg/domain/table.ts +++ b/src/lib/pg/domain/table.ts @@ -28,7 +28,7 @@ export class BaseTable< /** * The model associated with this domain. */ - model: M; + model; /** * Initializes a new instance of the `BaseTable` class. @@ -147,7 +147,7 @@ export class BaseTable< */ deleteByParams: (options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; }): Types.TCompareQueryResult => this.model.compareQuery.deleteByParams({ $and: options.params, $or: options.paramsOr }), /** @@ -173,7 +173,7 @@ export class BaseTable< */ getArrByParams: (options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; selected?: [T, ...T[]]; pagination?: SharedTypes.TPagination; order?: { @@ -193,7 +193,7 @@ export class BaseTable< */ getCountByParams: (options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; }): Types.TCompareQueryResult => this.model.compareQuery.getCountByParams({ $and: options.params, $or: options.paramsOr }), /** @@ -219,7 +219,7 @@ export class BaseTable< pks: T[], options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; }, ): Types.TCompareQueryResult => this.model.compareQuery.getCountByPksAndParams(pks, { $and: options.params, $or: options.paramsOr }), @@ -235,7 +235,7 @@ export class BaseTable< */ getOneByParams: (options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; selected?: [T, ...T[]]; }): Types.TCompareQueryResult => this.model.compareQuery.getOneByParams({ $and: options.params, $or: options.paramsOr }, options.selected as string[]), @@ -262,7 +262,7 @@ export class BaseTable< updateByParams: [] = Extract[]>( queryConditions: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; returningFields?: T; }, updateFields: Types.TConditionalRawParamsType, @@ -364,6 +364,46 @@ export class BaseTable< return this.model.deleteAll(); } + /** + * Drops the database table. + * + * @param [options] - The options for dropping the table. + * @param [options.cascade] - Whether to drop objects that depend on this table. + * @param [options.ifExists] - Whether to include the IF EXISTS clause. + * @param [options.restrict] - Whether to restrict the drop to prevent dropping the table if there are any dependent objects. + * + * @returns A promise that resolves when the table is dropped. + */ + async dropTable(options: { + cascade?: boolean; + ifExists?: boolean; + restrict?: boolean; + } = {}): Promise { + return this.model.dropTable(options); + } + + /** + * Truncates the database table. + * + * @param[options] - The options for truncating the table. + * @param [options.cascade] - Whether to truncate objects that depend on this table. + * @param [options.continueIdentity] - Whether to continue identity values. + * @param [options.restrict] - Whether to restrict the truncate to prevent truncating the table if there are any dependent objects. + * @param [options.only] - Whether to truncate only the specified table and not any of its descendant tables. + * @param [options.restartIdentity] - Whether to restart identity values. + * + * @returns A promise that resolves when the table is truncated. + */ + async truncateTable(options: { + cascade?: boolean; + continueIdentity?: boolean; + restrict?: boolean; + only?: boolean; + restartIdentity?: boolean; + } = {}): Promise { + return this.model.truncateTable(options); + } + /** * Deletes records based on the specified search parameters. * @@ -375,7 +415,7 @@ export class BaseTable< */ async deleteByParams(options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; }): Promise { return this.model.deleteByParams( { $and: options.params, $or: options.paramsOr }, @@ -409,7 +449,7 @@ export class BaseTable< */ async getArrByParams(this: BaseTable, options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; selected?: [T, ...T[]]; pagination?: SharedTypes.TPagination; order?: { @@ -450,7 +490,7 @@ export class BaseTable< pks: T[], options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; }, ): Promise { return this.model.getCountByPksAndParams( @@ -470,7 +510,7 @@ export class BaseTable< */ async getCountByParams(options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; }): Promise { return this.model.getCountByParams({ $and: options.params, $or: options.paramsOr }); } @@ -487,7 +527,7 @@ export class BaseTable< */ async getOneByParams(options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; selected?: [T, ...T[]]; }): Promise<{ message?: string; one?: Pick; }> { const one = await this.model.getOneByParams>( @@ -511,7 +551,7 @@ export class BaseTable< * @returns A promise that resolves to the retrieved record with the selected fields or a message if not found. */ async getOneByPk(pk: T): Promise<{ message?: string; one?: BTG["CoreFields"]; }> { - const one = await this.model.getOneByPk(pk); + const one = await this.model.getOneByPk(pk); if (!one) return { message: `Not found from ${this.model.tableName}` }; @@ -532,7 +572,7 @@ export class BaseTable< async updateByParams[] = Extract[]>( queryConditions: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; returningFields?: T; }, updateFields: Types.TConditionalRawParamsType, diff --git a/src/lib/pg/domain/types.ts b/src/lib/pg/domain/types.ts index 31dc29b..6db92c9 100644 --- a/src/lib/pg/domain/types.ts +++ b/src/lib/pg/domain/types.ts @@ -9,10 +9,14 @@ export type TDomain = { model: Model; }; export type TDomainFields = { [key: string]: any; }; export type TSearchParams = - & { [key in keyof T]: (null extends T[key] ? (null | { $eq: null; }) | TSearchParamValue> : TSearchParamValue>)} - & Partial>, TDefault>> + & TSearchParamsStrict + // & Partial>, TDefault>> & { [key: string]: TDefault | undefined; }; +export type TSearchParamsStrict = { + [key in keyof T]: (null extends T[key] ? (null | { $eq: null; }) | TSearchParamValue> : TSearchParamValue>) +}; + type TSearchParamValue = T extends object ? TSearchParamObjectValue : TSearchParamPrimitiveValue; @@ -103,7 +107,7 @@ type BaseBoolean = | { $ne: NonNullable | null; } | { $custom: { sign: string; value: string | number; }; }; -type Join = `${K}${P}`; +/* type Join = `${K}${P}`; type JsonKeysToStringStart = { [K in keyof T]: K extends string @@ -123,7 +127,7 @@ type JsonKeysToStringResult = { ? K | `${K}->${JsonKeysToStringStart}` : never : never -}[keyof T]; +}[keyof T]; */ type ClearBuffer = Omit>; type ClearDate = Omit>; diff --git a/src/lib/pg/domain/view.ts b/src/lib/pg/domain/view.ts index 44cc6f3..35da2e3 100644 --- a/src/lib/pg/domain/view.ts +++ b/src/lib/pg/domain/view.ts @@ -9,6 +9,8 @@ type BaseViewGeneric = { }; /** + * A class representing a base view with generic type parameters for handling database operations. + * * @experimental */ export class BaseView< @@ -23,6 +25,13 @@ export class BaseView< */ model; + /** + * Initializes a new instance of the `BaseView` class. + * + * @param data - The domain data object containing the model. + * + * @throws {Error} If `data.model` is not an instance of `Model`. + */ constructor(data: Types.TDomain) { if (!(data.model instanceof Model)) { throw new Error("You need pass data.model extended of PG.Model.BaseView"); @@ -34,10 +43,20 @@ export class BaseView< this.#coreFields = this.model.coreFields; } + /** + * Gets the name of the database view. + * + * @returns The name of the view. + */ get name() { return this.#name; } + /** + * Gets the fields of the database view. + * + * @returns An array of field names in the view. + */ get coreFields() { return this.#coreFields; } @@ -61,7 +80,7 @@ export class BaseView< */ getArrByParams: (options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; selected?: [T, ...T[]]; pagination?: SharedTypes.TPagination; order?: { @@ -81,7 +100,7 @@ export class BaseView< */ getCountByParams: (options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; }): Types.TCompareQueryResult => this.model.compareQuery.getCountByParams({ $and: options.params, $or: options.paramsOr }), /** @@ -96,7 +115,7 @@ export class BaseView< */ getOneByParams: (options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; selected?: [T, ...T[]]; }): Types.TCompareQueryResult => this.model.compareQuery.getOneByParams({ $and: options.params, $or: options.paramsOr }, options.selected as string[]), }; @@ -117,7 +136,7 @@ export class BaseView< */ async getArrByParams(options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; selected?: [T, ...T[]]; pagination?: SharedTypes.TPagination; order?: { orderBy: Extract | (BVG["AdditionalSortingFields"] extends string ? BVG["AdditionalSortingFields"] : never); @@ -143,7 +162,7 @@ export class BaseView< */ async getCountByParams(options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; }): Promise { return this.model.getCountByParams({ $and: options.params, $or: options.paramsOr }); } @@ -160,7 +179,7 @@ export class BaseView< */ async getOneByParams(options: { params: Types.TSearchParams>; - paramsOr?: Types.TArray2OrMore>>; + paramsOr?: Types.TSearchParams>[]; selected?: [T, ...T[]]; }): Promise<{ message?: string; one?: Pick; }> { const one = await this.model.getOneByParams>( diff --git a/src/lib/pg/model/materialized-view.ts b/src/lib/pg/model/materialized-view.ts index 0c842f2..14cbbbb 100644 --- a/src/lib/pg/model/materialized-view.ts +++ b/src/lib/pg/model/materialized-view.ts @@ -246,7 +246,7 @@ export class BaseMaterializedView { async refresh(concurrently: boolean = false): Promise { const query = `REFRESH MATERIALIZED VIEW ${concurrently ? "CONCURRENTLY" : ""} ${this.name}`; - await this.pool.query(query); + await this.#executeSql({ query, values: [] }); } /** diff --git a/src/lib/pg/model/table.ts b/src/lib/pg/model/table.ts index b7d7e61..dacb25a 100644 --- a/src/lib/pg/model/table.ts +++ b/src/lib/pg/model/table.ts @@ -9,6 +9,9 @@ import { QueryBuilder } from "../query-builder/index.js"; import queries from "./queries.js"; import { setLoggerAndExecutor } from "../helpers/index.js"; +/** + * Represents a base table with common database operations. + */ export class BaseTable { #insertOptions; #sortingOrders = new Set(["ASC", "DESC"]); @@ -26,6 +29,34 @@ export class BaseTable { tableFields: readonly string[]; updateField; + /** + * Creates an instance of BaseTable. + * + * Initializes a new instance of the `BaseTable` class with the provided table configuration and database credentials. + * Optionally, additional database options can be specified. + * + * @param data - The configuration object for the table, which includes: + * - `additionalSortingFields`: An optional array of fields to use for additional sorting. + * - `createField`: An optional object specifying a field to set with a timestamp when a record is created. + * - `title`: The field name. + * - `type`: The type of timestamp (`"unix_timestamp"` or `"timestamp"`). + * - `primaryKey`: The primary key field or fields for the table. This can be a single field or an array of fields. + * - `tableFields`: The fields in the table. + * - `tableName`: The name of the table. + * - `updateField`: An optional object specifying a field to update with a timestamp when a record is updated. + * - `title`: The field name. + * - `type`: The type of timestamp (`"unix_timestamp"` or `"timestamp"`). + * @param dbCreds - The credentials for connecting to the database, including: + * - `database`: The name of the database. + * - `host`: The database host. + * - `password`: The password for connecting to the database. + * - `port`: The port number for connecting to the database. + * - `user`: The username for connecting to the database. + * @param [options] - Optional configuration options for the database connection, which can include: + * - `insertOptions`: Configuration for insert operations. + * - `onConflict`: Specifies the action to take when there is a conflict on insert. + * - `poolClient`: An optional `pg.PoolClient` for managing database connections. + */ constructor( data: Types.TTable, dbCreds: Types.TDBCreds, @@ -59,7 +90,11 @@ export class BaseTable { } /** + * Sets the pool client in the current class. * @experimental + * @param poolClient - The pool client. + * + * @returns The current instance with the new pool client. */ setPoolClientInCurrentClass(poolClient: pg.PoolClient): this { // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -72,8 +107,12 @@ export class BaseTable { } /** - * @experimental - */ + * Sets the pool client in the base class. + * @experimental + * @param poolClient - The pool client. + * + * @returns A new instance of the base class with the new pool client. + */ setPoolClientInBaseClass(poolClient: pg.PoolClient): BaseTable { return new BaseTable( { ...this.#initialArgs.data }, @@ -339,6 +378,14 @@ export class BaseTable { }, }; + /** + * Deletes all records from the table. + * + * This method executes a `DELETE` SQL statement to remove all records from the table, leaving it empty. + * It does not return any data or status, only ensures that the operation has been completed. + * + * @returns A promise that resolves when the delete operation has been successfully completed. + */ async deleteAll(): Promise { const sql = this.compareQuery.deleteAll(); @@ -347,6 +394,13 @@ export class BaseTable { return; } + /** + * Deletes a single record from the database based on the provided primary key. + * + * @param primaryKey - The value of the primary key of the record to be deleted. The type of the primary key depends on the table schema and could be a string, number, or any other type. + * + * @returns A promise that resolves to the primary key of the deleted record if found, or `null` if no record with the given primary key was found. + */ async deleteOneByPk(primaryKey: T): Promise { const sql = this.compareQuery.deleteOneByPk(primaryKey); @@ -361,6 +415,15 @@ export class BaseTable { } } + /** + * Deletes records from the database based on the specified search parameters. + * + * @param params - The search parameters to identify which records to delete. + * @param params.$and - The conditions that must be met for a record to be deleted. This is an array of conditions that are combined with logical AND. + * @param [params.$or] - Optional. An array of additional conditions combined with logical OR. If provided, records that match any of these conditions will also be deleted. + * + * @returns A promise that resolves to `null` once the delete operation is complete. + */ async deleteByParams( params: { $and: Types.TSearchParams; $or?: Types.TSearchParams[]; }, ): Promise { @@ -371,18 +434,100 @@ export class BaseTable { return null; } + /** + * Drops the table from the database, with optional additional behaviors. + * + * @param [options={}] - Options to customize the drop operation. + * @param [options.cascade=false] - If true, drops tables that have foreign key references to this table. + * @param [options.ifExists=false] - If true, does not raise an error if the table does not exist. + * @param [options.restrict=false] - If true, prevents dropping the table if there are any foreign key references. + * + * @returns A promise that resolves when the drop operation is complete. + */ + async dropTable(options: { + cascade?: boolean; + ifExists?: boolean; + restrict?: boolean; + } = {}): Promise { + const { + cascade = false, + ifExists = false, + restrict = false, + } = options; + const behaviorOption = cascade ? "CASCADE" : restrict ? "RESTRICT" : ""; + const query = `DROP TABLE ${ifExists ? "IF EXISTS " : ""}${this.tableName} ${behaviorOption};`; + + await this.#executeSql({ query, values: [] }); + } + + /** + * Truncates the table, removing all records and optionally applying additional options. + * + * @param [options={}] - Options to customize the truncate operation. + * @param [options.cascade=false] - If true, truncates tables that have foreign key references to this table. + * @param [options.continueIdentity=false] - If true, does not reset the sequence for identity columns. + * @param [options.only=false] - If true, only truncates the specified table and not its partitions. + * @param [options.restrict=false] - If true, prevents truncating if there are any foreign key references. + * @param [options.restartIdentity=false] - If true, resets the sequence for identity columns. + * + * @returns A promise that resolves when the truncate operation is complete. + */ + async truncateTable(options: { + cascade?: boolean; + continueIdentity?: boolean; + only?: boolean; + restrict?: boolean; + restartIdentity?: boolean; + } = {}): Promise { + const { + cascade = false, + continueIdentity = false, + only = false, + restartIdentity = false, + restrict = false, + } = options; + const identityOption = restartIdentity ? "RESTART IDENTITY" : continueIdentity ? "CONTINUE IDENTITY" : ""; + const behaviorOption = cascade ? "CASCADE" : restrict ? "RESTRICT" : ""; + const truncateOptions = [identityOption, behaviorOption].filter(Boolean).join(" "); + + const query = `TRUNCATE ${only ? "ONLY " : ""}${this.tableName} ${truncateOptions};`; + + await this.#executeSql({ query, values: [] }); + } + + /** + * Retrieves an array of records from the database based on the provided search parameters. + * + * @param params - The search parameters used to filter records. + * @param params.$and - The conditions that must be met for a record to be included in the results. + * @param [params.$or] - Optional array of conditions where at least one must be met for a record to be included in the results. + * @param [selected=["*"]] - Optional array of fields to select from the records. If not specified, all fields are selected. + * @param [pagination] - Optional pagination options to limit and offset the results. + * @param [order] - Optional array of order options for sorting the results. + * @param order[].orderBy - The field by which to sort the results. + * @param order[].ordering - The sorting direction ("ASC" for ascending or "DESC" for descending). + * + * @returns A promise that resolves to an array of records matching the search parameters. + */ async getArrByParams( params: { $and: Types.TSearchParams; $or?: Types.TSearchParams[]; }, - selected = ["*"], + selected: string[] = ["*"], pagination?: SharedTypes.TPagination, order?: { orderBy: string; ordering: SharedTypes.TOrdering; }[], - ) { + ): Promise { const sql = this.compareQuery.getArrByParams(params, selected, pagination, order); const { rows } = await this.#executeSql(sql); return rows; } + /** + * Retrieves the count of records from the database based on the provided primary key values. + * + * @param pks - An array of primary key values for which to count the records. + * + * @returns A promise that resolves to the count of records with the specified primary keys. + */ async getCountByPks(pks: T[]): Promise { const sql = this.compareQuery.getCountByPks(pks); const { rows: [entity] } = await this.#executeSql(sql); @@ -390,42 +535,87 @@ export class BaseTable { return Number(entity?.count) || 0; } + /** + * Retrieves the count of records from the database based on the provided primary key values and search parameters. + * + * @param pks - An array of primary key values to filter the records. + * @param params - The search parameters to further filter the records. + * @param params.$and - The conditions that must be met for a record to be counted. + * @param [params.$or] - Optional array of conditions where at least one must be met for a record to be counted. + * + * @returns A promise that resolves to the count of records matching the primary keys and search parameters. + */ async getCountByPksAndParams( pks: T[], params: { $and: Types.TSearchParams; $or?: Types.TSearchParams[]; }, - ) { + ): Promise { const sql = this.compareQuery.getCountByPksAndParams(pks, params); const { rows: [entity] } = await this.#executeSql(sql); return Number(entity?.count) || 0; } - async getCountByParams(params: { $and: Types.TSearchParams; $or?: Types.TSearchParams[]; }) { + /** + * Retrieves the count of records from the database based on the provided search parameters. + * + * @param params - The search parameters to filter the records. + * @param params.$and - The conditions that must be met for a record to be counted. + * @param [params.$or] - Optional array of conditions where at least one must be met for a record to be counted. + * + * @returns A promise that resolves to the count of records matching the search parameters. + */ + async getCountByParams(params: { $and: Types.TSearchParams; $or?: Types.TSearchParams[]; }): Promise { const sql = this.compareQuery.getCountByParams(params); const { rows: [entity] } = await this.#executeSql(sql); return Number(entity?.count) || 0; } + /** + * Retrieves a single record from the database based on search parameters. + * + * @param params - The search parameters to filter the records. + * @param params.$and - The search conditions that must be met. + * @param [params.$or] - Optional array of search conditions where at least one must be met. + * @param[selected=["*"]] - The fields to select in the result. Defaults to selecting all fields. + * + * @returns A promise that resolves to the retrieved record or undefined if no record is found. + */ async getOneByParams( params: { $and: Types.TSearchParams; $or?: Types.TSearchParams[]; }, - selected = ["*"], - ) { + selected: string[] = ["*"], + ): Promise { const sql = this.compareQuery.getOneByParams(params, selected); const { rows: [entity] } = await this.#executeSql(sql); return entity; } - async getOneByPk(primaryKey: T) { + /** + * Retrieves a single record from the database based on the primary key. + * + * @param primaryKey - The value of the primary key to identify the record. + * + * @returns A promise that resolves to the retrieved record or undefined if no record is found. + */ + async getOneByPk(primaryKey: T): Promise { const sql = this.compareQuery.getOneByPk(primaryKey); - const { rows: [entity] } = await this.#executeSql(sql); + const { rows: [entity] } = await this.#executeSql(sql); return entity; } + /** + * Creates a single record in the database. + * + * @param [recordParams={}] - The parameters for the record to be created. + * @param [saveOptions] - The options for saving the record. + * @param [saveOptions.returningFields] - An array of field names to return after the record is created. + * + * @returns A promise that resolves to the created record or undefined if no record was created. + */ async createOne( - recordParams = {}, + recordParams: SharedTypes.TRawParams = {}, saveOptions?: { returningFields?: string[]; }, ): Promise { const sql = this.compareQuery.createOne(recordParams, saveOptions); @@ -434,6 +624,15 @@ export class BaseTable { return entity; } + /** + * Creates multiple records in the database. + * + * @param recordParams - An array of record parameters to insert. + * @param [saveOptions] - The options for saving records. + * @param [saveOptions.returningFields] - An array of field names to return after the records are created. + * + * @returns A promise that resolves to an array of created records. + */ async createMany( recordParams: SharedTypes.TRawParams[], saveOptions?: { returningFields?: string[]; }, @@ -444,31 +643,61 @@ export class BaseTable { return entities; } + /** + * Updates records based on search parameters. + * + * @param queryConditions - The query conditions for identifying records to update. + * @param queryConditions.$and - The mandatory conditions for the update. + * @param [queryConditions.$or] - The optional conditions for the update. + * @param [queryConditions.returningFields] - The fields to return after the update. + * @param [updateFields={}] - An object containing the fields to update. + * + * @returns A promise that resolves to the rows affected by the update. + */ async updateByParams( queryConditions: { $and: Types.TSearchParams; $or?: Types.TSearchParams[]; returningFields?: string[]; }, updateFields: SharedTypes.TRawParams = {}, - ) { + ): Promise { const sql = this.compareQuery.updateByParams(queryConditions, updateFields); const { rows } = await this.#executeSql(sql); return rows; } + /** + * Updates one record by primary key. + * + * @param primaryKeyValue - The value of the primary key to identify the record to be updated. + * @param [updateFields={}] - An object containing the fields to update. + * @param [updateOptions] - Options for the update operation. + * @param [updateOptions.returningFields] - An array of fields to return after the update. + * + * @returns A promise that resolves to the query result. + */ async updateOneByPk( primaryKeyValue: T, updateFields: SharedTypes.TRawParams = {}, updateOptions?: { returningFields?: string[]; }, - ) { + ): Promise { const sql = this.compareQuery.updateOneByPk(primaryKeyValue, updateFields, updateOptions); const { rows: [entity] } = await this.#executeSql(sql); return entity; } + /** + * Creates a query builder instance. + * + * @param [options] - The options for the query builder. + * @param [options.client] - The database client. + * @param [options.tableName] - The table name. + * + * @returns A new query builder instance. + */ queryBuilder(options?: { client?: pg.Pool | pg.PoolClient; tableName?: string; - }) { + }): QueryBuilder { const { client, tableName } = options || {}; return new QueryBuilder( @@ -479,22 +708,65 @@ export class BaseTable { } // STATIC METHODS + + /** + * Gets a standard pool for database connections. + * + * @param creds - The database credentials. + * @param [poolName] - The name of the pool. + * + * @returns The database connection pool. + */ static getStandardPool(creds: Types.TDBCreds, poolName?: string): pg.Pool { return connection.getStandardPool(creds, poolName); } + /** + * Removes a standard pool for database connections. + * + * @param creds - The database credentials. + * @param [poolName] - The name of the pool. + * + * @returns + */ static async removeStandardPool(creds: Types.TDBCreds, poolName?: string): Promise { return connection.removeStandardPool(creds, poolName); } + /** + * Gets a transaction pool for database connections. + * + * @param creds - The database credentials. + * @param [poolName] - The name of the pool. + * + * @returns The transaction connection pool. + */ static getTransactionPool(creds: Types.TDBCreds, poolName?: string): pg.Pool { return connection.getTransactionPool(creds, poolName); } + /** + * Removes a transaction pool for database connections. + * + * @param creds - The database credentials. + * @param [poolName] - The name of the pool. + * + * @returns + */ static async removeTransactionPool(creds: Types.TDBCreds, poolName?: string): Promise { return connection.removeTransactionPool(creds, poolName); } + /** + * Generates an SQL insert query and its corresponding values array based on the provided data. + * + * @param data - The data for generating the insert query. + * @param data.params - The parameters for the insert query. Can be a single object or an array of objects. + * @param [data.returning] - The fields to return after the insert operation. + * @param data.tableName - The name of the table to insert into. + * + * @returns An object containing the SQL insert query and its values. + */ static getInsertFields< P extends SharedTypes.TRawParams = SharedTypes.TRawParams, F extends string = string @@ -570,6 +842,22 @@ export class BaseTable { return { query, values: v }; } + /** + * Generates an SQL update query and its corresponding values array based on the provided data. + * + * @param data - The data for generating the update query. + * @param data.params - The parameters for the update query. + * @param data.primaryKey - The primary key field and its value. + * @param data.primaryKey.field - The primary key field name. + * @param data.primaryKey.value - The primary key value. + * @param [data.returning] - The fields to return after the update. + * @param data.tableName - The name of the table to update. + * @param [data.updateField] - The field to update with a timestamp. + * @param data.updateField.title - The field name to update. + * @param data.updateField.type - The type of the timestamp. + * + * @returns An object containing the SQL update query and its values. + */ static getUpdateFields< P extends SharedTypes.TRawParams = SharedTypes.TRawParams, F extends string = string diff --git a/src/lib/pg/query-builder/query-builder.ts b/src/lib/pg/query-builder/query-builder.ts index aedfbbc..e4852a1 100644 --- a/src/lib/pg/query-builder/query-builder.ts +++ b/src/lib/pg/query-builder/query-builder.ts @@ -354,7 +354,7 @@ export class QueryBuilder { */ where(data: { params?: ModelTypes.TSearchParams | DomainTypes.TSearchParams; - paramsOr?: DomainTypes.TArray2OrMore>; + paramsOr?: (ModelTypes.TSearchParams | DomainTypes.TSearchParams)[]; }): QueryBuilder { this.#queryHandler.where(data); @@ -418,13 +418,15 @@ export class QueryBuilder { /** * Specifies pagination for the SQL query. * - * @param data - The data for pagination. - * @param data.limit - The maximum number of rows to return. - * @param data.offset - The number of rows to skip before starting to return rows. + * @param [data] - The data for pagination. + * @param [data].limit - The maximum number of rows to return. + * @param [data].offset - The number of rows to skip before starting to return rows. * * @returns The current QueryBuilder instance for method chaining. */ - pagination(data: { limit: number; offset: number; }): QueryBuilder { + pagination(data?: { limit: number; offset: number; }): QueryBuilder { + if (!data) return this; + this.#queryHandler.pagination(data); return this; @@ -433,16 +435,18 @@ export class QueryBuilder { /** * Specifies an `ORDER BY` clause for the SQL query. * - * @param data - An array of objects specifying the columns and sorting order. - * @param data[].column - The column name to order by. - * @param data[].sorting - The sorting direction (`ASC` or `DESC`). + * @param [data] - An array of objects specifying the columns and sorting order. + * @param [data][].column - The column name to order by. + * @param [data][].sorting - The sorting direction (`ASC` or `DESC`). * * @returns The current QueryBuilder instance for method chaining. */ - orderBy(data: { + orderBy(data?: { column: string; sorting: SharedTypes.TOrdering; }[]): QueryBuilder { + if (!data?.length) return this; + this.#queryHandler.orderBy(data); return this; @@ -451,11 +455,13 @@ export class QueryBuilder { /** * Specifies a `GROUP BY` clause for the SQL query. * - * @param data - An array of column names to group by. + * @param [data] - An array of column names to group by. * * @returns The current QueryBuilder instance for method chaining. */ - groupBy(data: string[]): QueryBuilder { + groupBy(data?: string[]): QueryBuilder { + if (!data?.length) return this; + this.#queryHandler.groupBy(data); return this; @@ -472,7 +478,7 @@ export class QueryBuilder { */ having(data: { params?: ModelTypes.TSearchParams | DomainTypes.TSearchParams; - paramsOr?: DomainTypes.TArray2OrMore>; + paramsOr?: (ModelTypes.TSearchParams | DomainTypes.TSearchParams)[]; }): QueryBuilder { this.#queryHandler.having(data); @@ -518,4 +524,20 @@ export class QueryBuilder { return (await this.#executeSql(sql)).rows; } + + /** + * Executes a SQL custom query with specified data and values, and returns the result. + * + * This method executes a SQL query provided as a string with optional parameter values, and returns the result rows. + * + * @note All previously passed options are ignored. + * + * @param data - The SQL query string to execute. + * @param [values=[]] - Optional array of values to be used in the query. + * + * @returns A promise that resolves to an array of result rows. + */ + async executeRawQuery(data: string, values?: unknown[]): Promise { + return (await this.#executeSql({ query: data, values: values || [] })).rows; + } } diff --git a/src/lib/pg/query-builder/query-handler.ts b/src/lib/pg/query-builder/query-handler.ts index dbc23a6..a33a6c1 100644 --- a/src/lib/pg/query-builder/query-handler.ts +++ b/src/lib/pg/query-builder/query-handler.ts @@ -1,4 +1,3 @@ -import * as DomainTypes from "../domain/types.js"; import * as Helpers from "../helpers/index.js"; import * as ModelTypes from "../model/types.js"; import * as SharedHelpers from "../../../shared-helpers/index.js"; @@ -695,7 +694,7 @@ export class QueryHandler { */ where(data: { params?: ModelTypes.TSearchParams; - paramsOr?: DomainTypes.TArray2OrMore; + paramsOr?: ModelTypes.TSearchParams[]; }): void { const { queryArray, queryOrArray, values } = Helpers.compareFields( data.params as ModelTypes.TSearchParams, @@ -903,7 +902,7 @@ export class QueryHandler { */ having(data: { params?: ModelTypes.TSearchParams; - paramsOr?: DomainTypes.TArray2OrMore; + paramsOr?: ModelTypes.TSearchParams[]; }): void { const { queryArray, queryOrArray, values } = Helpers.compareFields( data.params as ModelTypes.TSearchParams, diff --git a/src/test/PG-01/index.ts b/src/test/PG-01/index.ts index e600269..002c6cb 100644 --- a/src/test/PG-01/index.ts +++ b/src/test/PG-01/index.ts @@ -536,7 +536,7 @@ export const start = async (creds: PG.ModelTypes.TDBCreds) => { { const params: { params: PG.DomainTypes.TSearchParams; - paramsOr: PG.DomainTypes.TArray2OrMore>; + paramsOr: PG.DomainTypes.TSearchParams[]; } = { params: { number_key: { $in: [1, 2] } }, paramsOr: [{ number_key: 1 }, { number_key: 2 }], @@ -556,7 +556,7 @@ export const start = async (creds: PG.ModelTypes.TDBCreds) => { const params: { pagination: Types.TPagination; params: PG.DomainTypes.TSearchParams; - paramsOr: PG.DomainTypes.TArray2OrMore>; + paramsOr: PG.DomainTypes.TSearchParams[]; } = { pagination: { limit: 1, offset: 1 }, params: { number_key: { $in: [1, 2] } }, @@ -998,14 +998,15 @@ export const start = async (creds: PG.ModelTypes.TDBCreds) => { ); await testContext.test( - "dropTable", + "drop tables", async () => { - const pool = PG.BaseModel.getStandardPool(creds); - - await pool.query(`DROP TABLE IF EXISTS ${testTable.tableName};`); + await testTable.dropTable({ cascade: true }); }, ); - await testContext.test("PG.connection shutdown", async () => await PG.connection.shutdown()); + await testContext.test( + "PG.connection shutdown", + async () => { await PG.connection.shutdown(); }, + ); }); }; diff --git a/src/test/PG-02/index.ts b/src/test/PG-02/index.ts index a7287a5..028ed94 100644 --- a/src/test/PG-02/index.ts +++ b/src/test/PG-02/index.ts @@ -103,6 +103,9 @@ export const start = async (creds: PG.ModelTypes.TDBCreds) => { }, ); - await testContext.test("PG.connection shutdown", async () => await PG.connection.shutdown()); + await testContext.test( + "PG.connection shutdown", + async () => { await PG.connection.shutdown(); }, + ); }); }; diff --git a/src/test/PG-03/index.ts b/src/test/PG-03/index.ts index f8b434e..ee7e7d3 100644 --- a/src/test/PG-03/index.ts +++ b/src/test/PG-03/index.ts @@ -7,8 +7,8 @@ import * as UserRoleTable from "./user-role/index.js"; import * as UserTable from "./user/index.js"; export const start = async (creds: PG.ModelTypes.TDBCreds) => { - const User = new UserTable.Domain(creds); - const UserRole = new UserRoleTable.Domain(creds); + const User = UserTable.domain(creds); + const UserRole = UserRoleTable.domain(creds); return test("PG-03", async (testContext) => { await testContext.test( @@ -109,8 +109,8 @@ export const start = async (creds: PG.ModelTypes.TDBCreds) => { "read admins getList", async () => { const users = await User.getList({ - order: [{ orderBy: "u.first_name", ordering: "ASC" }], - params: { "ur.title": "admin" }, + order: [{ column: "users.first_name", sorting: "ASC" }], + params: { "user_roles.title": "admin" }, }); assert.equal(users.length, 1); @@ -118,7 +118,7 @@ export const start = async (creds: PG.ModelTypes.TDBCreds) => { const firstUser = users.at(0); assert.equal(firstUser?.first_name, "Robin"); - assert.equal(firstUser?.ur_title, "admin"); + assert.equal(firstUser?.user_role_title, "admin"); }, ); } @@ -128,8 +128,8 @@ export const start = async (creds: PG.ModelTypes.TDBCreds) => { "read heads getList", async () => { const users = await User.getList({ - order: [{ orderBy: "u.first_name", ordering: "ASC" }], - params: { "ur.title": "head" }, + order: [{ column: "users.first_name", sorting: "ASC" }], + params: { "user_roles.title": "head" }, }); assert.equal(users.length, 1); @@ -137,7 +137,7 @@ export const start = async (creds: PG.ModelTypes.TDBCreds) => { const firstUser = users.at(0); assert.equal(firstUser?.first_name, "Bob"); - assert.equal(firstUser?.ur_title, "head"); + assert.equal(firstUser?.user_role_title, "head"); }, ); } @@ -147,8 +147,8 @@ export const start = async (creds: PG.ModelTypes.TDBCreds) => { "read users getList", async () => { const users = await User.getList({ - order: [{ orderBy: "u.first_name", ordering: "ASC" }], - params: { "ur.title": "user" }, + order: [{ column: "users.first_name", sorting: "ASC" }], + params: { "user_roles.title": "user" }, }); assert.equal(users.length, 5); @@ -156,12 +156,12 @@ export const start = async (creds: PG.ModelTypes.TDBCreds) => { const firstUser = users.at(0); assert.equal(firstUser?.first_name, "Ann"); - assert.equal(firstUser?.ur_title, "user"); + assert.equal(firstUser?.user_role_title, "user"); const lastUser = users.at(-1); assert.equal(lastUser?.first_name, "Peter"); - assert.equal(lastUser?.ur_title, "user"); + assert.equal(lastUser?.user_role_title, "user"); }, ); } @@ -231,8 +231,8 @@ export const start = async (creds: PG.ModelTypes.TDBCreds) => { "read admins getList", async () => { const users = await User.getList({ - order: [{ orderBy: "u.first_name", ordering: "ASC" }], - params: { "ur.title": "admin" }, + order: [{ column: "users.first_name", sorting: "ASC" }], + params: { "user_roles.title": "admin" }, }); assert.equal(users.length, 1); @@ -240,7 +240,7 @@ export const start = async (creds: PG.ModelTypes.TDBCreds) => { const firstUser = users.at(0); assert.equal(firstUser?.first_name, "Robin"); - assert.equal(firstUser?.ur_title, "admin"); + assert.equal(firstUser?.user_role_title, "admin"); }, ); } @@ -501,13 +501,14 @@ export const start = async (creds: PG.ModelTypes.TDBCreds) => { await testContext.test( "drop tables", async () => { - const pool = PG.BaseModel.getStandardPool(creds); - - await pool.query(`DROP TABLE IF EXISTS ${User.tableName};`); - await pool.query(`DROP TABLE IF EXISTS ${UserRole.tableName};`); + await User.dropTable({ cascade: true }); + await UserRole.dropTable({ cascade: true }); }, ); - await testContext.test("PG.connection shutdown", async () => await PG.connection.shutdown()); + await testContext.test( + "PG.connection shutdown", + async () => { await PG.connection.shutdown(); }, + ); }); }; diff --git a/src/test/PG-03/user-role/domain.ts b/src/test/PG-03/user-role/domain.ts index fb27b22..0ee2210 100644 --- a/src/test/PG-03/user-role/domain.ts +++ b/src/test/PG-03/user-role/domain.ts @@ -1,16 +1,12 @@ import { PG } from "../../../index.js"; -import { Model, Types } from "./model/index.js"; +import * as Types from "./types.js"; -export class Domain extends PG.BaseDomain<{ - Model: Model; - CreateFields: Types.CreateFields; - SearchFields: Types.SearchFields; - TableFields: Types.TableFields; - UpdateFields: Types.UpdateFields; -}> { +import { model } from "./model.js"; - constructor(creds: PG.ModelTypes.TDBCreds) { - super({ model: new Model(creds) }); - } -} +class Domain extends PG.Domain.BaseTable, { + CoreFields: Types.TableFields; +}> { } + +export const domain = (creds: PG.ModelTypes.TDBCreds) => + new Domain({ model: model(creds) }); diff --git a/src/test/PG-03/user-role/index.ts b/src/test/PG-03/user-role/index.ts index 59aca4b..2e19d95 100644 --- a/src/test/PG-03/user-role/index.ts +++ b/src/test/PG-03/user-role/index.ts @@ -1,2 +1,2 @@ export * from "./domain.js"; -export * from "./model/index.js"; +export * as Types from "./types.js"; diff --git a/src/test/PG-03/user-role/model.ts b/src/test/PG-03/user-role/model.ts new file mode 100644 index 0000000..5a52e67 --- /dev/null +++ b/src/test/PG-03/user-role/model.ts @@ -0,0 +1,19 @@ +import { PG } from "../../../index.js"; + +export const model = (creds: PG.ModelTypes.TDBCreds) => new PG.Model.BaseTable( + { + createField: { title: "created_at", type: "unix_timestamp" }, + primaryKey: "id", + tableFields: [ + "id", + + "title", + + "created_at", + "updated_at", + ] as const, + tableName: "user_roles", + updateField: { title: "updated_at", type: "unix_timestamp" }, + }, + creds, +); diff --git a/src/test/PG-03/user-role/model/index.ts b/src/test/PG-03/user-role/model/index.ts deleted file mode 100644 index 4f1c1f2..0000000 --- a/src/test/PG-03/user-role/model/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./model.js"; -export * as Types from "./types.js"; diff --git a/src/test/PG-03/user-role/model/model.ts b/src/test/PG-03/user-role/model/model.ts deleted file mode 100644 index 51a4dce..0000000 --- a/src/test/PG-03/user-role/model/model.ts +++ /dev/null @@ -1,38 +0,0 @@ -// ----- Dependencies -------------------------- -import { PG } from "../../../../index.js"; - -import { TableKeys } from "./types.js"; - -// ----- Class ------------------------------ -export class Model extends PG.BaseModel { - constructor(creds: PG.ModelTypes.TDBCreds, options?: PG.ModelTypes.TDBOptions) { - super( - { - createField, - primaryKey, - tableFields, - tableName, - updateField, - }, - creds, - options, - ); - } -} - -// ----- Table properties ---------------------- -const tableName = "user_roles"; // table from DB -const primaryKey = "id"; // primaryId from table -const createField = { title: "created_at", type: "timestamp" } as const; -const updateField = { title: "updated_at", type: "timestamp" } as const; -const tableFields: TableKeys[] = [ - "created_at", - "id", - "title", - "updated_at", -]; - -// ----- queries ----------------------- -// const queries = { - -// }; diff --git a/src/test/PG-03/user-role/model/types.ts b/src/test/PG-03/user-role/types.ts similarity index 66% rename from src/test/PG-03/user-role/model/types.ts rename to src/test/PG-03/user-role/types.ts index 2c1ec33..7e79f39 100644 --- a/src/test/PG-03/user-role/model/types.ts +++ b/src/test/PG-03/user-role/types.ts @@ -2,13 +2,7 @@ export type CreateFields = Pick; -export type SearchFields = Partial> & { - created_at?: string; - updated_at?: string | null; -}; +export type SearchFields = Partial; export type TableFields = { id: string; diff --git a/src/test/PG-03/user/domain.ts b/src/test/PG-03/user/domain.ts index 5d31454..ed97580 100644 --- a/src/test/PG-03/user/domain.ts +++ b/src/test/PG-03/user/domain.ts @@ -1,27 +1,66 @@ -import * as DbManager from "../../../index.js"; +import { Types as CoreTypes, PG } from "../../../index.js"; -import { Model, Types } from "./model/index.js"; +import * as Types from "./types.js"; -export class Domain extends DbManager.PG.BaseDomain<{ - Model: Model; - CreateFields: Types.CreateFields; - SearchFields: Types.SearchFields; - TableFields: Types.TableFields; - UpdateFields: Types.UpdateFields; -}> { +import { model } from "./model.js"; - constructor(creds: DbManager.PG.ModelTypes.TDBCreds) { - super({ model: new Model(creds) }); +class Domain extends PG.Domain.BaseTable, { + CoreFields: Types.TableFields; +}> { + #normalizeListParams(params: Types.SearchFieldsList): Types.SearchFieldsList { + return { + "user_roles.title": params["user_roles.title"], + "users.is_deleted": typeof params["users.is_deleted"] === "boolean" + ? params["users.is_deleted"] + : false, + }; } async getList( data: { - order?: { orderBy: "u.first_name"; ordering: DbManager.Types.TOrdering; }[]; - pagination?: DbManager.Types.TPagination; + order?: { column: "users.first_name"; sorting: CoreTypes.TOrdering; }[]; + pagination?: CoreTypes.TPagination; params: Types.SearchFieldsList; paramsOr?: Types.SearchFieldsListOr; }, ): Promise { - return this.model.getList(data); + const { + order, + pagination, + params, + paramsOr, + } = data; + + const paramsResult = this.#normalizeListParams(params); + const paramsOrResult = paramsOr + ? paramsOr.map((e) => this.#normalizeListParams(e)) + : undefined; + + return this.model.queryBuilder() + .select([ + "users.id AS id", + + "users.first_name AS first_name", + "users.last_name AS last_name", + "users.is_deleted AS is_deleted", + + "user_roles.id AS user_role_id", + "user_roles.title AS user_role_title", + ]) + .innerJoin({ + initialField: "id_user_role", + targetField: "id", + targetTableName: "user_roles", + }) + .where({ + params: paramsResult, + paramsOr: paramsOrResult, + }) + .pagination(pagination) + .orderBy(order) + .execute(); } } + +export const domain = (creds: PG.ModelTypes.TDBCreds) => + new Domain({ model: model(creds) }); diff --git a/src/test/PG-03/user/index.ts b/src/test/PG-03/user/index.ts index 59aca4b..2e19d95 100644 --- a/src/test/PG-03/user/index.ts +++ b/src/test/PG-03/user/index.ts @@ -1,2 +1,2 @@ export * from "./domain.js"; -export * from "./model/index.js"; +export * as Types from "./types.js"; diff --git a/src/test/PG-03/user/model.ts b/src/test/PG-03/user/model.ts new file mode 100644 index 0000000..e1e34ee --- /dev/null +++ b/src/test/PG-03/user/model.ts @@ -0,0 +1,23 @@ +import { PG } from "../../../index.js"; + +export const model = (creds: PG.ModelTypes.TDBCreds) => new PG.Model.BaseTable( + { + createField: { title: "created_at", type: "unix_timestamp" }, + primaryKey: "id", + tableFields: [ + "id", + + "id_user_role", + + "first_name", + "is_deleted", + "last_name", + + "created_at", + "updated_at", + ] as const, + tableName: "users", + updateField: { title: "updated_at", type: "unix_timestamp" }, + }, + creds, +); diff --git a/src/test/PG-03/user/model/index.ts b/src/test/PG-03/user/model/index.ts deleted file mode 100644 index 4f1c1f2..0000000 --- a/src/test/PG-03/user/model/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./model.js"; -export * as Types from "./types.js"; diff --git a/src/test/PG-03/user/model/model.ts b/src/test/PG-03/user/model/model.ts deleted file mode 100644 index cc16fd2..0000000 --- a/src/test/PG-03/user/model/model.ts +++ /dev/null @@ -1,82 +0,0 @@ -// ----- Dependencies -------------------------- -import { PG, Types } from "../../../../index.js"; - -import { TableKeys } from "./types.js"; - -// ----- Class ------------------------------ -export class Model extends PG.BaseModel { - constructor(creds: PG.ModelTypes.TDBCreds, options?: PG.ModelTypes.TDBOptions) { - super( - { - createField, - primaryKey, - tableFields, - tableName, - updateField, - }, - creds, - options, - ); - } - - async getList({ order, pagination, params = {} }: { - order?: { orderBy: string; ordering: Types.TOrdering; }[]; - pagination?: { limit: number; offset: number; }; - params?: PG.ModelTypes.TSearchParams; - paramsOr?: PG.ModelTypes.TSearchParams[]; - }) { - const { queryArray, values } = this.compareFields(params); - const selected = [ - "u.first_name AS first_name", - "u.last_name AS last_name", - "u.id AS id", - "u.is_deleted AS is_deleted", - "ur.id AS ur_id", - "ur.title AS ur_title", - ]; - - const data = this.getFieldsToSearch( - { queryArray }, - selected, - pagination, - order, - ); - - return (await this.pool.query(queries.getList(data), values)).rows; - } - -} - -// ----- Table properties ---------------------- -const tableName = "users"; // table from DB -const primaryKey = "id"; // primaryId from table -const createField = { title: "created_at", type: "unix_timestamp" } as const; -const updateField = { title: "updated_at", type: "unix_timestamp" } as const; -const tableFields: TableKeys[] = [ - "created_at", - "first_name", - "id", - "id_user_role", - "is_deleted", - "last_name", - "updated_at", -]; - -// ----- queries ----------------------- -const queries = { - getList(payload: { - orderByFields: string; - paginationFields: string; - searchFields: string; - selectedFields: string; - }) { - return ` - SELECT ${payload.selectedFields} - FROM ${tableName} u - JOIN user_roles ur ON ur.id = u.id_user_role - ${payload.searchFields} - ${payload.orderByFields} - ${payload.paginationFields} - `; - }, -}; diff --git a/src/test/PG-03/user/model/types.ts b/src/test/PG-03/user/types.ts similarity index 59% rename from src/test/PG-03/user/model/types.ts rename to src/test/PG-03/user/types.ts index 6ea3dc1..dc869fe 100644 --- a/src/test/PG-03/user/model/types.ts +++ b/src/test/PG-03/user/types.ts @@ -1,4 +1,4 @@ -import * as DbManager from "../../../../index.js"; +import { PG } from "../../../index.js"; export type CreateFields = Pick; - last_name: NonNullable; + first_name: TableFields["first_name"]; + last_name: TableFields["last_name"]; + is_deleted: TableFields["is_deleted"]; - ur_id: string; - ur_title: string; + user_role_id: string; + user_role_title: string; }; export type SearchFields = Partial; -export type SearchFieldsList = DbManager.PG.DomainTypes.TSearchParams; -export type SearchFieldsListOr = DbManager.PG.DomainTypes.TArray2OrMore; +export type SearchFieldsList = PG.DomainTypes.TSearchParamsStrict; +export type SearchFieldsListOr = SearchFieldsList[] | undefined; export type SearchListFields = { - "u.is_deleted"?: boolean; - "ur.title"?: string; + "users.is_deleted"?: boolean; + "user_roles.title"?: string; }; export type TableFields = { diff --git a/src/test/PG-04/index.ts b/src/test/PG-04/index.ts index acdd049..8f433bf 100644 --- a/src/test/PG-04/index.ts +++ b/src/test/PG-04/index.ts @@ -322,6 +322,9 @@ export const start = async (creds: PG.ModelTypes.TDBCreds) => { }, ); - await testContext.test("PG.connection shutdown", async () => await PG.connection.shutdown()); + await testContext.test( + "PG.connection shutdown", + async () => { await PG.connection.shutdown(); }, + ); }); }; diff --git a/src/test/PG-04/user/model/types.ts b/src/test/PG-04/user/model/types.ts index b327d6c..043b1d0 100644 --- a/src/test/PG-04/user/model/types.ts +++ b/src/test/PG-04/user/model/types.ts @@ -21,7 +21,7 @@ export type ListedEntity = { export type SearchFields = Partial; export type SearchFieldsList = DbManager.PG.DomainTypes.TSearchParams; -export type SearchFieldsListOr = DbManager.PG.DomainTypes.TArray2OrMore; +export type SearchFieldsListOr = SearchFieldsList[] | undefined; export type SearchListFields = { "u.id"?: TableFields["id"]; diff --git a/src/test/PG-05/file-system/domain.ts b/src/test/PG-05/file-system/domain.ts index ba9c47f..e38ff1a 100644 --- a/src/test/PG-05/file-system/domain.ts +++ b/src/test/PG-05/file-system/domain.ts @@ -14,35 +14,37 @@ export class Domain extends DbManager.PG.Domain.BaseTable(); + .select([ + "name", + "path", + ]) + .execute(); } - async getAllWithLevel() { + async getAllInsideHomePath() { return this.model.queryBuilder() .select([ "name", "path", - "NLEVEL(path) as level", ]) - .execute(); + .where({ params: { path: { "$<@": "root.home" } } }) + .execute(); } - async getAllInsideHomePath() { + async getAllOutsideHomePath() { return this.model.queryBuilder() .select(["name", "path"]) - .where({ - params: { path: { "$<@": "root.home" } }, - }) - .execute(); + .where({ params: { path: { "$@>": "root.home" } } }) + .execute(); } - async getAllOutsideHomePath() { + async getAllWithLevel() { return this.model.queryBuilder() - .select(["name", "path"]) - .where({ - params: { path: { "$@>": "root.home" } }, - }) - .execute(); + .select([ + "name", + "path", + "NLEVEL(path) as level", + ]) + .execute(); } } diff --git a/src/test/PG-05/file-system/model/types.ts b/src/test/PG-05/file-system/model/types.ts index d640d33..fad8d63 100644 --- a/src/test/PG-05/file-system/model/types.ts +++ b/src/test/PG-05/file-system/model/types.ts @@ -4,6 +4,11 @@ export type CreateFields = Pick; +export type GetAll = Pick; +export type GetAllInsideHomePath = Pick; +export type GetAllOutsideHomePath = Pick; +export type GetAllWithLevel = Pick & { level: number; }; + export type TableFields = { id: string; diff --git a/src/test/PG-05/index.ts b/src/test/PG-05/index.ts index 463780b..57e9e9b 100644 --- a/src/test/PG-05/index.ts +++ b/src/test/PG-05/index.ts @@ -109,6 +109,9 @@ export const start = async (creds: PG.ModelTypes.TDBCreds) => { }, ); - await testContext.test("PG.connection shutdown", async () => await PG.connection.shutdown()); + await testContext.test( + "PG.connection shutdown", + async () => { await PG.connection.shutdown(); }, + ); }); }; diff --git a/src/test/PG-06/index.ts b/src/test/PG-06/index.ts index 21be319..df61b75 100644 --- a/src/test/PG-06/index.ts +++ b/src/test/PG-06/index.ts @@ -297,14 +297,15 @@ export const start = async (creds: PG.ModelTypes.TDBCreds) => { await testContext.test( "dropTable", async () => { - const pool = PG.BaseModel.getStandardPool(creds); - - await pool.query(`DROP TABLE IF EXISTS ${admin.tableName};`); - await pool.query(`DROP TABLE IF EXISTS ${user.tableName};`); - await pool.query(`DROP TABLE IF EXISTS ${userTest.tableName};`); + await admin.dropTable({ cascade: true }); + await user.dropTable({ cascade: true }); + await userTest.dropTable({ cascade: true }); }, ); - await testContext.test("PG.connection shutdown", async () => await PG.connection.shutdown()); + await testContext.test( + "PG.connection shutdown", + async () => { await PG.connection.shutdown(); }, + ); }); };