Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@athenna/database

Package Overview
Dependencies
Maintainers
1
Versions
205
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@athenna/database - npm Package Compare versions

Comparing version
5.36.0
to
5.37.0
+18
configurer/docker/mongo/service.d.ts
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare const _default: {
container_name: string;
image: string;
ports: string[];
environment: {
MONGO_INITDB_ROOT_USERNAME: string;
MONGO_INITDB_ROOT_PASSWORD: string;
};
};
export default _default;
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export default {
container_name: 'athenna_mongo',
image: 'mongo',
ports: ['27017:27017'],
environment: {
MONGO_INITDB_ROOT_USERNAME: 'root',
MONGO_INITDB_ROOT_PASSWORD: 'root'
}
};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare const _default: {
container_name: string;
image: string;
ports: string[];
environment: {
MYSQL_DATABASE: string;
MYSQL_ROOT_PASSWORD: string;
MYSQL_ALLOW_EMPTY_PASSWORD: string;
};
};
export default _default;
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export default {
container_name: 'athenna_mysql',
image: 'mysql',
ports: ['3306:3306'],
environment: {
MYSQL_DATABASE: 'athenna',
MYSQL_ROOT_PASSWORD: 'root',
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
}
};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare const _default: {
container_name: string;
image: string;
ports: string[];
environment: {
POSTGRES_DB: string;
POSTGRES_USER: string;
POSTGRES_PASSWORD: string;
POSTGRES_ROOT_PASSWORD: string;
};
};
export default _default;
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export default {
container_name: 'athenna_postgres',
image: 'postgres',
ports: ['5432:5432'],
environment: {
POSTGRES_DB: 'athenna',
POSTGRES_USER: 'root',
POSTGRES_PASSWORD: 'root',
POSTGRES_ROOT_PASSWORD: 'root'
}
};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { BaseConfigurer } from '@athenna/artisan';
export default class DatabaseConfigurer extends BaseConfigurer {
configure(): Promise<void>;
private databasePath;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { relative } from 'node:path';
import { BaseConfigurer } from '@athenna/artisan';
import { File, Module, Parser, Path } from '@athenna/common';
export default class DatabaseConfigurer extends BaseConfigurer {
async configure() {
const connection = await this.prompt.list('What will be your default connection?', ['mysql', 'postgres', 'sqlite', 'mongo']);
const ext = Path.ext();
const task = this.logger.task();
task.addPromise(`Create database.${ext} configuration file`, () => {
return new File(`./database`).copy(Path.config(`database.${ext}`));
});
task.addPromise('Update commands of .athennarc.json', () => {
return this.rc
.setTo('commands', 'make:model', '@athenna/database/commands/MakeModelCommand')
.setTo('commands', 'make:seeder', '@athenna/database/commands/MakeSeederCommand')
.setTo('commands', 'make:migration', '@athenna/database/commands/MakeMigrationCommand')
.setTo('commands', 'make:crud', {
path: '@athenna/database/commands/MakeCrudCommand',
fileCase: 'toDotCase'
})
.setTo('commands', 'db:fresh', {
path: '@athenna/database/commands/DbFreshCommand',
loadApp: true,
loadAllCommands: true
})
.setTo('commands', 'db:seed', {
path: '@athenna/database/commands/DbSeedCommand',
loadApp: true
})
.setTo('commands', 'db:wipe', {
path: '@athenna/database/commands/DbWipeCommand',
loadApp: true
})
.setTo('commands', 'migration:run', {
path: '@athenna/database/commands/MigrationRunCommand',
loadApp: true
})
.setTo('commands', 'migration:revert', {
path: '@athenna/database/commands/MigrationRevertCommand',
loadApp: true
})
.save();
});
task.addPromise('Update templates of .athennarc.json', () => {
return this.rc
.setTo('templates', 'model', 'node_modules/@athenna/database/templates/model.edge')
.setTo('templates', 'seeder', 'node_modules/@athenna/database/templates/seeder.edge')
.setTo('templates', 'migration', 'node_modules/@athenna/database/templates/migration.edge')
.setTo('templates', 'crud-model', 'node_modules/@athenna/database/templates/crud-model.edge')
.setTo('templates', 'crud-migration', 'node_modules/@athenna/database/templates/crud-migration.edge')
.setTo('templates', 'crud-service', 'node_modules/@athenna/database/templates/crud-service.edge')
.setTo('templates', 'crud-controller', 'node_modules/@athenna/database/templates/crud-controller.edge')
.setTo('templates', 'crud-service-test', 'node_modules/@athenna/database/templates/crud-service-test.edge')
.setTo('templates', 'crud-controller-test', 'node_modules/@athenna/database/templates/crud-controller-test.edge')
.save();
});
task.addPromise('Update providers of .athennarc.json', () => {
return this.rc
.pushTo('providers', '@athenna/database/providers/DatabaseProvider')
.save();
});
task.addPromise('Update .env, .env.test and .env.example', () => {
let envs = '';
switch (connection) {
case 'mongo':
envs =
'\nDB_CONNECTION=mongo\n' +
'DB_DEBUG=false\n' +
'DB_URL=mongodb://root:root@localhost:27017/admin\n';
break;
case 'sqlite':
envs =
'\nDB_CONNECTION=sqlite\n' +
'DB_DEBUG=false\n' +
`DB_FILENAME=${this.databasePath()}/sqlite.db\n`;
break;
default:
// eslint-disable-next-line no-case-declarations
const ports = {
mysql: 3306,
postgres: 5432
};
envs =
`\nDB_CONNECTION=${connection}\n` +
'DB_HOST=localhost\n' +
`DB_PORT=${ports[connection]}\n` +
'DB_DEBUG=false\n' +
'DB_USERNAME=root\n' +
'DB_PASSWORD=root\n' +
'DB_DATABASE=athenna\n';
}
const testEnvs = envs.replace(`DB_CONNECTION=${connection}`, 'DB_CONNECTION=fake');
return new File(Path.pwd('.env'), '')
.append(envs)
.then(() => new File(Path.pwd('.env.test'), '').append(testEnvs))
.then(() => new File(Path.pwd('.env.example'), '').append(envs));
});
if (connection !== 'sqlite') {
task.addPromise('Add service to docker-compose.yml file', async () => {
const hasDockerCompose = await File.exists(Path.pwd('docker-compose.yml'));
if (hasDockerCompose) {
const docker = await new File(Path.pwd('docker-compose.yml')).getContentAsYaml();
docker.services[connection] = await Module.get(import(`./docker/${connection}/service.ts`));
return new File(Path.pwd('docker-compose.yml')).setContent(Parser.objectToYamlString(docker));
}
return new File(`./docker/${connection}/file.yml`).copy(Path.pwd('docker-compose.yml'));
});
}
const libraries = {
mysql: ['knex', 'mysql2'],
postgres: ['knex', 'pg'],
sqlite: ['knex', 'better-sqlite3'],
mongo: ['mongoose']
};
task.addPromise(`Install ${libraries[connection].join(', ')} libraries`, () => {
return this.npm.install(libraries[connection]);
});
await task.run();
console.log();
this.logger.success('Successfully configured ({dim,yellow} @athenna/database) library');
if (connection !== 'sqlite') {
this.logger
.instruction()
.head('Run following commands to get started:')
.add(`docker-compose up -d`)
.render();
}
}
databasePath() {
return relative(Path.pwd(), Path.database()).replace(/\\/g, '/');
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { BaseCommand } from '@athenna/artisan';
export declare class DbWipeCommand extends BaseCommand {
connection: string;
withSeeders: boolean;
static signature(): string;
static description(): string;
handle(): Promise<void>;
private getConfig;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { Artisan, BaseCommand, Option } from '@athenna/artisan';
export class DbWipeCommand extends BaseCommand {
static signature() {
return 'db:fresh';
}
static description() {
return 'Drop all the tables of your database and run migrations again.';
}
async handle() {
await Artisan.call(`db:wipe --connection ${this.connection}`);
console.log();
if (this.getConfig('driver') !== 'mongo') {
await Artisan.call(`migration:run --connection ${this.connection}`);
}
if (this.withSeeders) {
console.log();
await Artisan.call(`db:seed --connection ${this.connection}`);
}
}
getConfig(name, defaultValue) {
return Config.get(`database.connections.${this.connection === 'default'
? Config.get('database.default')
: this.connection}.${name}`, defaultValue);
}
}
__decorate([
Option({
default: 'default',
signature: '-c, --connection <connection>',
description: 'Set the the database connection.'
}),
__metadata("design:type", String)
], DbWipeCommand.prototype, "connection", void 0);
__decorate([
Option({
default: false,
signature: '--with-seeders',
description: 'Run seeders at the end.'
}),
__metadata("design:type", Boolean)
], DbWipeCommand.prototype, "withSeeders", void 0);
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { BaseCommand } from '@athenna/artisan';
export declare class DbSeedCommand extends BaseCommand {
classes: string[];
connection: string;
static signature(): string;
static description(): string;
handle(): Promise<void>;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { Database } from '#src/facades/Database';
import { BaseCommand, Option } from '@athenna/artisan';
export class DbSeedCommand extends BaseCommand {
static signature() {
return 'db:seed';
}
static description() {
return 'Run your application seeders.';
}
async handle() {
this.logger.simple('({bold,green} [ SEEDING DATABASE ])\n');
const task = this.logger.task();
const DB = Database.connection(this.connection);
await DB.runSeeders({ task, classes: this.classes });
await task
.run()
.then(async () => {
const dbName = await DB.getCurrentDatabase();
console.log();
this.logger.success(`Database ({yellow} "${dbName}") successfully seeded.`);
})
.finally(() => DB.close());
}
}
__decorate([
Option({
default: [],
signature: '--classes <classes...>',
description: 'Specify the classes names that should run.'
}),
__metadata("design:type", Array)
], DbSeedCommand.prototype, "classes", void 0);
__decorate([
Option({
default: 'default',
signature: '-c, --connection <connection>',
description: 'Set the the database connection.'
}),
__metadata("design:type", String)
], DbSeedCommand.prototype, "connection", void 0);
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { BaseCommand } from '@athenna/artisan';
export declare class DbWipeCommand extends BaseCommand {
connection: string;
static signature(): string;
static description(): string;
handle(): Promise<void>;
private getConfig;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { Database } from '#src/facades/Database';
import { BaseCommand, Option } from '@athenna/artisan';
export class DbWipeCommand extends BaseCommand {
static signature() {
return 'db:wipe';
}
static description() {
return 'Drop all the tables of your database.';
}
async handle() {
this.logger.simple('({bold,green} [ WIPING DATABASE ])\n');
const task = this.logger.task();
const DB = Database.connection(this.connection);
if (this.getConfig('driver') === 'mongo') {
task.addPromise('Dropping all database tables', async () => {
const tables = await DB.getTables();
return tables.athenna.concurrently(table => DB.dropTable(table));
});
}
else {
const migrationsTable = this.getConfig('migrations.tableName', 'migrations');
task.addPromise('Reverting migrations', () => DB.revertMigrations());
task.addPromise('Drop migrations table', () => DB.dropTable(migrationsTable));
}
await task
.run()
.then(async () => {
const dbName = await DB.getCurrentDatabase();
console.log();
this.logger.success(`Database ({yellow} "${dbName}") successfully wiped.`);
})
.finally(() => DB.close());
}
getConfig(name, defaultValue) {
return Config.get(`database.connections.${this.connection === 'default'
? Config.get('database.default')
: this.connection}.${name}`, defaultValue);
}
}
__decorate([
Option({
default: 'default',
signature: '-c, --connection <connection>',
description: 'Set the the database connection.'
}),
__metadata("design:type", String)
], DbWipeCommand.prototype, "connection", void 0);
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { BaseCommand } from '@athenna/artisan';
export declare class MakeCrudCommand extends BaseCommand {
name: string;
namePascal: string;
nameLower: string;
isMongo: boolean;
properties: any[];
static signature(): string;
static description(): string;
cleanGenerator(): void;
toCase(value: string): any;
handle(): Promise<void>;
makeModel(): Promise<void>;
makeMigration(): Promise<void>;
makeController(): Promise<void>;
addRoutes(): Promise<void>;
makeService(): Promise<void>;
makeControllerTest(): Promise<void>;
makeServiceTest(): Promise<void>;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { sep } from 'node:path';
import { Json, Path, File, String } from '@athenna/common';
import { BaseCommand, Option, Argument, Generator } from '@athenna/artisan';
export class MakeCrudCommand extends BaseCommand {
constructor() {
super(...arguments);
this.properties = [];
}
static signature() {
return 'make:crud';
}
static description() {
return 'Make a new CRUD in your application.';
}
cleanGenerator() {
this.generator = new Generator();
}
toCase(value) {
const fileCase = Config.get('rc.commands.make:crud.fileCase', 'toPascalCase');
return String[fileCase](value);
}
async handle() {
this.logger.simple('({bold,green} [ MAKING CRUD ])\n');
this.namePascal = String.toPascalCase(this.name);
this.nameLower = this.name.toLowerCase();
const addId = await this.prompt.confirm(`Do you want to add the ${this.paint.yellow('"id"')} property?`);
if (addId) {
this.properties.push({ name: 'id', type: 'increments', custom: false });
}
const addTimestamps = await this.prompt.confirm(`Does your CRUD need ${this.paint.yellow('"createdAt/updateAt"')} properties?`);
const addSoftDelete = await this.prompt.confirm(`Does your CRUD needs ${this.paint.yellow('"deletedAt"')} property? (Soft Delete)`);
let addMoreProps = await this.prompt.confirm('Do you want to add more properties to your CRUD?');
while (addMoreProps) {
const name = await this.prompt.input('What will be the name of your property?');
const type = await this.prompt.list('What will be the type of your property?', ['string', 'number', 'boolean', 'Date']);
const options = await this.prompt.checkbox('Select the options that fits your property:', [
'isPrimary',
'isUnique',
'isHidden',
'notNullable',
'isIndex',
'isSparse'
]);
const optionsObj = {};
options.forEach(option => (optionsObj[option] = true));
this.properties.push({ name, type, custom: true, ...optionsObj });
addMoreProps = await this.prompt.confirm('Do you want to add more properties?');
}
if (addTimestamps) {
this.properties.push({
name: 'createdAt',
type: 'Date',
custom: false,
isCreateDate: true
});
this.properties.push({
name: 'updatedAt',
type: 'Date',
custom: false,
isUpdateDate: true
});
}
if (addSoftDelete) {
this.properties.push({
name: 'deletedAt',
type: 'Date',
custom: false,
isDeleteDate: true
});
}
console.log();
const task = this.logger.task();
if (Config.get('rc.commands.make:crud.model.enabled', true)) {
task.addPromise('Creating model', () => this.makeModel());
}
if (!this.isMongo &&
Config.get('rc.commands.make:crud.migration.enabled', true)) {
task.addPromise('Creating migration', () => this.makeMigration());
}
if (Config.get('rc.commands.make:crud.controller.enabled', true)) {
task.addPromise('Creating controller', () => this.makeController());
task.addPromise('Adding CRUD routes', () => this.addRoutes());
}
if (Config.get('rc.commands.make:crud.service.enabled', true)) {
task.addPromise('Creating service', () => this.makeService());
}
if (Config.get('rc.commands.make:crud.controller-test.enabled', true)) {
task.addPromise('Creating e2e tests for controller', () => this.makeControllerTest());
}
if (Config.get('rc.commands.make:crud.service-test.enabled', true)) {
task.addPromise('Creating unitary tests for service', () => this.makeServiceTest());
}
await task.run();
console.log();
this.logger.success(`CRUD ({yellow} "${this.name}") successfully created.`);
}
async makeModel() {
this.cleanGenerator();
const destination = Config.get('rc.commands.make:crud.model.destination', Path.models());
let properties = '';
let definitions = '';
this.properties.forEach((p, i) => {
const property = Json.copy(p);
let annotationProps = '';
if (property.isPrimary) {
annotationProps += 'isPrimary: true, ';
}
if (property.isUnique) {
annotationProps += 'isUnique: true, ';
}
if (property.isHidden) {
annotationProps += 'isHidden: true, ';
}
if (property.notNullable) {
annotationProps += 'isNullable: false, ';
}
if (property.isIndex) {
annotationProps += 'isIndex: true, ';
}
if (property.isSparse) {
annotationProps += 'isSparse: true, ';
}
if (property.isCreateDate) {
annotationProps += 'isCreateDate: true, ';
}
if (property.isUpdateDate) {
annotationProps += 'isUpdateDate: true, ';
}
if (property.isDeleteDate) {
annotationProps += 'isDeleteDate: true, ';
}
if (property.type === 'increments') {
property.type = this.isMongo ? 'string' : 'number';
}
if (annotationProps.length) {
properties += ` @Column({ ${annotationProps.slice(0, annotationProps.length - 2)} })\n public ${property.name}: ${property.type}`;
}
else {
properties += ` @Column()\n public ${property.name}: ${property.type}`;
}
const type = {
string: 'this.faker.string.sample()',
number: 'this.faker.number.int({ max: 10000000 })',
boolean: 'this.faker.datatype.boolean()',
Date: 'this.faker.date.anytime()'
};
if (property.custom) {
definitions += ` ${property.name}: ${type[property.type]}`;
}
if (this.properties.length - 1 !== i) {
properties += '\n\n';
if (definitions.length && property.custom)
definitions += ',\n';
}
});
await this.generator
.fileName(this.toCase(this.name))
.destination(destination)
.template('crud-model')
.properties({ properties, definitions })
.setNameProperties(true)
.make();
const importPath = this.generator.getImportPath();
await this.rc.pushTo('models', importPath).save();
}
async makeMigration() {
this.cleanGenerator();
const destination = Config.get('rc.commands.make:crud.migration.destination', Path.migrations());
let properties = '';
this.properties.forEach((property, i) => {
if (property.isCreateDate || property.isUpdateDate) {
if (properties.includes('builder.timestamps(true, true, true)')) {
return;
}
properties += ` builder.timestamps(true, true, true)${this.properties.length - 1 !== i ? '\n' : ''}`;
return;
}
switch (property.type) {
case 'increments':
properties += ` builder.increments('${property.name}')`;
break;
case 'string':
properties += ` builder.string('${property.name}')`;
break;
case 'boolean':
properties += ` builder.boolean('${property.name}')`;
break;
case 'number':
properties += ` builder.integer('${property.name}')`;
break;
case 'Date':
properties += ` builder.timestamp('${property.name}')`;
}
if (property.isPrimary) {
properties += '.primary()';
}
if (property.isUnique) {
properties += '.unique()';
}
if (!property.notNullable && property.type !== 'increments') {
properties += '.nullable()';
}
if (property.isIndex) {
properties += '.index()';
}
if (this.properties.length - 1 !== i) {
properties += '\n';
}
});
const tableName = String.pluralize(this.name);
const namePascal = String.toPascalCase(`Create_${tableName}_Table`);
let [date, time] = new Date().toISOString().split('T');
date = date.replace(/-/g, '_');
time = time.split('.')[0].replace(/:/g, '');
await this.generator
.fileName(`${sep}${date}_${time}_create_${tableName}_table`)
.destination(destination)
.properties({ namePascal, properties, tableName })
.template('crud-migration')
.setNameProperties(false)
.make();
}
async makeController() {
this.cleanGenerator();
const destination = Config.get('rc.commands.make:crud.controller.destination', Path.controllers());
const serviceDest = Config.get('rc.commands.make:crud.service.destination', Path.services());
let properties = '';
this.properties
.filter(p => p.custom)
.forEach(p => {
properties += `'${p.name}', `;
});
this.generator
.fileName(this.toCase(`${this.name}Controller`))
.destination(destination)
.properties({
properties: properties.slice(0, properties.length - 2),
serviceImportPath: new Generator()
.fileName(this.toCase(`${this.name}Service`))
.destination(serviceDest)
.getImportPath(),
crudNamePascal: this.namePascal,
crudNameLower: this.nameLower
})
.template('crud-controller')
.setNameProperties(true);
await this.generator.make();
const importPath = this.generator.getImportPath();
await this.rc.pushTo('controllers', importPath).save();
}
async addRoutes() {
const routeFilePath = Config.get('rc.commands.make:crud.routeFilePath', Path.routes(`http.${Path.ext()}`));
const path = `/${String.pluralize(this.nameLower)}`;
const controller = `${this.namePascal}Controller`;
let body = '';
this.properties
.filter(p => p.custom)
.forEach(property => {
const type = {
string: 'string',
number: 'number',
boolean: 'boolean',
Date: 'string'
};
body += `.body('${property.name}', { type: '${type[property.type]}' })`;
});
const routeContent = `
Route.get('${path}', '${controller}.index')
Route.post('${path}', '${controller}.store')${body}
Route.get('${path}/:id', '${controller}.show')
Route.put('${path}/:id', '${controller}.update')${body}
Route.delete('${path}/:id', '${controller}.delete')
\n`;
await new File(routeFilePath, `import { Route } from '@athenna/http'\n\n`).append(routeContent);
}
async makeService() {
this.cleanGenerator();
const destination = Config.get('rc.commands.make:crud.service.destination', Path.services());
const modelDest = Config.get('rc.commands.make:crud.model.destination', Path.models());
let propertiesToUpdate = '';
this.properties
.filter(p => p.custom)
.forEach(property => {
propertiesToUpdate += ` ${this.nameLower}.${property.name} = body.${property.name}\n`;
});
await this.generator
.fileName(this.toCase(`${this.name}Service`))
.destination(destination)
.properties({
propertiesToUpdate,
idType: this.isMongo ? 'string' : 'number',
modelImportPath: new Generator()
.fileName(this.toCase(this.name))
.destination(modelDest)
.getImportPath(),
crudNamePascal: this.namePascal,
crudNameLower: this.nameLower
})
.template('crud-service')
.setNameProperties(true)
.make();
const importPath = this.generator.getImportPath();
await this.rc.pushTo('services', importPath).save();
}
async makeControllerTest() {
this.cleanGenerator();
const destination = Config.get('rc.commands.make:crud.controller-test.destination', Path.tests('e2e'));
const modelDest = Config.get('rc.commands.make:crud.model.destination', Path.models());
let createBody = '';
let updateBody = '';
let showAssertBody = `id: ${this.nameLower}.id, `;
let createAssertBody = '';
let updateAssertBody = `id: ${this.nameLower}.id, `;
this.properties
.filter(p => p.custom)
.forEach(property => {
const type = {
string: `'string'`,
number: 1,
boolean: true,
Date: 'new Date()'
};
createBody += `${property.name}: ${type[property.type]}, `;
updateBody += `${property.name}: ${type[property.type]}, `;
showAssertBody += `${property.name}: ${type[property.type]}, `;
createAssertBody += `'${property.name}', `;
updateAssertBody += `${property.name}: ${type[property.type]}, `;
});
await this.generator
.fileName(this.toCase(`${this.name}ControllerTest`))
.destination(destination)
.properties({
hasDeletedAt: this.properties.find(p => p.name === 'deletedAt'),
createBody: createBody.slice(0, createBody.length - 2),
updateBody: updateBody.slice(0, updateBody.length - 2),
showAssertBody: showAssertBody.slice(0, showAssertBody.length - 2),
createAssertBody: createAssertBody.slice(0, createAssertBody.length - 2),
updateAssertBody: updateAssertBody.slice(0, updateAssertBody.length - 2),
modelImportPath: new Generator()
.fileName(this.toCase(this.name))
.destination(modelDest)
.getImportPath(),
crudNamePascal: this.namePascal,
crudNamePascalPlural: String.pluralize(this.namePascal),
crudNameLower: this.nameLower,
crudNameLowerPlural: String.pluralize(this.nameLower)
})
.template('crud-controller-test')
.setNameProperties(true)
.make();
}
async makeServiceTest() {
this.cleanGenerator();
const destination = Config.get('rc.commands.make:crud.service-test.destination', Path.tests('unit'));
const modelDest = Config.get('rc.commands.make:crud.model.destination', Path.models());
const serviceDest = Config.get('rc.commands.make:crud.service.destination', Path.services());
let createBody = '';
let updateBody = '';
this.properties
.filter(p => p.custom)
.forEach(property => {
const type = {
string: `'string'`,
number: 1,
boolean: true,
Date: 'new Date()'
};
createBody += `${property.name}: ${type[property.type]}, `;
updateBody += `${property.name}: ${type[property.type]}, `;
});
await this.generator
.fileName(this.toCase(`${this.name}ServiceTest`))
.destination(destination)
.properties({
createBody: createBody.slice(0, createBody.length - 2),
updateBody: updateBody.slice(0, updateBody.length - 2),
modelImportPath: new Generator()
.fileName(this.toCase(this.name))
.destination(modelDest)
.getImportPath(),
serviceImportPath: new Generator()
.fileName(this.toCase(`${this.name}Service`))
.destination(serviceDest)
.getImportPath(),
crudNamePascal: this.namePascal,
crudNamePascalPlural: String.pluralize(this.namePascal),
crudNameLower: this.nameLower,
crudNameLowerPlural: String.pluralize(this.nameLower)
})
.template('crud-service-test')
.setNameProperties(true)
.make();
}
}
__decorate([
Argument({
description: 'The crud name.'
}),
__metadata("design:type", String)
], MakeCrudCommand.prototype, "name", void 0);
__decorate([
Option({
default: false,
signature: '--mongo',
description: 'Define if CRUD will use Mongo as database. Migration will be skipped and "id" will be defined as string.'
}),
__metadata("design:type", Boolean)
], MakeCrudCommand.prototype, "isMongo", void 0);
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { BaseCommand } from '@athenna/artisan';
export declare class MakeMigrationCommand extends BaseCommand {
name: string;
static signature(): string;
static description(): string;
handle(): Promise<void>;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { sep } from 'node:path';
import { Path, String } from '@athenna/common';
import { BaseCommand, Argument } from '@athenna/artisan';
export class MakeMigrationCommand extends BaseCommand {
static signature() {
return 'make:migration';
}
static description() {
return 'Make a new migration file.';
}
async handle() {
this.logger.simple('({bold,green} [ MAKING MIGRATION ])\n');
const nameUp = this.name.toUpperCase();
const nameCamel = String.toCamelCase(this.name);
const namePlural = String.pluralize(this.name);
const namePascal = String.toPascalCase(this.name);
const nameSnake = String.toSnakeCase(this.name);
const namePluralCamel = String.toCamelCase(String.pluralize(this.name));
const namePluralPascal = String.toPascalCase(String.pluralize(this.name));
const destination = Config.get('rc.commands.make:migration.destination', Path.migrations());
const tableName = String.pluralize(namePascal
.replace('Migration', '')
.replace('Migrations', '')
.toLowerCase());
let [date, time] = new Date().toISOString().split('T');
date = date.replace(/-/g, '_');
time = time.split('.')[0].replace(/:/g, '');
const file = await this.generator
.fileName(`${sep}${date}_${time}_${nameSnake}`)
.destination(destination)
.properties({
nameUp,
nameCamel,
namePlural,
namePascal,
namePluralCamel,
namePluralPascal,
tableName
})
.template('migration')
.make();
this.logger.success(`Migration ({yellow} "${file.name}") successfully created.`);
}
}
__decorate([
Argument({
description: 'The migration name.'
}),
__metadata("design:type", String)
], MakeMigrationCommand.prototype, "name", void 0);
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { BaseCommand } from '@athenna/artisan';
export declare class MakeModelCommand extends BaseCommand {
name: string;
static signature(): string;
static description(): string;
handle(): Promise<void>;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { Path } from '@athenna/common';
import { BaseCommand, Argument } from '@athenna/artisan';
export class MakeModelCommand extends BaseCommand {
static signature() {
return 'make:model';
}
static description() {
return 'Make a new model file.';
}
async handle() {
this.logger.simple('({bold,green} [ MAKING MODEL ])\n');
const destination = Config.get('rc.commands.make:model.destination', Path.models());
const file = await this.generator
.fileName(this.name)
.destination(destination)
.template('model')
.setNameProperties(true)
.make();
this.logger.success(`Model ({yellow} "${file.name}") successfully created.`);
const importPath = this.generator.getImportPath();
await this.rc.pushTo('models', importPath).save();
this.logger.success(`Athenna RC updated: ({dim,yellow} [ models += "${importPath}" ])`);
}
}
__decorate([
Argument({
description: 'The model name.'
}),
__metadata("design:type", String)
], MakeModelCommand.prototype, "name", void 0);
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { BaseCommand } from '@athenna/artisan';
export declare class MakeSeederCommand extends BaseCommand {
name: string;
static signature(): string;
static description(): string;
handle(): Promise<void>;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { Path } from '@athenna/common';
import { BaseCommand, Argument } from '@athenna/artisan';
export class MakeSeederCommand extends BaseCommand {
static signature() {
return 'make:seeder';
}
static description() {
return 'Make a new seeder file.';
}
async handle() {
this.logger.simple('({bold,green} [ MAKING SEEDER ])\n');
const destination = Config.get('rc.commands.make:seeder.destination', Path.seeders());
const file = await this.generator
.fileName(this.name)
.destination(destination)
.template('seeder')
.setNameProperties(true)
.make();
this.logger.success(`Seeder ({yellow} "${file.name}") successfully created.`);
}
}
__decorate([
Argument({
description: 'The seeder name.'
}),
__metadata("design:type", String)
], MakeSeederCommand.prototype, "name", void 0);
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { BaseCommand } from '@athenna/artisan';
export declare class MigrationRevertCommand extends BaseCommand {
connection: string;
static signature(): string;
static description(): string;
handle(): Promise<void>;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { Database } from '#src/facades/Database';
import { BaseCommand, Option } from '@athenna/artisan';
export class MigrationRevertCommand extends BaseCommand {
static signature() {
return 'migration:revert';
}
static description() {
return 'Revert your application migrations.';
}
async handle() {
this.logger.simple('({bold,green} [ REVERTING MIGRATIONS ])\n');
if (Config.is(`database.connections.${this.connection}.driver`, 'mongo')) {
this.logger.warn(`Connection ({yellow} "${this.connection}") is using ({yellow} "mongo") driver and migrations revert will be skipped.`);
return;
}
const DB = Database.connection(this.connection);
await DB.revertMigrations()
.then(async () => {
const dbName = await DB.getCurrentDatabase();
this.logger.success(`Successfully reverted migrations on ({yellow} "${dbName}") database.`);
})
.finally(() => DB.close());
}
}
__decorate([
Option({
default: 'default',
signature: '-c, --connection <connection>',
description: 'Set the the database connection.'
}),
__metadata("design:type", String)
], MigrationRevertCommand.prototype, "connection", void 0);
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { BaseCommand } from '@athenna/artisan';
export declare class MigrationRunCommand extends BaseCommand {
connection: string;
static signature(): string;
static description(): string;
handle(): Promise<void>;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { Database } from '#src/facades/Database';
import { BaseCommand, Option } from '@athenna/artisan';
export class MigrationRunCommand extends BaseCommand {
static signature() {
return 'migration:run';
}
static description() {
return 'Run your application migrations.';
}
async handle() {
this.logger.simple('({bold,green} [ RUNNING MIGRATIONS ])\n');
if (Config.is(`database.connections.${this.connection}.driver`, 'mongo')) {
this.logger.warn(`Connection ({yellow} "${this.connection}") is using ({yellow} "mongo") driver and migrations run will be skipped.`);
return;
}
const DB = Database.connection(this.connection);
await DB.runMigrations()
.then(async () => {
const dbName = await DB.getCurrentDatabase();
this.logger.success(`Successfully ran migrations on ({yellow} "${dbName}") database.`);
})
.finally(() => DB.close());
}
}
__decorate([
Option({
default: 'default',
signature: '-c, --connection <connection>',
description: 'Set the the database connection.'
}),
__metadata("design:type", String)
], MigrationRunCommand.prototype, "connection", void 0);
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export declare const COLUMNS_KEY = "database:columns:options";
export declare const HAS_ONE_KEY = "database:hasOne:options";
export declare const HAS_MANY_KEY = "database:hasMany:options";
export declare const BELONGS_TO_KEY = "database:belongsTo:options";
export declare const BELONGS_TO_MANY_KEY = "database:belongsToMany:options";
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export const COLUMNS_KEY = 'database:columns:options';
export const HAS_ONE_KEY = 'database:hasOne:options';
export const HAS_MANY_KEY = 'database:hasMany:options';
export const BELONGS_TO_KEY = 'database:belongsTo:options';
export const BELONGS_TO_MANY_KEY = 'database:belongsToMany:options';
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Map the operations dictionary from an SQL Database
* to MongoDB operators.
*/
export declare const MONGO_OPERATIONS_DICTIONARY: {
'=': string;
'>': string;
'>=': string;
'<': string;
'<=': string;
'<>': string;
like: string;
ilike: string;
};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Map the operations dictionary from an SQL Database
* to MongoDB operators.
*/
export const MONGO_OPERATIONS_DICTIONARY = {
'=': '$match',
'>': '$gt',
'>=': '$gte',
'<': '$lt',
'<=': '$lte',
'<>': '$ne',
like: '$regex',
ilike: '$regex'
};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Symbol used to store the original value of a model.
*/
export declare const ORIGINAL_SYMBOL: unique symbol;
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Symbol used to store the original value of a model.
*/
export const ORIGINAL_SYMBOL = Symbol('BaseModel.original');
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* All Knex methods that are protected by drivers that
* are dependent of Knex query builder. Every time that
* one of these methods are called, a new instance of query
* builder is created inside the Driver using Proxies.
*/
export declare const PROTECTED_QUERY_METHODS: string[];
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* All Knex methods that are protected by drivers that
* are dependent of Knex query builder. Every time that
* one of these methods are called, a new instance of query
* builder is created inside the Driver using Proxies.
*/
export const PROTECTED_QUERY_METHODS = [
'pluck',
'insert',
'update',
'delete',
'first',
'min',
'max',
'sum',
'sumDistinct',
'avg',
'avgDistinct',
'count',
'countDistinct',
'increment',
'decrement'
];
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Macroable, type Collection, type PaginatedResponse, type PaginationOptions } from '@athenna/common';
import type { Operations } from '#src/types/Operations';
import type { Direction, ModelColumns } from '#src/types';
import type { Driver as DriverImpl } from '#src/database/drivers/Driver';
export declare class QueryBuilder<T = any, Driver extends DriverImpl = any> extends Macroable {
/**
* The drivers responsible for handling database operations.
*/
protected driver: Driver;
/**
* The initial table that the query is going to work with.
*/
protected tableName: string;
/**
* Creates a new instance of QueryBuilder.
*/
constructor(driver: Driver, tableName: string);
/**
* Return the client of driver.
*/
getClient(): any;
/**
* Return the query builder of driver.
*/
getQueryBuilder(): any;
/**
* Set the query builder of driver.
*/
setQueryBuilder(queryBuilder: any): this;
/**
* Return the driver of the query builder.
*/
getDriver(): Driver;
/**
* Set the driver of the query builder.
*/
setDriver(driver: any, tableName?: string): this;
/**
* Set the driver primary key that will be used
* when creating new data.
*/
setPrimaryKey(primaryKey: string): this;
/**
* Calculate the average of a given column.
*/
avg(column: string | ModelColumns<T>): Promise<string>;
/**
* Calculate the average of a given column.
*/
avgDistinct(column: string | ModelColumns<T>): Promise<string>;
/**
* Get the max number of a given column.
*/
max(column: string | ModelColumns<T>): Promise<string>;
/**
* Get the min number of a given column.
*/
min(column: string | ModelColumns<T>): Promise<string>;
/**
* Sum all numbers of a given column.
*/
sum(column: string | ModelColumns<T>): Promise<string>;
/**
* Sum all numbers of a given column.
*/
sumDistinct(column: string | ModelColumns<T>): Promise<string>;
/**
* Increment a value of a given column.
*/
increment(column: string | ModelColumns<T>): Promise<void>;
/**
* Decrement a value of a given column.
*/
decrement(column: string | ModelColumns<T>): Promise<void>;
/**
* Calculate the average of a given column using distinct.
*/
count(column?: string | ModelColumns<T>): Promise<string>;
/**
* Calculate the average of a given column using distinct.
*/
countDistinct(column: string | ModelColumns<T>): Promise<string>;
/**
* Find a value in database or throw exception if undefined.
*/
findOrFail(): Promise<T>;
/**
* Return a single data or, if no results are found,
* execute the given closure.
*/
findOr(callback: () => Promise<T>): Promise<T>;
/**
* Find value in database but returns only the value of
* selected column directly.
*/
pluck<K extends keyof T = keyof T>(column: K): Promise<T[K]>;
/**
* Find many values in database but returns only the
* values of selected column directly.
*/
pluckMany<K extends keyof T = keyof T>(column: K): Promise<T[K][]>;
/**
* Find a value in database.
*/
find(): Promise<T>;
/**
* Find a value in database and return as boolean.
*/
exists(): Promise<boolean>;
/**
* Find many values in database.
*/
findMany(): Promise<T[]>;
/**
* Find many values in database and return as a Collection.
*/
collection(): Promise<Collection<T>>;
/**
* Find many values in database and return as paginated response.
*/
paginate(page?: PaginationOptions | number, limit?: number, resourceUrl?: string): Promise<PaginatedResponse>;
/**
* Create a value in database.
*/
create(data?: Partial<T>): Promise<T>;
/**
* Create many values in database.
*/
createMany(data?: Partial<T>[]): Promise<T[]>;
/**
* Create data or update if already exists.
*/
createOrUpdate(data?: Partial<T>): Promise<T | T[]>;
/**
* Update data in database.
*/
update(data: Partial<T>): Promise<T | T[]>;
/**
* Delete data in database.
*/
delete(): Promise<T | T[] | void>;
/**
* Make a raw query in database.
*/
raw(sql: string, bindings?: any): import("knex").Knex.Raw<any>;
/**
* Set a new table to work with in query builder.
*/
table(tableName: string): this;
/**
* Executes the given closure when the first argument is true.
*/
when(criteria: any, closure: (query: this, criteriaValue: any) => any | Promise<any>): this;
/**
* Log in console the actual query built.
*/
dump(): this;
/**
* Set the columns that should be selected on query.
*/
select(...columns: string[] | ModelColumns<T>[]): this;
/**
* Set the columns that should be selected on query raw.
*/
selectRaw(sql: string, bindings?: any): this;
/**
* Set the table that should be used on query.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
from(table: string): this;
/**
* Set the table that should be used on query raw.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
fromRaw(sql: string, bindings?: any): this;
join(tableName: string): this;
join(tableName: string, column: string): this;
join(tableName: string, column1: string, column2: string): this;
join(tableName: string, column1: string, operation: Operations, column2: string): this;
leftJoin(tableName: string): this;
leftJoin(tableName: string, column: string): this;
leftJoin(tableName: string, column1: string, column2: string): this;
leftJoin(tableName: string, column1: string, operation: Operations, column2: string): this;
rightJoin(tableName: string): this;
rightJoin(tableName: string, column: string): this;
rightJoin(tableName: string, column1: string, column2: string): this;
rightJoin(tableName: string, column1: string, operation: Operations, column2: string): this;
crossJoin(tableName: string): this;
crossJoin(tableName: string, column: string): this;
crossJoin(tableName: string, column1: string, column2: string): this;
crossJoin(tableName: string, column1: string, operation: Operations, column2: string): this;
fullOuterJoin(tableName: string): this;
fullOuterJoin(tableName: string, column: string): this;
fullOuterJoin(tableName: string, column1: string, column2: string): this;
fullOuterJoin(tableName: string, column1: string, operation: Operations, column2: string): this;
leftOuterJoin(tableName: string): this;
leftOuterJoin(tableName: string, column: string): this;
leftOuterJoin(tableName: string, column1: string, column2: string): this;
leftOuterJoin(tableName: string, column1: string, operation: Operations, column2: string): this;
rightOuterJoin(tableName: string): this;
rightOuterJoin(tableName: string, column: string): this;
rightOuterJoin(tableName: string, column1: string, column2: string): this;
rightOuterJoin(tableName: string, column1: string, operation: Operations, column2: string): this;
/**
* Set a join raw statement in your query.
*/
joinRaw(sql: string, bindings?: any): this;
/**
* Set a group by statement in your query.
*/
groupBy(...columns: string[] | ModelColumns<T>[]): this;
/**
* Set a group by raw statement in your query.
*/
groupByRaw(sql: string, bindings?: any): this;
/**
* Set a having statement in your query.
*/
having(column: string | ModelColumns<T>, operation?: any | Operations, value?: any): this;
/**
* Set a having raw statement in your query.
*/
havingRaw(sql: string, bindings?: any): this;
/**
* Set a having exists statement in your query.
*/
havingExists(closure: (query: Driver) => void): this;
/**
* Set a having not exists statement in your query.
*/
havingNotExists(closure: (query: Driver) => void): this;
/**
* Set a having in statement in your query.
*/
havingIn(column: string | ModelColumns<T>, values: any[]): this;
/**
* Set a having not in statement in your query.
*/
havingNotIn(column: string | ModelColumns<T>, values: any[]): this;
/**
* Set a having between statement in your query.
*/
havingBetween(column: string | ModelColumns<T>, values: [any, any]): this;
/**
* Set a having not between statement in your query.
*/
havingNotBetween(column: string | ModelColumns<T>, values: [any, any]): this;
/**
* Set a having null statement in your query.
*/
havingNull(column: string | ModelColumns<T>): this;
/**
* Set a having not null statement in your query.
*/
havingNotNull(column: string | ModelColumns<T>): this;
/**
* Set an or having statement in your query.
*/
orHaving(column: string | ModelColumns<T>, operation?: any | Operations, value?: any): this;
/**
* Set an or having raw statement in your query.
*/
orHavingRaw(sql: string, bindings?: any): this;
/**
* Set an or having exists statement in your query.
*/
orHavingExists(closure: (query: Driver) => void): this;
/**
* Set an or having not exists statement in your query.
*/
orHavingNotExists(closure: (query: Driver) => void): this;
/**
* Set an or having in statement in your query.
*/
orHavingIn(column: string | ModelColumns<T>, values: any[]): this;
/**
* Set an or having not in statement in your query.
*/
orHavingNotIn(column: string | ModelColumns<T>, values: any[]): this;
/**
* Set an or having between statement in your query.
*/
orHavingBetween(column: string | ModelColumns<T>, values: [any, any]): this;
/**
* Set an or having not between statement in your query.
*/
orHavingNotBetween(column: string | ModelColumns<T>, values: [any, any]): this;
/**
* Set an or having null statement in your query.
*/
orHavingNull(column: string | ModelColumns<T>): this;
/**
* Set an or having not null statement in your query.
*/
orHavingNotNull(column: string | ModelColumns<T>): this;
where(statement: (query: this) => void): this;
where(statement: Partial<T>): this;
where(statement: Record<string, any>): this;
where(key: string | ModelColumns<T>, value: any): this;
where(key: string | ModelColumns<T>, operation: Operations, value: any): this;
whereNot(statement: (query: this) => void): this;
whereNot(statement: Partial<T>): this;
whereNot(statement: Record<string, any>): this;
whereNot(key: string | ModelColumns<T>, value: any): this;
/**
* Set a where raw statement in your query.
*/
whereRaw(sql: string, bindings?: any): this;
/**
* Set a where exists statement in your query.
*/
whereExists(closure: (query: Driver) => void): this;
/**
* Set a where not exists statement in your query.
*/
whereNotExists(closure: (query: Driver) => void): this;
/**
* Set a where like statement in your query.
*/
whereLike(column: string | ModelColumns<T>, value: any): this;
/**
* Set a where ILike statement in your query.
*/
whereILike(column: string | ModelColumns<T>, value: any): this;
/**
* Set a where in statement in your query.
*/
whereIn(column: string | ModelColumns<T>, values: any[]): this;
/**
* Set a where not in statement in your query.
*/
whereNotIn(column: string | ModelColumns<T>, values: any[]): this;
/**
* Set a where between statement in your query.
*/
whereBetween(column: string | ModelColumns<T>, values: [any, any]): this;
/**
* Set a where not between statement in your query.
*/
whereNotBetween(column: string | ModelColumns<T>, values: [any, any]): this;
/**
* Set a where null statement in your query.
*/
whereNull(column: string | ModelColumns<T>): this;
/**
* Set a where not null statement in your query.
*/
whereNotNull(column: string | ModelColumns<T>): this;
orWhere(statement: (query: this) => void): this;
orWhere(statement: Partial<T>): this;
orWhere(statement: Record<string, any>): this;
orWhere(key: string | ModelColumns<T>, value: any): this;
orWhere(key: string | ModelColumns<T>, operation: Operations, value: any): this;
orWhereNot(statement: (query: this) => void): this;
orWhereNot(statement: Partial<T>): this;
orWhereNot(statement: Record<string, any>): this;
orWhereNot(key: string | ModelColumns<T>, value: any): this;
/**
* Set a or where raw statement in your query.
*/
orWhereRaw(sql: string, bindings?: any): this;
/**
* Set an or where exists statement in your query.
*/
orWhereExists(closure: (query: Driver) => void): this;
/**
* Set an or where not exists statement in your query.
*/
orWhereNotExists(closure: (query: Driver) => void): this;
orWhereLike(statement: Partial<T>): this;
orWhereLike(statement: Record<string, any>): this;
orWhereLike(key: string | ModelColumns<T>, value: any): this;
orWhereILike(statement: Partial<T>): this;
orWhereILike(statement: Record<string, any>): this;
orWhereILike(key: string | ModelColumns<T>, value: any): this;
/**
* Set an or where in statement in your query.
*/
orWhereIn(column: string | ModelColumns<T>, values: any[]): this;
/**
* Set an or where not in statement in your query.
*/
orWhereNotIn(column: string | ModelColumns<T>, values: any[]): this;
/**
* Set an or where between statement in your query.
*/
orWhereBetween(column: string | ModelColumns<T>, values: [any, any]): this;
/**
* Set an or where not between statement in your query.
*/
orWhereNotBetween(column: string | ModelColumns<T>, values: [any, any]): this;
/**
* Set an or where null statement in your query.
*/
orWhereNull(column: string | ModelColumns<T>): this;
/**
* Set an or where not null statement in your query.
*/
orWhereNotNull(column: string | ModelColumns<T>): this;
/**
* Set an order by statement in your query.
*/
orderBy(column: string | ModelColumns<T>, direction?: Direction): this;
/**
* Set an order by raw statement in your query.
*/
orderByRaw(sql: string, bindings?: any): this;
/**
* Order the results easily by the latest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
latest(column?: string | ModelColumns<T>): this;
/**
* Order the results easily by the oldest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
oldest(column?: string | ModelColumns<T>): this;
/**
* Set the skip number in your query.
*/
offset(number: number): this;
/**
* Set the limit number in your query.
*/
limit(number: number): this;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Macroable } from '@athenna/common';
export class QueryBuilder extends Macroable {
/**
* Creates a new instance of QueryBuilder.
*/
constructor(driver, tableName) {
super();
this.driver = driver;
this.tableName = tableName;
this.driver.table(tableName);
}
/**
* Return the client of driver.
*/
getClient() {
return this.driver.getClient();
}
/**
* Return the query builder of driver.
*/
getQueryBuilder() {
return this.driver.getQueryBuilder();
}
/**
* Set the query builder of driver.
*/
setQueryBuilder(queryBuilder) {
this.driver.setQueryBuilder(queryBuilder, { useSetQB: true });
return this;
}
/**
* Return the driver of the query builder.
*/
getDriver() {
return this.driver;
}
/**
* Set the driver of the query builder.
*/
setDriver(driver, tableName) {
this.driver = driver;
this.driver.table(tableName || this.tableName);
return this;
}
/**
* Set the driver primary key that will be used
* when creating new data.
*/
setPrimaryKey(primaryKey) {
this.driver.setPrimaryKey(primaryKey);
return this;
}
/**
* Calculate the average of a given column.
*/
async avg(column) {
return this.driver.avg(column);
}
/**
* Calculate the average of a given column.
*/
async avgDistinct(column) {
return this.driver.avgDistinct(column);
}
/**
* Get the max number of a given column.
*/
async max(column) {
return this.driver.max(column);
}
/**
* Get the min number of a given column.
*/
async min(column) {
return this.driver.min(column);
}
/**
* Sum all numbers of a given column.
*/
async sum(column) {
return this.driver.sum(column);
}
/**
* Sum all numbers of a given column.
*/
async sumDistinct(column) {
return this.driver.sumDistinct(column);
}
/**
* Increment a value of a given column.
*/
async increment(column) {
await this.driver.increment(column);
}
/**
* Decrement a value of a given column.
*/
async decrement(column) {
await this.driver.decrement(column);
}
/**
* Calculate the average of a given column using distinct.
*/
async count(column = '*') {
return this.driver.count(column);
}
/**
* Calculate the average of a given column using distinct.
*/
async countDistinct(column) {
return this.driver.countDistinct(column);
}
/**
* Find a value in database or throw exception if undefined.
*/
async findOrFail() {
return this.driver.findOrFail();
}
/**
* Return a single data or, if no results are found,
* execute the given closure.
*/
async findOr(callback) {
return this.driver.findOr(callback);
}
/**
* Find value in database but returns only the value of
* selected column directly.
*/
async pluck(column) {
return this.driver.pluck(column);
}
/**
* Find many values in database but returns only the
* values of selected column directly.
*/
async pluckMany(column) {
return this.driver.pluckMany(column);
}
/**
* Find a value in database.
*/
async find() {
return this.driver.find();
}
/**
* Find a value in database and return as boolean.
*/
async exists() {
return this.driver.exists();
}
/**
* Find many values in database.
*/
async findMany() {
return this.driver.findMany();
}
/**
* Find many values in database and return as a Collection.
*/
async collection() {
return this.driver.collection();
}
/**
* Find many values in database and return as paginated response.
*/
async paginate(page = { page: 0, limit: 10, resourceUrl: '/' }, limit = 10, resourceUrl = '/') {
return this.driver.paginate(page, limit, resourceUrl);
}
/**
* Create a value in database.
*/
async create(data) {
return this.driver.create(data);
}
/**
* Create many values in database.
*/
async createMany(data) {
return this.driver.createMany(data);
}
/**
* Create data or update if already exists.
*/
async createOrUpdate(data) {
return this.driver.createOrUpdate(data);
}
/**
* Update data in database.
*/
async update(data) {
return this.driver.update(data);
}
/**
* Delete data in database.
*/
async delete() {
return this.driver.delete();
}
/**
* Make a raw query in database.
*/
raw(sql, bindings) {
return this.driver.raw(sql, bindings);
}
/**
* Set a new table to work with in query builder.
*/
table(tableName) {
this.driver.table(tableName);
return this;
}
/**
* Executes the given closure when the first argument is true.
*/
when(criteria, closure) {
if (criteria) {
closure(this, criteria);
return this;
}
return this;
}
/**
* Log in console the actual query built.
*/
dump() {
this.driver.dump();
return this;
}
/**
* Set the columns that should be selected on query.
*/
select(...columns) {
this.driver.select(...columns);
return this;
}
/**
* Set the columns that should be selected on query raw.
*/
selectRaw(sql, bindings) {
this.driver.selectRaw(sql, bindings);
return this;
}
/**
* Set the table that should be used on query.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
from(table) {
this.driver.from(table);
return this;
}
/**
* Set the table that should be used on query raw.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
fromRaw(sql, bindings) {
this.driver.fromRaw(sql, bindings);
return this;
}
/**
* Set a join statement in your query.
*/
join(tableName, column1, operation, column2) {
this.driver.join(tableName, column1, operation, column2);
return this;
}
/**
* Set a left join statement in your query.
*/
leftJoin(tableName, column1, operation, column2) {
this.driver.leftJoin(tableName, column1, operation, column2);
return this;
}
/**
* Set a right join statement in your query.
*/
rightJoin(tableName, column1, operation, column2) {
this.driver.rightJoin(tableName, column1, operation, column2);
return this;
}
/**
* Set a cross join statement in your query.
*/
crossJoin(tableName, column1, operation, column2) {
this.driver.crossJoin(tableName, column1, operation, column2);
return this;
}
/**
* Set a full outer join statement in your query.
*/
fullOuterJoin(tableName, column1, operation, column2) {
this.driver.fullOuterJoin(tableName, column1, operation, column2);
return this;
}
/**
* Set a left outer join statement in your query.
*/
leftOuterJoin(tableName, column1, operation, column2) {
this.driver.leftOuterJoin(tableName, column1, operation, column2);
return this;
}
/**
* Set a right outer join statement in your query.
*/
rightOuterJoin(tableName, column1, operation, column2) {
this.driver.rightOuterJoin(tableName, column1, operation, column2);
return this;
}
/**
* Set a join raw statement in your query.
*/
joinRaw(sql, bindings) {
this.driver.joinRaw(sql, bindings);
return this;
}
/**
* Set a group by statement in your query.
*/
groupBy(...columns) {
this.driver.groupBy(...columns);
return this;
}
/**
* Set a group by raw statement in your query.
*/
groupByRaw(sql, bindings) {
this.driver.groupByRaw(sql, bindings);
return this;
}
/**
* Set a having statement in your query.
*/
having(column, operation, value) {
this.driver.having(column, operation, value);
return this;
}
/**
* Set a having raw statement in your query.
*/
havingRaw(sql, bindings) {
this.driver.havingRaw(sql, bindings);
return this;
}
/**
* Set a having exists statement in your query.
*/
havingExists(closure) {
this.driver.havingExists(closure);
return this;
}
/**
* Set a having not exists statement in your query.
*/
havingNotExists(closure) {
this.driver.havingNotExists(closure);
return this;
}
/**
* Set a having in statement in your query.
*/
havingIn(column, values) {
this.driver.havingIn(column, values);
return this;
}
/**
* Set a having not in statement in your query.
*/
havingNotIn(column, values) {
this.driver.havingNotIn(column, values);
return this;
}
/**
* Set a having between statement in your query.
*/
havingBetween(column, values) {
this.driver.havingBetween(column, values);
return this;
}
/**
* Set a having not between statement in your query.
*/
havingNotBetween(column, values) {
this.driver.havingNotBetween(column, values);
return this;
}
/**
* Set a having null statement in your query.
*/
havingNull(column) {
this.driver.havingNull(column);
return this;
}
/**
* Set a having not null statement in your query.
*/
havingNotNull(column) {
this.driver.havingNotNull(column);
return this;
}
/**
* Set an or having statement in your query.
*/
orHaving(column, operation, value) {
this.driver.orHaving(column, operation, value);
return this;
}
/**
* Set an or having raw statement in your query.
*/
orHavingRaw(sql, bindings) {
this.driver.orHavingRaw(sql, bindings);
return this;
}
/**
* Set an or having exists statement in your query.
*/
orHavingExists(closure) {
this.driver.orHavingExists(closure);
return this;
}
/**
* Set an or having not exists statement in your query.
*/
orHavingNotExists(closure) {
this.driver.orHavingNotExists(closure);
return this;
}
/**
* Set an or having in statement in your query.
*/
orHavingIn(column, values) {
this.driver.orHavingIn(column, values);
return this;
}
/**
* Set an or having not in statement in your query.
*/
orHavingNotIn(column, values) {
this.driver.orHavingNotIn(column, values);
return this;
}
/**
* Set an or having between statement in your query.
*/
orHavingBetween(column, values) {
this.driver.orHavingBetween(column, values);
return this;
}
/**
* Set an or having not between statement in your query.
*/
orHavingNotBetween(column, values) {
this.driver.orHavingNotBetween(column, values);
return this;
}
/**
* Set an or having null statement in your query.
*/
orHavingNull(column) {
this.driver.orHavingNull(column);
return this;
}
/**
* Set an or having not null statement in your query.
*/
orHavingNotNull(column) {
this.driver.orHavingNotNull(column);
return this;
}
/**
* Set a where statement in your query.
*/
where(statement, operation, value) {
this.driver.where(statement, operation, value);
return this;
}
/**
* Set a where not statement in your query.
*/
whereNot(statement, value) {
this.driver.whereNot(statement, value);
return this;
}
/**
* Set a where raw statement in your query.
*/
whereRaw(sql, bindings) {
this.driver.whereRaw(sql, bindings);
return this;
}
/**
* Set a where exists statement in your query.
*/
whereExists(closure) {
this.driver.whereExists(closure);
return this;
}
/**
* Set a where not exists statement in your query.
*/
whereNotExists(closure) {
this.driver.whereNotExists(closure);
return this;
}
/**
* Set a where like statement in your query.
*/
whereLike(column, value) {
this.driver.whereLike(column, value);
return this;
}
/**
* Set a where ILike statement in your query.
*/
whereILike(column, value) {
this.driver.whereILike(column, value);
return this;
}
/**
* Set a where in statement in your query.
*/
whereIn(column, values) {
this.driver.whereIn(column, values);
return this;
}
/**
* Set a where not in statement in your query.
*/
whereNotIn(column, values) {
this.driver.whereNotIn(column, values);
return this;
}
/**
* Set a where between statement in your query.
*/
whereBetween(column, values) {
this.driver.whereBetween(column, values);
return this;
}
/**
* Set a where not between statement in your query.
*/
whereNotBetween(column, values) {
this.driver.whereNotBetween(column, values);
return this;
}
/**
* Set a where null statement in your query.
*/
whereNull(column) {
this.driver.whereNull(column);
return this;
}
/**
* Set a where not null statement in your query.
*/
whereNotNull(column) {
this.driver.whereNotNull(column);
return this;
}
/**
* Set a or where statement in your query.
*/
orWhere(statement, operation, value) {
this.driver.orWhere(statement, operation, value);
return this;
}
/**
* Set a where not statement in your query.
*/
orWhereNot(statement, value) {
this.driver.orWhereNot(statement, value);
return this;
}
/**
* Set a or where raw statement in your query.
*/
orWhereRaw(sql, bindings) {
this.driver.orWhereRaw(sql, bindings);
return this;
}
/**
* Set an or where exists statement in your query.
*/
orWhereExists(closure) {
this.driver.orWhereExists(closure);
return this;
}
/**
* Set an or where not exists statement in your query.
*/
orWhereNotExists(closure) {
this.driver.orWhereNotExists(closure);
return this;
}
/**
* Set an or where like statement in your query.
*/
orWhereLike(statement, value) {
this.driver.orWhereLike(statement, value);
return this;
}
/**
* Set an or where ILike statement in your query.
*/
orWhereILike(statement, value) {
this.driver.orWhereILike(statement, value);
return this;
}
/**
* Set an or where in statement in your query.
*/
orWhereIn(column, values) {
this.driver.orWhereIn(column, values);
return this;
}
/**
* Set an or where not in statement in your query.
*/
orWhereNotIn(column, values) {
this.driver.orWhereNotIn(column, values);
return this;
}
/**
* Set an or where between statement in your query.
*/
orWhereBetween(column, values) {
this.driver.orWhereBetween(column, values);
return this;
}
/**
* Set an or where not between statement in your query.
*/
orWhereNotBetween(column, values) {
this.driver.orWhereNotBetween(column, values);
return this;
}
/**
* Set an or where null statement in your query.
*/
orWhereNull(column) {
this.driver.orWhereNull(column);
return this;
}
/**
* Set an or where not null statement in your query.
*/
orWhereNotNull(column) {
this.driver.orWhereNotNull(column);
return this;
}
/**
* Set an order by statement in your query.
*/
orderBy(column, direction = 'ASC') {
this.driver.orderBy(column, direction.toUpperCase());
return this;
}
/**
* Set an order by raw statement in your query.
*/
orderByRaw(sql, bindings) {
this.driver.orderByRaw(sql, bindings);
return this;
}
/**
* Order the results easily by the latest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
latest(column = 'createdAt') {
this.driver.latest(column);
return this;
}
/**
* Order the results easily by the oldest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
oldest(column = 'createdAt') {
this.driver.oldest(column);
return this;
}
/**
* Set the skip number in your query.
*/
offset(number) {
this.driver.offset(number);
return this;
}
/**
* Set the limit number in your query.
*/
limit(number) {
this.driver.limit(number);
return this;
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { Knex } from 'knex';
import type { ConnectionOptions } from '#src/types';
import type { FakeDriver } from '#src/database/drivers/FakeDriver';
import { QueryBuilder } from '#src/database/builders/QueryBuilder';
import { Macroable } from '@athenna/common';
import type { MongoDriver } from '#src/database/drivers/MongoDriver';
import type { MySqlDriver } from '#src/database/drivers/MySqlDriver';
import type { SqliteDriver } from '#src/database/drivers/SqliteDriver';
import type { Driver as DriverImpl } from '#src/database/drivers/Driver';
import type { Transaction } from '#src/database/transactions/Transaction';
import type { PostgresDriver } from '#src/database/drivers/PostgresDriver';
export declare class DatabaseImpl<Driver extends DriverImpl = any> extends Macroable {
/**
* The connection name used for this instance.
*/
connectionName: string;
/**
* The drivers responsible for handling database operations.
*/
driver: Driver;
/**
* Creates a new instance of DatabaseImpl.
*/
constructor(athennaDbOpts?: ConnectionOptions);
connection(con: 'mongo', options?: ConnectionOptions): DatabaseImpl<MongoDriver>;
connection(con: 'mysql', options?: ConnectionOptions): DatabaseImpl<MySqlDriver>;
connection(con: 'sqlite', options?: ConnectionOptions): DatabaseImpl<SqliteDriver>;
connection(con: 'postgres', options?: ConnectionOptions): DatabaseImpl<PostgresDriver>;
connection(con: 'fake', options?: ConnectionOptions): DatabaseImpl<typeof FakeDriver>;
connection(con: 'mongo' | 'mysql' | 'sqlite' | 'postgres' | 'fake' | string, options?: ConnectionOptions): DatabaseImpl<MongoDriver> | DatabaseImpl<MySqlDriver> | DatabaseImpl<SqliteDriver> | DatabaseImpl<PostgresDriver> | DatabaseImpl<typeof FakeDriver>;
/**
* Verify if database is already connected.
*/
isConnected(): boolean;
/**
* Connect to database.
*/
connect(options?: ConnectionOptions): DatabaseImpl<Driver>;
/**
* Close the connection with database in this instance.
*/
close(): Promise<void>;
/**
* Close all the connections with all databases.
*/
closeAll(): Promise<void>;
/**
* Return the client of driver.
*/
getClient(): any;
/**
* Return the query builder of driver.
*/
getQueryBuilder(): any;
/**
* Create a new transaction.
*/
startTransaction(): Promise<Transaction<Driver>>;
/**
* Run database seeders.
*/
runSeeders(options?: {
task?: any;
path?: string;
classes?: string[];
}): Promise<void>;
/**
* Run database migrations.
*/
runMigrations(): Promise<void>;
/**
* Revert database migrations.
*/
revertMigrations(): Promise<void>;
/**
* List all databases available.
*/
getDatabases(): Promise<string[]>;
/**
* Get the current database name.
*/
getCurrentDatabase(): Promise<string | undefined>;
/**
* Verify if database exists.
*/
hasDatabase(database: string): Promise<boolean>;
/**
* Create a new database.
*/
createDatabase(database: string): Promise<void>;
/**
* Drop some database.
*/
dropDatabase(database: string): Promise<void>;
/**
* List all tables available.
*/
getTables(): Promise<string[]>;
/**
* Verify if table exists.
*/
hasTable(table: string): Promise<boolean>;
/**
* Create a new table in database.
*/
createTable(table: string, closure: (builder: Knex.TableBuilder) => void | Promise<void>): Promise<void>;
/**
* Alter a table in database.
*/
alterTable(table: string, closure: (builder: Knex.TableBuilder) => void | Promise<void>): Promise<void>;
/**
* Drop a table in database.
*/
dropTable(table: string): Promise<void>;
/**
* Remove all data inside some database table
* and restart the identity of the table.
*/
truncate(table: string): Promise<void>;
/**
* Make a raw query in database.
*/
raw<T = any>(sql: string, bindings?: any): Knex.Raw<T>;
/**
* Creates a new instance of QueryBuilder for this table.
*/
table<T = any>(table: string | any): QueryBuilder<T, Driver>;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { QueryBuilder } from '#src/database/builders/QueryBuilder';
import { Path, Module, Options, Macroable } from '@athenna/common';
import { ConnectionFactory } from '#src/factories/ConnectionFactory';
export class DatabaseImpl extends Macroable {
/**
* Creates a new instance of DatabaseImpl.
*/
constructor(athennaDbOpts) {
super();
/**
* The connection name used for this instance.
*/
this.connectionName = Config.get('database.default');
/**
* The drivers responsible for handling database operations.
*/
this.driver = null;
this.driver = ConnectionFactory.fabricate(this.connectionName);
this.connect(athennaDbOpts);
}
/**
* Change the database connection.
*/
connection(con, options) {
const driver = ConnectionFactory.fabricate(con);
const database = new DatabaseImpl(options);
database.connectionName = con;
database.driver = driver;
return database.connect(options);
}
/**
* Verify if database is already connected.
*/
isConnected() {
return this.driver.isConnected;
}
/**
* Connect to database.
*/
connect(options) {
this.driver.connect(options);
return this;
}
/**
* Close the connection with database in this instance.
*/
async close() {
await this.driver.close();
}
/**
* Close all the connections with all databases.
*/
async closeAll() {
const cons = ConnectionFactory.availableConnections();
const promises = cons.map(con => {
const driver = ConnectionFactory.fabricate(con);
return driver.close().then(() => ConnectionFactory.setClient(con, null));
});
await Promise.all(promises);
}
/**
* Return the client of driver.
*/
getClient() {
return this.driver.getClient();
}
/**
* Return the query builder of driver.
*/
getQueryBuilder() {
return this.driver.getQueryBuilder();
}
/**
* Create a new transaction.
*/
async startTransaction() {
return this.driver.startTransaction();
}
/**
* Run database seeders.
*/
async runSeeders(options = {}) {
options = Options.create(options, {
classes: [],
task: null,
path: Path.seeders()
});
const seeds = await Module.getAllFrom(options.path);
for (const Seed of seeds) {
if (options.classes?.length && !options.classes.includes(Seed.name)) {
continue;
}
if (!options.task) {
await new Seed().run(this);
continue;
}
const msg = `Running "${Seed.name}" seeder`;
options.task.addPromise(msg, () => new Seed().run(this));
}
}
/**
* Run database migrations.
*/
async runMigrations() {
await this.driver.runMigrations();
}
/**
* Revert database migrations.
*/
async revertMigrations() {
await this.driver.revertMigrations();
}
/**
* List all databases available.
*/
async getDatabases() {
return this.driver.getDatabases();
}
/**
* Get the current database name.
*/
async getCurrentDatabase() {
return this.driver.getCurrentDatabase();
}
/**
* Verify if database exists.
*/
async hasDatabase(database) {
return this.driver.hasDatabase(database);
}
/**
* Create a new database.
*/
async createDatabase(database) {
await this.driver.createDatabase(database);
}
/**
* Drop some database.
*/
async dropDatabase(database) {
await this.driver.dropDatabase(database);
}
/**
* List all tables available.
*/
async getTables() {
return this.driver.getTables();
}
/**
* Verify if table exists.
*/
async hasTable(table) {
return this.driver.hasTable(table);
}
/**
* Create a new table in database.
*/
async createTable(table, closure) {
await this.driver.createTable(table, closure);
}
/**
* Alter a table in database.
*/
async alterTable(table, closure) {
await this.driver.alterTable(table, closure);
}
/**
* Drop a table in database.
*/
async dropTable(table) {
await this.driver.dropTable(table);
}
/**
* Remove all data inside some database table
* and restart the identity of the table.
*/
async truncate(table) {
return this.driver.truncate(table);
}
/**
* Make a raw query in database.
*/
raw(sql, bindings) {
return this.driver.raw(sql, bindings);
}
/**
* Creates a new instance of QueryBuilder for this table.
*/
table(table) {
return new QueryBuilder(this.driver, table);
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Collection, type PaginatedResponse, type PaginationOptions } from '@athenna/common';
import type { Knex, TableBuilder } from 'knex';
import type { ModelSchema } from '#src/models/schemas/ModelSchema';
import type { Transaction } from '#src/database/transactions/Transaction';
import type { Direction, ConnectionOptions, Operations } from '#src/types';
export declare abstract class Driver<Client = any, QB = any> {
/**
* Set the primary key of the driver.
*/
primaryKey: string;
/**
* Set if this instance is connected with database.
*/
isConnected: boolean;
/**
* Set if the connection will be saved on factory.
*/
isSavedOnFactory: boolean;
/**
* The connection name used for this instance.
*/
connection: string;
/**
* Set the table that this instance will work with.
*/
tableName: string;
/**
* Set the client of this driver.
*/
client: Client;
/**
* The main query builder of driver.
*/
qb: QB;
/**
* Use set query builder instead of creating a new one
* using client.
*/
useSetQB: boolean;
/**
* Creates a new instance of the Driver.
*/
constructor(connection: string | any, client?: Client);
/**
* Import knex in a method to be easier to mock.
*/
getKnex(): any;
/**
* Import mongoose in a method to be easier to mock.
*/
getMongoose(): any;
/**
* Clone the driver instance.
*/
clone(): Driver<Client, QB>;
/**
* Return the client of driver.
*/
getClient(): Client;
/**
* Set a client in driver.
*/
setClient(client: Client): this;
/**
* Return the query builder of driver.
*/
getQueryBuilder(): QB;
/**
* Set a query builder in driver.
*/
setQueryBuilder(queryBuilder: QB, options?: {
useSetQB?: boolean;
}): this;
/**
* Set the primary key of the driver.
*/
setPrimaryKey(primaryKey: string): this;
/**
* Create a join clause by join type
*/
joinByType(joinType: string, table: any, column1?: string, operation?: string | Operations, column2?: string): this;
/**
* Connect to database.
*/
abstract connect(options?: ConnectionOptions): void;
/**
* Close the connection with database in this instance.
*/
abstract close(): Promise<void>;
/**
* Creates a new instance of query builder.
*/
abstract query(): QB;
/**
* Sync a model schema with the driver.
*/
abstract sync(schema: ModelSchema): Promise<any>;
/**
* Create a new transaction.
*/
abstract startTransaction(): Promise<Transaction<Client, QB>>;
/**
* Commit the transaction.
*/
abstract commitTransaction(): Promise<void>;
/**
* Rollback the transaction.
*/
abstract rollbackTransaction(): Promise<void>;
/**
* Run database migrations.
*/
abstract runMigrations(): Promise<void>;
/**
* Revert database migrations.
*/
abstract revertMigrations(): Promise<void>;
/**
* List all databases available.
*/
abstract getDatabases(): Promise<string[]>;
/**
* Get the current database name.
*/
abstract getCurrentDatabase(): Promise<string | undefined>;
/**
* Verify if database exists.
*/
abstract hasDatabase(database: string): Promise<boolean>;
/**
* Create a new database.
*/
abstract createDatabase(database: string): Promise<void>;
/**
* Drop some database.
*/
abstract dropDatabase(database: string): Promise<void>;
/**
* List all tables available.
*/
abstract getTables(): Promise<string[]>;
/**
* Verify if table exists.
*/
abstract hasTable(table: string): Promise<boolean>;
/**
* Create a new table in database.
*/
abstract createTable(table: string, closure?: TableBuilder): Promise<void>;
/**
* Alter a table in database.
*/
abstract alterTable(table: string, closure?: TableBuilder): Promise<void>;
/**
* Drop a table in database.
*/
abstract dropTable(table: string): Promise<void>;
/**
* Remove all data inside some database table
* and restart the identity of the table.
*/
abstract truncate(table: string): Promise<void>;
/**
* Make a raw query in database.
*/
abstract raw<T = any>(sql: string, bindings?: any): Knex.Raw<T>;
/**
* Calculate the average of a given column.
*/
abstract avg(column: string): Promise<string>;
/**
* Calculate the average of a given column using distinct.
*/
abstract avgDistinct(column: string): Promise<string>;
/**
* Get the max number of a given column.
*/
abstract max(column: string): Promise<string>;
/**
* Get the min number of a given column.
*/
abstract min(column: string): Promise<string>;
/**
* Sum all numbers of a given column.
*/
abstract sum(column: string): Promise<string>;
/**
* Sum all numbers of a given column in distinct mode.
*/
abstract sumDistinct(column: string): Promise<string>;
/**
* Increment a value of a given column.
*/
abstract increment(column: string): Promise<void>;
/**
* Decrement a value of a given column.
*/
abstract decrement(column: string): Promise<void>;
/**
* Calculate the average of a given column using distinct.
*/
abstract count(column?: string): Promise<string>;
/**
* Calculate the average of a given column using distinct.
*/
abstract countDistinct(column?: string): Promise<string>;
/**
* Find a value in database and return as boolean.
*/
exists(): Promise<boolean>;
/**
* Find a value in database or throw exception if undefined.
*/
findOrFail<T = any>(): Promise<T>;
/**
* Return a single model instance or, if no results are found,
* execute the given closure.
*/
findOr<T = any>(closure: () => T | Promise<T>): Promise<T>;
/**
* Find value in database but returns only the value of
* selected column directly.
*/
pluck<T = any, K extends keyof T = keyof T>(column: K): Promise<T[K]>;
/**
* Find many values in database but returns only the
* values of selected column directly.
*/
pluckMany<T = any, K extends keyof T = keyof T>(column: K): Promise<T[K][]>;
/**
* Find a value in database.
*/
abstract find<T = any>(): Promise<T>;
/**
* Find many values in database.
*/
abstract findMany<T = any>(): Promise<T[]>;
/**
* Find many values in database and return as a Collection.
*/
collection<T = any>(): Promise<Collection<T>>;
/**
* Find many values in database and return as paginated response.
*/
abstract paginate<T = any>(page?: PaginationOptions | number, limit?: number, resourceUrl?: string): Promise<PaginatedResponse<T>>;
/**
* Create a value in database.
*/
abstract create<T = any>(data?: Partial<T>): Promise<T>;
/**
* Create many values in database.
*/
abstract createMany<T = any>(data?: Partial<T>[]): Promise<T[]>;
/**
* Create data or update if already exists.
*/
abstract createOrUpdate<T = any>(data?: Partial<T>): Promise<T | T[]>;
/**
* Update a value in database.
*/
abstract update<T = any>(data: Partial<T>): Promise<T | T[]>;
/**
* Delete one value in database.
*/
abstract delete(): Promise<void>;
/**
* Set the table that this query will be executed.
*/
abstract table(tableName: string | any): this;
/**
* Log in console the actual query built.
*/
abstract dump(): this;
/**
* Executes the given closure when the first argument is true.
*/
when(criteria: any, closure: (query: this, criteriaValue?: any) => any | Promise<any>): this;
/**
* Set the columns that should be selected on query.
*/
abstract select(...columns: string[]): this;
/**
* Set the columns that should be selected on query raw.
*/
abstract selectRaw(sql: string, bindings?: any): this;
/**
* Set the table that should be used on query.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
abstract from(table: string): this;
/**
* Set the table that should be used on query raw.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
abstract fromRaw(sql: string, bindings?: any): this;
/**
* Set a join statement in your query.
*/
abstract join(table: any, column1?: string, operation?: string | Operations, column2?: string): this;
/**
* Set a left join statement in your query.
*/
abstract leftJoin(table: any, column1?: string, operation?: string | Operations, column2?: string): this;
/**
* Set a right join statement in your query.
*/
abstract rightJoin(table: any, column1?: string, operation?: string | Operations, column2?: string): this;
/**
* Set a cross join statement in your query.
*/
abstract crossJoin(table: any, column1?: string, operation?: string | Operations, column2?: string): this;
/**
* Set a full outer join statement in your query.
*/
abstract fullOuterJoin(table: any, column1?: string, operation?: string | Operations, column2?: string): this;
/**
* Set a left outer join statement in your query.
*/
abstract leftOuterJoin(table: any, column1?: string, operation?: string | Operations, column2?: string): this;
/**
* Set a right outer join statement in your query.
*/
abstract rightOuterJoin(table: any, column1?: string, operation?: string | Operations, column2?: string): this;
/**
* Set a join raw statement in your query.
*/
abstract joinRaw(sql: string, bindings?: any): this;
/**
* Set a group by statement in your query.
*/
abstract groupBy(...columns: string[]): this;
/**
* Set a group by raw statement in your query.
*/
abstract groupByRaw(sql: string, bindings?: any): this;
/**
* Set a having statement in your query.
*/
abstract having(column: any, operation?: string | Operations, value?: any): this;
/**
* Set a having raw statement in your query.
*/
abstract havingRaw(sql: string, bindings?: any): this;
/**
* Set a having exists statement in your query.
*/
abstract havingExists(closure: (query: Driver<Client, QB>) => void): this;
/**
* Set a having not exists statement in your query.
*/
abstract havingNotExists(closure: (query: Driver<Client, QB>) => void): this;
/**
* Set a having in statement in your query.
*/
abstract havingIn(column: string, values: any[]): this;
/**
* Set a having not in statement in your query.
*/
abstract havingNotIn(column: string, values: any[]): this;
/**
* Set a having between statement in your query.
*/
abstract havingBetween(column: string, values: [any, any]): this;
/**
* Set a having not between statement in your query.
*/
abstract havingNotBetween(column: string, values: [any, any]): this;
/**
* Set a having null statement in your query.
*/
abstract havingNull(column: string): this;
/**
* Set a having not null statement in your query.
*/
abstract havingNotNull(column: string): this;
/**
* Set an or having statement in your query.
*/
abstract orHaving(column: any, operation?: string | Operations, value?: any): this;
/**
* Set an or having raw statement in your query.
*/
abstract orHavingRaw(sql: string, bindings?: any): this;
/**
* Set an or having exists statement in your query.
*/
abstract orHavingExists(closure: (query: Driver<Client, QB>) => void): this;
/**
* Set an or having not exists statement in your query.
*/
abstract orHavingNotExists(closure: (query: Driver<Client, QB>) => void): this;
/**
* Set an or having in statement in your query.
*/
abstract orHavingIn(column: string, values: any[]): this;
/**
* Set an or having not in statement in your query.
*/
abstract orHavingNotIn(column: string, values: any[]): this;
/**
* Set an or having between statement in your query.
*/
abstract orHavingBetween(column: string, values: [any, any]): this;
/**
* Set an or having not between statement in your query.
*/
abstract orHavingNotBetween(column: string, values: [any, any]): this;
/**
* Set an or having null statement in your query.
*/
abstract orHavingNull(column: string): this;
/**
* Set an or having not null statement in your query.
*/
abstract orHavingNotNull(column: string): this;
/**
* Set a where statement in your query.
*/
abstract where(statement: any, operation?: string | Operations, value?: any): this;
/**
* Set a where not statement in your query.
*/
abstract whereNot(statement: any, value?: any): this;
/**
* Set a where raw statement in your query.
*/
abstract whereRaw(sql: string, bindings?: any): this;
/**
* Set a where exists statement in your query.
*/
abstract whereExists(closure: (query: Driver<Client, QB>) => void): this;
/**
* Set a where not exists statement in your query.
*/
abstract whereNotExists(closure: (query: Driver<Client, QB>) => void): this;
/**
* Set a where like statement in your query.
*/
abstract whereLike(statement: any, value?: any): this;
/**
* Set a where ILike statement in your query.
*/
abstract whereILike(statement: any, value?: any): this;
/**
* Set a where in statement in your query.
*/
abstract whereIn(column: string, values: any[]): this;
/**
* Set a where not in statement in your query.
*/
abstract whereNotIn(column: string, values: any[]): this;
/**
* Set a where between statement in your query.
*/
abstract whereBetween(column: string, values: [any, any]): this;
/**
* Set a where not between statement in your query.
*/
abstract whereNotBetween(column: string, values: [any, any]): this;
/**
* Set a where null statement in your query.
*/
abstract whereNull(column: string): this;
/**
* Set a where not null statement in your query.
*/
abstract whereNotNull(column: string): this;
/**
* Set a or where statement in your query.
*/
abstract orWhere(statement: string | Record<string, any>, operation: string | Record<string, any>, value: Record<string, any>): this;
/**
* Set an or where not statement in your query.
*/
abstract orWhereNot(statement: string | Record<string, any>, value: any): this;
/**
* Set a or where raw statement in your query.
*
* @param sql {string}
* @param [bindings] {any}
* @return {MySqlDriver}
*/
abstract orWhereRaw(sql: string, bindings?: any): this;
/**
* Set an or where exists statement in your query.
*/
abstract orWhereExists(closure: (query: Driver<Client, QB>) => void): this;
/**
* Set an or where not exists statement in your query.
*/
abstract orWhereNotExists(closure: (query: Driver<Client, QB>) => void): this;
/**
* Set an or where like statement in your query.
*/
abstract orWhereLike(statement: any, value?: any): this;
/**
* Set an or where ILike statement in your query.
*/
abstract orWhereILike(statement: any, value?: any): this;
/**
* Set an or where in statement in your query.
*/
abstract orWhereIn(column: string, values: any[]): this;
/**
* Set an or where not in statement in your query.
*/
abstract orWhereNotIn(column: string, values: any[]): this;
/**
* Set an or where between statement in your query.
*/
abstract orWhereBetween(column: string, values: [any, any]): this;
/**
* Set an or where not between statement in your query.
*/
abstract orWhereNotBetween(column: string, values: [any, any]): this;
/**
* Set an or where null statement in your query.
*/
abstract orWhereNull(column: string): this;
/**
* Set an or where not null statement in your query.
*/
abstract orWhereNotNull(column: string): this;
/**
* Set an order by statement in your query.
*/
abstract orderBy(column: string, direction?: Direction): this;
/**
* Set an order by raw statement in your query.
*/
abstract orderByRaw(sql: string, bindings?: any): this;
/**
* Order the results easily by the latest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
abstract latest(column?: string): this;
/**
* Order the results easily by the oldest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
abstract oldest(column?: string): this;
/**
* Set the skip number in your query.
*/
abstract offset(number: number): this;
/**
* Set the limit number in your query.
*/
abstract limit(number: number): this;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Module, Options, Collection } from '@athenna/common';
import { NotFoundDataException } from '#src/exceptions/NotFoundDataException';
export class Driver {
/**
* Creates a new instance of the Driver.
*/
constructor(connection, client = null) {
/**
* Set the primary key of the driver.
*/
this.primaryKey = 'id';
/**
* Set if this instance is connected with database.
*/
this.isConnected = false;
/**
* Set if the connection will be saved on factory.
*/
this.isSavedOnFactory = false;
/**
* The connection name used for this instance.
*/
this.connection = null;
/**
* Set the table that this instance will work with.
*/
this.tableName = null;
/**
* Set the client of this driver.
*/
this.client = null;
/**
* The main query builder of driver.
*/
this.qb = null;
/**
* Use set query builder instead of creating a new one
* using client.
*/
this.useSetQB = false;
this.connection = connection;
if (client) {
this.client = client;
this.isConnected = true;
this.isSavedOnFactory = true;
}
}
/**
* Import knex in a method to be easier to mock.
*/
getKnex() {
const require = Module.createRequire(import.meta.url);
return require('knex');
}
/**
* Import mongoose in a method to be easier to mock.
*/
getMongoose() {
const require = Module.createRequire(import.meta.url);
let mongoose = require('mongoose');
if (!mongoose.createConnection) {
mongoose = mongoose.default;
}
return mongoose;
}
/**
* Clone the driver instance.
*/
clone() {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return new this.constructor(this.connection, this.client);
}
/**
* Return the client of driver.
*/
getClient() {
return this.client;
}
/**
* Set a client in driver.
*/
setClient(client) {
this.client = client;
return this;
}
/**
* Return the query builder of driver.
*/
getQueryBuilder() {
return this.qb;
}
/**
* Set a query builder in driver.
*/
setQueryBuilder(queryBuilder, options = {}) {
options = Options.create(options, {
useSetQB: false
});
this.qb = queryBuilder;
this.useSetQB = !this.qb ? false : options.useSetQB;
return this;
}
/**
* Set the primary key of the driver.
*/
setPrimaryKey(primaryKey) {
this.primaryKey = primaryKey;
return this;
}
/**
* Create a join clause by join type
*/
joinByType(joinType, table, column1, operation, column2) {
if (!column1) {
this.qb[joinType](table);
return this;
}
if (!operation) {
this.qb[joinType](table, column1);
return this;
}
if (!column2) {
this.qb[joinType](table, column1, operation);
return this;
}
this.qb[joinType](table, column1, operation, column2);
return this;
}
/**
* Find a value in database and return as boolean.
*/
async exists() {
const data = await this.find();
return !!data;
}
/**
* Find a value in database or throw exception if undefined.
*/
async findOrFail() {
const data = await this.find();
if (!data) {
throw new NotFoundDataException(this.connection);
}
return data;
}
/**
* Return a single model instance or, if no results are found,
* execute the given closure.
*/
async findOr(closure) {
const data = await this.find();
if (!data) {
return closure();
}
return data;
}
/**
* Find value in database but returns only the value of
* selected column directly.
*/
async pluck(column) {
const data = await this.find();
return data[column];
}
/**
* Find many values in database but returns only the
* values of selected column directly.
*/
async pluckMany(column) {
const data = await this.findMany();
return data.map(d => d[column]);
}
/**
* Find many values in database and return as a Collection.
*/
async collection() {
return new Collection(await this.findMany());
}
/**
* Executes the given closure when the first argument is true.
*/
when(criteria, closure) {
if (!criteria) {
return this;
}
closure(this, criteria);
return this;
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Collection, type PaginatedResponse } from '@athenna/common';
import type { ConnectionOptions, Operations } from '#src/types';
import { Transaction } from '#src/database/transactions/Transaction';
export declare class FakeDriver {
constructor(connection?: string, client?: any);
static primaryKey: string;
static tables: string[];
static databases: string[];
static isConnected: boolean;
static isSavedOnFactory: boolean;
static tableName: string;
static connection: string;
static client: any;
static qb: any;
static useSetQB: boolean;
static joinByType(): typeof FakeDriver;
static getKnex(): void;
static getMongoose(): void;
static clone(): typeof FakeDriver;
static setPrimaryKey(primaryKey: string): typeof FakeDriver;
static getClient(): any;
static setClient(client: any): typeof FakeDriver;
static getQueryBuilder(): any;
static setQueryBuilder(client: any): typeof FakeDriver;
/**
* Connect to database.
*/
static connect(options?: ConnectionOptions): void;
/**
* Close the connection with database in this instance.
*/
static close(): Promise<void>;
/**
* Creates a new instance of query builder.
*/
static query(): {};
/**
* Sync a model schema with database.
*/
static sync(): Promise<void>;
/**
* Create a new transaction.
*/
static startTransaction(): Promise<Transaction>;
/**
* Commit the transaction.
*/
static commitTransaction(): Promise<void>;
/**
* Rollback the transaction.
*/
static rollbackTransaction(): Promise<void>;
/**
* Run database migrations.
*/
static runMigrations(): Promise<void>;
/**
* Revert database migrations.
*/
static revertMigrations(): Promise<void>;
/**
* List all databases available.
*/
static getDatabases(): Promise<string[]>;
/**
* Get the current database name.
*/
static getCurrentDatabase(): Promise<string | undefined>;
/**
* Verify if database exists.
*/
static hasDatabase(database: string): Promise<boolean>;
/**
* Create a new database.
*/
static createDatabase(database: string): Promise<void>;
/**
* Drop some database.
*/
static dropDatabase(database: string): Promise<void>;
/**
* List all tables available.
*/
static getTables(): Promise<string[]>;
/**
* Verify if table exists.
*/
static hasTable(table: string): Promise<boolean>;
/**
* Create a new table in database.
*/
static createTable(table: string): Promise<void>;
/**
* Alter a table in database.
*/
static alterTable(): Promise<void>;
/**
* Drop a table in database.
*/
static dropTable(table: string): Promise<void>;
/**
* Remove all data inside some database table
* and restart the identity of the table.
*/
static truncate(table: string): Promise<void>;
/**
* Make a raw query in database.
*/
static raw(): any;
/**
* Calculate the average of a given column.
*/
static avg(): Promise<string>;
/**
* Calculate the average of a given column using distinct.
*/
static avgDistinct(): Promise<string>;
/**
* Get the max number of a given column.
*/
static max(): Promise<string>;
/**
* Get the min number of a given column.
*/
static min(): Promise<string>;
/**
* Sum all numbers of a given column.
*/
static sum(): Promise<string>;
/**
* Sum all numbers of a given column in distinct mode.
*/
static sumDistinct(): Promise<string>;
/**
* Increment a value of a given column.
*/
static increment(): Promise<void>;
/**
* Decrement a value of a given column.
*/
static decrement(): Promise<void>;
/**
* Calculate the average of a given column using distinct.
*/
static count(): Promise<string>;
/**
* Calculate the average of a given column using distinct.
*/
static countDistinct(): Promise<string>;
/**
* Find value in database but returns only the value of
* selected column directly.
*/
static pluck(): Promise<any>;
/**
* Find many values in database but returns only the
* values of selected column directly.
*/
static pluckMany(): Promise<any>;
/**
* Find a value in database.
*/
static find(): Promise<any>;
/**
* Find a value in database and return as boolean.
*/
static exists(): Promise<boolean>;
/**
* Find a value in database or fail.
*/
static findOrFail(): Promise<any>;
/**
* Find a value in database or execute closure.
*/
static findOr(): Promise<any>;
/**
* Executes the given closure when the first argument is true.
*/
static when(criteria: any, closure: (query: typeof FakeDriver, criteriaValue?: any) => void): typeof FakeDriver;
/**
* Find many values in database.
*/
static findMany(): Promise<any[]>;
/**
* Find many values in database and return as a Collection.
*/
static collection(): Promise<Collection<any>>;
/**
* Find many values in database and return as paginated response.
*/
static paginate<T = any>(page?: {
page: number;
limit: number;
resourceUrl: string;
}, limit?: number, resourceUrl?: string): Promise<PaginatedResponse<T>>;
/**
* Create a value in database.
*/
static create<T = any>(data?: Partial<T>): Promise<T>;
/**
* Create many values in database.
*/
static createMany<T = any>(data?: Partial<T>[]): Promise<T[]>;
/**
* Create data or update if already exists.
*/
static createOrUpdate<T = any>(data?: Partial<T>): Promise<T | T[]>;
/**
* Update a value in database.
*/
static update<T = any>(data: Partial<T>): Promise<T | T[]>;
/**
* Delete one value in database.
*/
static delete(): Promise<void>;
/**
* Set the table that this query will be executed.
*/
static table(table: string): typeof FakeDriver;
/**
* Log in console the actual query built.
*/
static dump(): typeof FakeDriver;
/**
* Set the columns that should be selected on query.
*/
static select(...columns: string[]): typeof FakeDriver;
/**
* Set the columns that should be selected on query raw.
*/
static selectRaw(): typeof FakeDriver;
/**
* Set the table that should be used on query.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
static from(): typeof FakeDriver;
/**
* Set the table that should be used on query raw.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
static fromRaw(): typeof FakeDriver;
/**
* Set a join statement in your query.
*/
static join(): typeof FakeDriver;
/**
* Set a left join statement in your query.
*/
static leftJoin(): typeof FakeDriver;
/**
* Set a right join statement in your query.
*/
static rightJoin(): typeof FakeDriver;
/**
* Set a cross join statement in your query.
*/
static crossJoin(): typeof FakeDriver;
/**
* Set a full outer join statement in your query.
*/
static fullOuterJoin(): typeof FakeDriver;
/**
* Set a left outer join statement in your query.
*/
static leftOuterJoin(): typeof FakeDriver;
/**
* Set a right outer join statement in your query.
*/
static rightOuterJoin(): typeof FakeDriver;
/**
* Set a join raw statement in your query.
*/
static joinRaw(): typeof FakeDriver;
/**
* Set a group by statement in your query.
*/
static groupBy(): typeof FakeDriver;
/**
* Set a group by raw statement in your query.
*/
static groupByRaw(): typeof FakeDriver;
static having(column: string): typeof FakeDriver;
static having(column: string, value: any): typeof FakeDriver;
static having(column: string, operation: Operations, value: any): typeof FakeDriver;
/**
* Set a having raw statement in your query.
*/
static havingRaw(): typeof FakeDriver;
/**
* Set a having exists statement in your query.
*/
static havingExists(): typeof FakeDriver;
/**
* Set a having not exists statement in your query.
*/
static havingNotExists(): typeof FakeDriver;
/**
* Set a having in statement in your query.
*/
static havingIn(): typeof FakeDriver;
/**
* Set a having not in statement in your query.
*/
static havingNotIn(): typeof FakeDriver;
/**
* Set a having between statement in your query.
*/
static havingBetween(): typeof FakeDriver;
/**
* Set a having not between statement in your query.
*/
static havingNotBetween(): typeof FakeDriver;
/**
* Set a having null statement in your query.
*/
static havingNull(): typeof FakeDriver;
/**
* Set a having not null statement in your query.
*/
static havingNotNull(): typeof FakeDriver;
static orHaving(column: string): typeof FakeDriver;
static orHaving(column: string, value: any): typeof FakeDriver;
static orHaving(column: string, operation: Operations, value: any): typeof FakeDriver;
/**
* Set an or having raw statement in your query.
*/
static orHavingRaw(): typeof FakeDriver;
/**
* Set an or having exists statement in your query.
*/
static orHavingExists(): typeof FakeDriver;
/**
* Set an or having not exists statement in your query.
*/
static orHavingNotExists(): typeof FakeDriver;
/**
* Set an or having in statement in your query.
*/
static orHavingIn(): typeof FakeDriver;
/**
* Set an or having not in statement in your query.
*/
static orHavingNotIn(): typeof FakeDriver;
/**
* Set an or having between statement in your query.
*/
static orHavingBetween(): typeof FakeDriver;
/**
* Set an or having not between statement in your query.
*/
static orHavingNotBetween(): typeof FakeDriver;
/**
* Set an or having null statement in your query.
*/
static orHavingNull(): typeof FakeDriver;
/**
* Set an or having not null statement in your query.
*/
static orHavingNotNull(): typeof FakeDriver;
static where(statement: Record<string, any>): typeof FakeDriver;
static where(key: string, value: any): typeof FakeDriver;
static where(key: string, operation: Operations, value: any): typeof FakeDriver;
static whereNot(statement: Record<string, any>): any;
static whereNot(key: string, value: any): any;
/**
* Set a where raw statement in your query.
*/
static whereRaw(): typeof FakeDriver;
/**
* Set a where exists statement in your query.
*/
static whereExists(): typeof FakeDriver;
/**
* Set a where not exists statement in your query.
*/
static whereNotExists(): typeof FakeDriver;
/**
* Set a where like statement in your query.
*/
static whereLike(): typeof FakeDriver;
/**
* Set a where ILike statement in your query.
*/
static whereILike(): typeof FakeDriver;
/**
* Set a where in statement in your query.
*/
static whereIn(): typeof FakeDriver;
/**
* Set a where not in statement in your query.
*/
static whereNotIn(): typeof FakeDriver;
/**
* Set a where between statement in your query.
*/
static whereBetween(): typeof FakeDriver;
/**
* Set a where not between statement in your query.
*/
static whereNotBetween(): typeof FakeDriver;
/**
* Set a where null statement in your query.
*/
static whereNull(): typeof FakeDriver;
/**
* Set a where not null statement in your query.
*/
static whereNotNull(): typeof FakeDriver;
static orWhere(statement: Record<string, any>): typeof FakeDriver;
static orWhere(key: string, value: any): typeof FakeDriver;
static orWhere(key: string, operation: Operations, value: any): typeof FakeDriver;
static orWhereNot(statement: Record<string, any>): typeof FakeDriver;
static orWhereNot(key: string, value: any): typeof FakeDriver;
/**
* Set a or where raw statement in your query.
*/
static orWhereRaw(): typeof FakeDriver;
/**
* Set an or where exists statement in your query.
*/
static orWhereExists(): typeof FakeDriver;
/**
* Set an or where not exists statement in your query.
*/
static orWhereNotExists(): typeof FakeDriver;
/**
* Set an or where like statement in your query.
*/
static orWhereLike(): typeof FakeDriver;
/**
* Set an or where ILike statement in your query.
*/
static orWhereILike(): typeof FakeDriver;
/**
* Set an or where in statement in your query.
*/
static orWhereIn(): typeof FakeDriver;
/**
* Set an or where not in statement in your query.
*/
static orWhereNotIn(): typeof FakeDriver;
/**
* Set an or where between statement in your query.
*/
static orWhereBetween(): typeof FakeDriver;
/**
* Set an or where not between statement in your query.
*/
static orWhereNotBetween(): typeof FakeDriver;
/**
* Set an or where null statement in your query.
*/
static orWhereNull(): typeof FakeDriver;
/**
* Set an or where not null statement in your query.
*/
static orWhereNotNull(): typeof FakeDriver;
/**
* Set an order by statement in your query.
*/
static orderBy(): typeof FakeDriver;
/**
* Set an order by raw statement in your query.
*/
static orderByRaw(): typeof FakeDriver;
/**
* Order the results easily by the latest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
static latest(): typeof FakeDriver;
/**
* Order the results easily by the oldest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
static oldest(): typeof FakeDriver;
/**
* Set the skip number in your query.
*/
static offset(): typeof FakeDriver;
/**
* Set the limit number in your query.
*/
static limit(): typeof FakeDriver;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Collection, Exec, Is, Json, Options } from '@athenna/common';
import { Transaction } from '#src/database/transactions/Transaction';
import { WrongMethodException } from '#src/exceptions/WrongMethodException';
import { NotFoundDataException } from '#src/exceptions/NotFoundDataException';
import { NotConnectedDatabaseException } from '#src/exceptions/NotConnectedDatabaseException';
export class FakeDriver {
constructor(connection, client) {
FakeDriver.connection = connection;
if (client) {
FakeDriver.client = client;
FakeDriver.isConnected = true;
FakeDriver.isSavedOnFactory = true;
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return FakeDriver;
}
static { this.primaryKey = 'id'; }
static { this.tables = ['fake']; }
static { this.databases = ['fake']; }
static { this.isConnected = false; }
static { this.isSavedOnFactory = false; }
static { this.tableName = 'fake'; }
static { this.connection = 'fake'; }
static { this.client = null; }
static { this.qb = null; }
static { this.useSetQB = false; }
static joinByType() {
return this;
}
static getKnex() { }
static getMongoose() { }
static clone() {
return Json.copy(FakeDriver);
}
static setPrimaryKey(primaryKey) {
this.primaryKey = primaryKey;
return this;
}
static getClient() {
return this.client;
}
static setClient(client) {
this.client = client;
return this;
}
static getQueryBuilder() {
return this.client;
}
static setQueryBuilder(client) {
this.client = client;
return this;
}
/**
* Connect to database.
*/
static connect(options = {}) {
options = Options.create(options, {
force: false,
saveOnFactory: true,
connect: true
});
if (!options.connect) {
return;
}
if (this.isConnected && !options.force) {
return;
}
this.isConnected = true;
this.isSavedOnFactory = options.saveOnFactory;
}
/**
* Close the connection with database in this instance.
*/
static async close() {
if (!this.isConnected) {
return;
}
this.tableName = null;
this.isConnected = false;
}
/**
* Creates a new instance of query builder.
*/
static query() {
if (!this.isConnected) {
throw new NotConnectedDatabaseException();
}
return {};
}
/**
* Sync a model schema with database.
*/
static async sync() { }
/**
* Create a new transaction.
*/
static async startTransaction() {
return new Transaction(FakeDriver);
}
/**
* Commit the transaction.
*/
static async commitTransaction() {
this.tableName = null;
this.client = null;
this.isConnected = false;
}
/**
* Rollback the transaction.
*/
static async rollbackTransaction() {
this.tableName = null;
this.client = null;
this.isConnected = false;
}
/**
* Run database migrations.
*/
static async runMigrations() { }
/**
* Revert database migrations.
*/
static async revertMigrations() { }
/**
* List all databases available.
*/
static async getDatabases() {
return this.databases;
}
/**
* Get the current database name.
*/
static async getCurrentDatabase() {
return this.databases[0];
}
/**
* Verify if database exists.
*/
static async hasDatabase(database) {
const databases = await this.getDatabases();
return databases.includes(database);
}
/**
* Create a new database.
*/
static async createDatabase(database) {
this.databases.push(database);
}
/**
* Drop some database.
*/
static async dropDatabase(database) {
const index = this.databases.indexOf(database);
this.databases.splice(index, 1);
}
/**
* List all tables available.
*/
static async getTables() {
return this.tables;
}
/**
* Verify if table exists.
*/
static async hasTable(table) {
const tables = await this.getTables();
return tables.includes(table);
}
/**
* Create a new table in database.
*/
static async createTable(table) {
this.tables.push(table);
}
/**
* Alter a table in database.
*/
static async alterTable() { }
/**
* Drop a table in database.
*/
static async dropTable(table) {
const index = this.tables.indexOf(table);
this.tables.splice(index, 1);
}
/**
* Remove all data inside some database table
* and restart the identity of the table.
*/
static async truncate(table) {
this.tables.indexOf(table);
}
/**
* Make a raw query in database.
*/
static raw() {
return {};
}
/**
* Calculate the average of a given column.
*/
static async avg() {
return '1';
}
/**
* Calculate the average of a given column using distinct.
*/
static async avgDistinct() {
return '1';
}
/**
* Get the max number of a given column.
*/
static async max() {
return '1';
}
/**
* Get the min number of a given column.
*/
static async min() {
return '1';
}
/**
* Sum all numbers of a given column.
*/
static async sum() {
return '1';
}
/**
* Sum all numbers of a given column in distinct mode.
*/
static async sumDistinct() {
return '1';
}
/**
* Increment a value of a given column.
*/
static async increment() { }
/**
* Decrement a value of a given column.
*/
static async decrement() { }
/**
* Calculate the average of a given column using distinct.
*/
static async count() {
return '1';
}
/**
* Calculate the average of a given column using distinct.
*/
static async countDistinct() {
return '1';
}
/**
* Find value in database but returns only the value of
* selected column directly.
*/
static async pluck() {
return '';
}
/**
* Find many values in database but returns only the
* values of selected column directly.
*/
static async pluckMany() {
return [''];
}
/**
* Find a value in database.
*/
static async find() {
return {};
}
/**
* Find a value in database and return as boolean.
*/
static async exists() {
const data = await this.find();
return !!data;
}
/**
* Find a value in database or fail.
*/
static async findOrFail() {
const data = await this.find();
if (!data) {
throw new NotFoundDataException(this.connection);
}
return data;
}
/**
* Find a value in database or execute closure.
*/
static async findOr() {
return {};
}
/**
* Executes the given closure when the first argument is true.
*/
static when(criteria, closure) {
if (!criteria) {
return this;
}
closure(this, criteria);
return this;
}
/**
* Find many values in database.
*/
static async findMany() {
return [{}];
}
/**
* Find many values in database and return as a Collection.
*/
static async collection() {
return new Collection(await this.findMany());
}
/**
* Find many values in database and return as paginated response.
*/
static async paginate(page = { page: 0, limit: 10, resourceUrl: '/' }, limit = 10, resourceUrl = '/') {
if (Is.Number(page)) {
page = { page, limit, resourceUrl };
}
const data = await this.findMany();
return Exec.pagination(data, data.length, page);
}
/**
* Create a value in database.
*/
static async create(data = {}) {
if (Is.Array(data)) {
throw new WrongMethodException('create', 'createMany');
}
const created = await this.createMany([data]);
return created[0];
}
/**
* Create many values in database.
*/
static async createMany(data = []) {
if (!Is.Array(data)) {
throw new WrongMethodException('createMany', 'create');
}
return data;
}
/**
* Create data or update if already exists.
*/
static async createOrUpdate(data = {}) {
return data;
}
/**
* Update a value in database.
*/
static async update(data) {
return data;
}
/**
* Delete one value in database.
*/
static async delete() { }
/**
* Set the table that this query will be executed.
*/
static table(table) {
if (!this.isConnected) {
throw new NotConnectedDatabaseException();
}
this.tableName = table;
return this;
}
/**
* Log in console the actual query built.
*/
static dump() {
return this;
}
/**
* Set the columns that should be selected on query.
*/
static select(...columns) {
if (columns.includes('*')) {
return this;
}
return this;
}
/**
* Set the columns that should be selected on query raw.
*/
static selectRaw() {
return this;
}
/**
* Set the table that should be used on query.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
static from() {
return this;
}
/**
* Set the table that should be used on query raw.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
static fromRaw() {
return this;
}
/**
* Set a join statement in your query.
*/
static join() {
return this;
}
/**
* Set a left join statement in your query.
*/
static leftJoin() {
return this;
}
/**
* Set a right join statement in your query.
*/
static rightJoin() {
return this;
}
/**
* Set a cross join statement in your query.
*/
static crossJoin() {
return this;
}
/**
* Set a full outer join statement in your query.
*/
static fullOuterJoin() {
return this;
}
/**
* Set a left outer join statement in your query.
*/
static leftOuterJoin() {
return this;
}
/**
* Set a right outer join statement in your query.
*/
static rightOuterJoin() {
return this;
}
/**
* Set a join raw statement in your query.
*/
static joinRaw() {
return this;
}
/**
* Set a group by statement in your query.
*/
static groupBy() {
return this;
}
/**
* Set a group by raw statement in your query.
*/
static groupByRaw() {
return this;
}
/**
* Set a having statement in your query.
*/
static having() {
return this;
}
/**
* Set a having raw statement in your query.
*/
static havingRaw() {
return this;
}
/**
* Set a having exists statement in your query.
*/
static havingExists() {
return this;
}
/**
* Set a having not exists statement in your query.
*/
static havingNotExists() {
return this;
}
/**
* Set a having in statement in your query.
*/
static havingIn() {
return this;
}
/**
* Set a having not in statement in your query.
*/
static havingNotIn() {
return this;
}
/**
* Set a having between statement in your query.
*/
static havingBetween() {
return this;
}
/**
* Set a having not between statement in your query.
*/
static havingNotBetween() {
return this;
}
/**
* Set a having null statement in your query.
*/
static havingNull() {
return this;
}
/**
* Set a having not null statement in your query.
*/
static havingNotNull() {
return this;
}
/**
* Set an or having statement in your query.
*/
static orHaving() {
return this;
}
/**
* Set an or having raw statement in your query.
*/
static orHavingRaw() {
return this;
}
/**
* Set an or having exists statement in your query.
*/
static orHavingExists() {
return this;
}
/**
* Set an or having not exists statement in your query.
*/
static orHavingNotExists() {
return this;
}
/**
* Set an or having in statement in your query.
*/
static orHavingIn() {
return this;
}
/**
* Set an or having not in statement in your query.
*/
static orHavingNotIn() {
return this;
}
/**
* Set an or having between statement in your query.
*/
static orHavingBetween() {
return this;
}
/**
* Set an or having not between statement in your query.
*/
static orHavingNotBetween() {
return this;
}
/**
* Set an or having null statement in your query.
*/
static orHavingNull() {
return this;
}
/**
* Set an or having not null statement in your query.
*/
static orHavingNotNull() {
return this;
}
/**
* Set a where statement in your query.
*/
static where() {
return this;
}
/**
* Set a where not statement in your query.
*/
static whereNot() {
return this;
}
/**
* Set a where raw statement in your query.
*/
static whereRaw() {
return this;
}
/**
* Set a where exists statement in your query.
*/
static whereExists() {
return this;
}
/**
* Set a where not exists statement in your query.
*/
static whereNotExists() {
return this;
}
/**
* Set a where like statement in your query.
*/
static whereLike() {
return this;
}
/**
* Set a where ILike statement in your query.
*/
static whereILike() {
return this;
}
/**
* Set a where in statement in your query.
*/
static whereIn() {
return this;
}
/**
* Set a where not in statement in your query.
*/
static whereNotIn() {
return this;
}
/**
* Set a where between statement in your query.
*/
static whereBetween() {
return this;
}
/**
* Set a where not between statement in your query.
*/
static whereNotBetween() {
return this;
}
/**
* Set a where null statement in your query.
*/
static whereNull() {
return this;
}
/**
* Set a where not null statement in your query.
*/
static whereNotNull() {
return this;
}
/**
* Set a or where statement in your query.
*/
static orWhere() {
return this;
}
/**
* Set an or where not statement in your query.
*/
static orWhereNot() {
return this;
}
/**
* Set a or where raw statement in your query.
*/
static orWhereRaw() {
return this;
}
/**
* Set an or where exists statement in your query.
*/
static orWhereExists() {
return this;
}
/**
* Set an or where not exists statement in your query.
*/
static orWhereNotExists() {
return this;
}
/**
* Set an or where like statement in your query.
*/
static orWhereLike() {
return this;
}
/**
* Set an or where ILike statement in your query.
*/
static orWhereILike() {
return this;
}
/**
* Set an or where in statement in your query.
*/
static orWhereIn() {
return this;
}
/**
* Set an or where not in statement in your query.
*/
static orWhereNotIn() {
return this;
}
/**
* Set an or where between statement in your query.
*/
static orWhereBetween() {
return this;
}
/**
* Set an or where not between statement in your query.
*/
static orWhereNotBetween() {
return this;
}
/**
* Set an or where null statement in your query.
*/
static orWhereNull() {
return this;
}
/**
* Set an or where not null statement in your query.
*/
static orWhereNotNull() {
return this;
}
/**
* Set an order by statement in your query.
*/
static orderBy() {
return this;
}
/**
* Set an order by raw statement in your query.
*/
static orderByRaw() {
return this;
}
/**
* Order the results easily by the latest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
static latest() {
return this;
}
/**
* Order the results easily by the oldest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
static oldest() {
return this;
}
/**
* Set the skip number in your query.
*/
static offset() {
return this;
}
/**
* Set the limit number in your query.
*/
static limit() {
return this;
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { type PaginatedResponse, type PaginationOptions } from '@athenna/common';
import { Driver } from '#src/database/drivers/Driver';
import { ModelSchema } from '#src/models/schemas/ModelSchema';
import { Transaction } from '#src/database/transactions/Transaction';
import type { Connection, Collection, ClientSession } from 'mongoose';
import type { ConnectionOptions, Direction, Operations } from '#src/types';
export declare class MongoDriver extends Driver<Connection, Collection> {
primaryKey: string;
session: ClientSession;
/**
* The where clause used in update queries.
*/
private _where;
/**
* The or where clause used in update queries.
*/
private _orWhere;
/**
* The aggregate pipeline to make mongo queries.
*/
private pipeline;
/**
* Set the mongo session that should be used by the driver.
*/
setSession(session: ClientSession): this;
/**
* Connect to database.
*/
connect(options?: ConnectionOptions): void;
/**
* Close the connection with database in this instance.
*/
close(): Promise<void>;
/**
* Creates a new instance of query builder.
*/
query(): Collection;
/**
* Sync a model schema with database.
*/
sync(schema: ModelSchema): Promise<void>;
/**
* Create a new transaction.
*/
startTransaction(): Promise<Transaction<Connection, Collection>>;
/**
* Commit the transaction.
*/
commitTransaction(): Promise<void>;
/**
* Rollback the transaction.
*/
rollbackTransaction(): Promise<void>;
/**
* Run database migrations.
*/
runMigrations(): Promise<void>;
/**
* Revert database migrations.
*/
revertMigrations(): Promise<void>;
/**
* List all databases available.
*/
getDatabases(): Promise<string[]>;
/**
* Get the current database name.
*/
getCurrentDatabase(): Promise<string | undefined>;
/**
* Verify if database exists.
*/
hasDatabase(database: string): Promise<boolean>;
/**
* Create a new database.
*/
createDatabase(): Promise<void>;
/**
* Drop some database.
*/
dropDatabase(database: string): Promise<void>;
/**
* List all tables available.
*/
getTables(): Promise<string[]>;
/**
* Verify if table exists.
*/
hasTable(table: string): Promise<boolean>;
/**
* Create a new table in database.
*/
createTable(): Promise<void>;
/**
* Alter a table in database.
*/
alterTable(): Promise<void>;
/**
* Drop a table in database.
*/
dropTable(table: string): Promise<void>;
/**
* Remove all data inside some database table
* and restart the identity of the table.
*/
truncate(table: string): Promise<void>;
/**
* Make a raw query in database.
*/
raw<T = any>(): T;
/**
* Calculate the average of a given column.
*/
avg(column: string): Promise<string>;
/**
* Calculate the average of a given column using distinct.
*/
avgDistinct(column: string): Promise<string>;
/**
* Get the max number of a given column.
*/
max(column: string): Promise<string>;
/**
* Get the min number of a given column.
*/
min(column: string): Promise<string>;
/**
* Sum all numbers of a given column.
*/
sum(column: string): Promise<string>;
/**
* Sum all numbers of a given column in distinct mode.
*/
sumDistinct(column: string): Promise<string>;
/**
* Increment a value of a given column.
*/
increment(column: string): Promise<void>;
/**
* Decrement a value of a given column.
*/
decrement(column: string): Promise<void>;
/**
* Calculate the average of a given column using distinct.
*/
count(column?: string): Promise<string>;
/**
* Calculate the average of a given column using distinct.
*/
countDistinct(column: string): Promise<string>;
/**
* Find a value in database.
*/
find<T = any>(): Promise<T>;
/**
* Find many values in database.
*/
findMany<T = any>(): Promise<T[]>;
/**
* Find many values in database and return as paginated response.
*/
paginate<T = any>(page?: PaginationOptions | number, limit?: number, resourceUrl?: string): Promise<PaginatedResponse<T>>;
/**
* Create a value in database.
*/
create<T = any>(data?: Partial<T>): Promise<T>;
/**
* Create many values in database.
*/
createMany<T = any>(data?: Partial<T>[]): Promise<T[]>;
/**
* Create data or update if already exists.
*/
createOrUpdate<T = any>(data?: Partial<T>): Promise<T | T[]>;
/**
* Update a value in database.
*/
update<T = any>(data: Partial<T>): Promise<T | T[]>;
/**
* Delete one value in database.
*/
delete(): Promise<void>;
/**
* Set the table that this query will be executed.
*/
table(table: string): this;
/**
* Log in console the actual query built.
*/
dump(): this;
/**
* Set the columns that should be selected on query.
*/
select(...columns: string[]): this;
/**
* Set the columns that should be selected on query raw.
*/
selectRaw(): this;
/**
* Set the table that should be used on query.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
from(): this;
/**
* Set the table that should be used on query raw.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
fromRaw(): this;
/**
* Set a join statement in your query.
*/
join(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a left join statement in your query.
*/
leftJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a right join statement in your query.
*/
rightJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a cross join statement in your query.
*/
crossJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a full outer join statement in your query.
*/
fullOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a left outer join statement in your query.
*/
leftOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a right outer join statement in your query.
*/
rightOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a join raw statement in your query.
*/
joinRaw(): this;
/**
* Set a group by statement in your query.
*/
groupBy(...columns: string[]): this;
/**
* Set a group by raw statement in your query.
*/
groupByRaw(): this;
having(column: string): this;
having(column: string, value: any): this;
having(column: string, operation: Operations, value: any): this;
/**
* Set a having raw statement in your query.
*/
havingRaw(): this;
/**
* Set a having exists statement in your query.
*/
havingExists(): this;
/**
* Set a having not exists statement in your query.
*/
havingNotExists(): this;
/**
* Set a having in statement in your query.
*/
havingIn(column: string, values: any[]): this;
/**
* Set a having not in statement in your query.
*/
havingNotIn(column: string, values: any[]): this;
/**
* Set a having between statement in your query.
*/
havingBetween(column: string, values: [any, any]): this;
/**
* Set a having not between statement in your query.
*/
havingNotBetween(column: string, values: [any, any]): this;
/**
* Set a having null statement in your query.
*/
havingNull(column: string): this;
/**
* Set a having not null statement in your query.
*/
havingNotNull(column: string): this;
orHaving(column: string): this;
orHaving(column: string, value: any): this;
orHaving(column: string, operation: Operations, value: any): this;
/**
* Set an or having raw statement in your query.
*/
orHavingRaw(): this;
/**
* Set an or having exists statement in your query.
*/
orHavingExists(): this;
/**
* Set an or having not exists statement in your query.
*/
orHavingNotExists(): this;
/**
* Set an or having in statement in your query.
*/
orHavingIn(column: string, values: any[]): this;
/**
* Set an or having not in statement in your query.
*/
orHavingNotIn(column: string, values: any[]): this;
/**
* Set an or having between statement in your query.
*/
orHavingBetween(column: string, values: [any, any]): this;
/**
* Set an or having not between statement in your query.
*/
orHavingNotBetween(column: string, values: [any, any]): this;
/**
* Set an or having null statement in your query.
*/
orHavingNull(column: string): this;
/**
* Set an or having not null statement in your query.
*/
orHavingNotNull(column: string): this;
where(statement: Record<string, any>): this;
where(key: string, value: any): this;
where(key: string, operation: Operations, value: any): this;
whereNot(statement: Record<string, any>): this;
whereNot(key: string, value: any): this;
/**
* Set a where raw statement in your query.
*/
whereRaw(): this;
/**
* Set a where exists statement in your query.
*/
whereExists(): this;
/**
* Set a where not exists statement in your query.
*/
whereNotExists(): this;
/**
* Set a where like statement in your query.
*/
whereLike(column: string, value: any): this;
/**
* Set a where ILike statement in your query.
*/
whereILike(column: string, value: any): this;
/**
* Set a where in statement in your query.
*/
whereIn(column: string, values: any[]): this;
/**
* Set a where not in statement in your query.
*/
whereNotIn(column: string, values: any[]): this;
/**
* Set a where between statement in your query.
*/
whereBetween(column: string, values: [any, any]): this;
/**
* Set a where not between statement in your query.
*/
whereNotBetween(column: string, values: [any, any]): this;
/**
* Set a where null statement in your query.
*/
whereNull(column: string): this;
/**
* Set a where not null statement in your query.
*/
whereNotNull(column: string): this;
orWhere(statement: Record<string, any>): this;
orWhere(key: string, value: any): this;
orWhere(key: string, operation: Operations, value: any): this;
orWhereNot(statement: Record<string, any>): this;
orWhereNot(key: string, value: any): this;
/**
* Set a or where raw statement in your query.
*/
orWhereRaw(): this;
/**
* Set an or where exists statement in your query.
*/
orWhereExists(): this;
/**
* Set an or where not exists statement in your query.
*/
orWhereNotExists(): this;
/**
* Set an or where like statement in your query.
*/
orWhereLike(column: string, value: any): this;
/**
* Set an or where ILike statement in your query.
*/
orWhereILike(column: string, value: any): this;
/**
* Set an or where in statement in your query.
*/
orWhereIn(column: string, values: any[]): this;
/**
* Set an or where not in statement in your query.
*/
orWhereNotIn(column: string, values: any[]): this;
/**
* Set an or where between statement in your query.
*/
orWhereBetween(column: string, values: [any, any]): this;
/**
* Set an or where not between statement in your query.
*/
orWhereNotBetween(column: string, values: [any, any]): this;
/**
* Set an or where null statement in your query.
*/
orWhereNull(column: string): this;
/**
* Set an or where not null statement in your query.
*/
orWhereNotNull(column: string): this;
/**
* Set an order by statement in your query.
*/
orderBy(column: string, direction?: Direction): this;
/**
* Set an order by raw statement in your query.
*/
orderByRaw(): this;
/**
* Order the results easily by the latest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
latest(column?: string): this;
/**
* Order the results easily by the oldest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
oldest(column?: string): this;
/**
* Set the skip number in your query.
*/
offset(number: number): this;
/**
* Set the limit number in your query.
*/
limit(number: number): this;
/**
* Set the mongo operation in value.
*/
private setOperator;
/**
* Creates the where clause with where and orWhere.
*/
private createWhere;
/**
* Creates the aggregation pipeline.
*/
private createPipeline;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Is, Json, Exec, Options } from '@athenna/common';
import { debug } from '#src/debug';
import { Log } from '@athenna/logger';
import { ObjectId } from '#src/helpers/ObjectId';
import { Driver } from '#src/database/drivers/Driver';
import { ModelSchema } from '#src/models/schemas/ModelSchema';
import { Transaction } from '#src/database/transactions/Transaction';
import { ConnectionFactory } from '#src/factories/ConnectionFactory';
import { WrongMethodException } from '#src/exceptions/WrongMethodException';
import { MONGO_OPERATIONS_DICTIONARY } from '#src/constants/MongoOperationsDictionary';
import { NotConnectedDatabaseException } from '#src/exceptions/NotConnectedDatabaseException';
import { NotImplementedMethodException } from '#src/exceptions/NotImplementedMethodException';
export class MongoDriver extends Driver {
constructor() {
super(...arguments);
this.primaryKey = '_id';
this.session = null;
/**
* The where clause used in update queries.
*/
this._where = [];
/**
* The or where clause used in update queries.
*/
this._orWhere = [];
/**
* The aggregate pipeline to make mongo queries.
*/
this.pipeline = [];
}
/**
* Set the mongo session that should be used by the driver.
*/
setSession(session) {
this.session = session;
return this;
}
/**
* Connect to database.
*/
connect(options = {}) {
options = Options.create(options, {
force: false,
saveOnFactory: true,
connect: true
});
if (!options.connect) {
return;
}
if (this.isConnected && !options.force) {
return;
}
const mongoose = this.getMongoose();
const configs = Config.get(`database.connections.${this.connection}`, {});
if (configs.debug !== undefined) {
mongoose.set('debug', configs.debug);
}
const mongoOpts = Json.omit(configs, [
'url',
'debug',
'driver',
'validations'
]);
debug('creating new connection using mongoose. options defined: %o', {
url: configs.url,
debug: configs.debug,
...mongoOpts
});
this.client = mongoose.createConnection(configs.url, mongoOpts);
this.client.on('connected', () => {
if (Config.is('rc.bootLogs', true)) {
Log.channelOrVanilla('application').success(`Successfully connected to ({yellow} ${this.connection}) database connection`);
}
});
this.isConnected = true;
this.isSavedOnFactory = options.saveOnFactory;
if (this.isSavedOnFactory) {
ConnectionFactory.setClient(this.connection, this.client);
}
this.qb = this.query();
}
/**
* Close the connection with database in this instance.
*/
async close() {
if (!this.isConnected) {
return;
}
await this.client.close();
this.qb = null;
this.tableName = null;
this.client = null;
this.session = null;
this.isConnected = false;
ConnectionFactory.setClient(this.connection, null);
}
/**
* Creates a new instance of query builder.
*/
query() {
if (!this.isConnected) {
throw new NotConnectedDatabaseException();
}
return this.client.collection(this.tableName || 'lock');
}
/**
* Sync a model schema with database.
*/
async sync(schema) {
const columns = {};
const mongoose = await import('mongoose');
schema.columns.forEach(column => {
columns[column.name] = {};
if (column.type !== undefined) {
columns[column.name].type = column.type;
}
if (column.isNullable !== undefined) {
columns[column.name].required = column.isNullable;
}
if (column.length !== undefined) {
columns[column.name].maxLength = column.length;
}
if (column.defaultTo !== undefined) {
columns[column.name].default = column.defaultTo;
}
if (column.isIndex !== undefined) {
columns[column.name].index = column.isIndex;
}
if (column.isSparse !== undefined) {
columns[column.name].sparse = column.isSparse;
}
if (column.isUnique !== undefined) {
columns[column.name].unique = column.isUnique;
}
if (columns[column.name].unique || columns[column.name].sparse) {
columns[column.name].index = true;
}
});
/**
* Relations will not be registered because
* Athenna will handle them instead of mongoose.
*/
return this.client
.model(schema.getModelName(), new mongoose.Schema(columns))
.createIndexes();
}
/**
* Create a new transaction.
*/
async startTransaction() {
await this.client.asPromise();
const session = await this.client.startSession();
session.startTransaction();
return new Transaction(new MongoDriver(this.connection, this.client).setSession(session));
}
/**
* Commit the transaction.
*/
async commitTransaction() {
await this.client.asPromise();
await this.session.commitTransaction();
await this.session.endSession();
this.tableName = null;
this.client = null;
this.session = null;
this.isConnected = false;
}
/**
* Rollback the transaction.
*/
async rollbackTransaction() {
await this.client.asPromise();
await this.session.abortTransaction();
await this.session.endSession();
this.tableName = null;
this.client = null;
this.session = null;
this.isConnected = false;
}
/**
* Run database migrations.
*/
async runMigrations() {
throw new NotImplementedMethodException(this.runMigrations.name, 'mongo');
}
/**
* Revert database migrations.
*/
async revertMigrations() {
throw new NotImplementedMethodException(this.revertMigrations.name, 'mongo');
}
/**
* List all databases available.
*/
async getDatabases() {
await this.client.asPromise();
const admin = this.client.db.admin();
const { databases } = await admin.listDatabases();
return databases.map(database => database.name);
}
/**
* Get the current database name.
*/
async getCurrentDatabase() {
await this.client.asPromise();
return this.client.db.databaseName;
}
/**
* Verify if database exists.
*/
async hasDatabase(database) {
await this.client.asPromise();
const databases = await this.getDatabases();
return databases.includes(database);
}
/**
* Create a new database.
*/
async createDatabase() {
throw new NotImplementedMethodException(this.createDatabase.name, 'mongo');
}
/**
* Drop some database.
*/
async dropDatabase(database) {
await this.client.asPromise();
await this.client.useDb(database).dropDatabase();
}
/**
* List all tables available.
*/
async getTables() {
await this.client.asPromise();
const collections = await this.client.db.listCollections().toArray();
return collections.map(collection => collection.name);
}
/**
* Verify if table exists.
*/
async hasTable(table) {
await this.client.asPromise();
const tables = await this.getTables();
return tables.includes(table);
}
/**
* Create a new table in database.
*/
async createTable() {
throw new NotImplementedMethodException(this.createTable.name, 'mongo');
}
/**
* Alter a table in database.
*/
async alterTable() {
throw new NotImplementedMethodException(this.alterTable.name, 'mongo');
}
/**
* Drop a table in database.
*/
async dropTable(table) {
try {
await this.client.asPromise();
await this.client.dropCollection(table);
}
catch (err) {
debug('error happened while dropping table %s in MongoDriver: %o', err);
}
}
/**
* Remove all data inside some database table
* and restart the identity of the table.
*/
async truncate(table) {
await this.client.asPromise();
const collection = this.client.collection(table);
await collection.deleteMany({}, { session: this.session });
}
/**
* Make a raw query in database.
*/
raw() {
throw new NotImplementedMethodException(this.raw.name, 'mongo');
}
/**
* Calculate the average of a given column.
*/
async avg(column) {
await this.client.asPromise();
const pipeline = this.createPipeline();
pipeline.push({
$group: { [this.primaryKey]: null, avg: { $avg: `$${column}` } }
});
pipeline.push({ $project: { [this.primaryKey]: 0, avg: 1 } });
const result = await this.qb
.aggregate(pipeline, { session: this.session })
.toArray();
if (Is.Empty(result)) {
return null;
}
return `${result[0].avg}`;
}
/**
* Calculate the average of a given column using distinct.
*/
async avgDistinct(column) {
await this.client.asPromise();
const pipeline = this.createPipeline();
pipeline.push({
$group: { [this.primaryKey]: null, set: { $addToSet: `$${column}` } }
});
pipeline.push({ $project: { [this.primaryKey]: 0, avg: { $avg: '$set' } } });
const result = await this.qb
.aggregate(pipeline, { session: this.session })
.toArray();
if (Is.Empty(result)) {
return null;
}
return `${result[0].avg}`;
}
/**
* Get the max number of a given column.
*/
async max(column) {
await this.client.asPromise();
const pipeline = this.createPipeline();
pipeline.push({
$group: { [this.primaryKey]: null, max: { $max: `$${column}` } }
});
pipeline.push({ $project: { [this.primaryKey]: 0, max: 1 } });
const result = await this.qb
.aggregate(pipeline, { session: this.session })
.toArray();
if (Is.Empty(result)) {
return null;
}
return `${result[0].max}`;
}
/**
* Get the min number of a given column.
*/
async min(column) {
await this.client.asPromise();
const pipeline = this.createPipeline();
pipeline.push({
$group: { [this.primaryKey]: null, min: { $min: `$${column}` } }
});
pipeline.push({ $project: { [this.primaryKey]: 0, min: 1 } });
const result = await this.qb
.aggregate(pipeline, { session: this.session })
.toArray();
if (Is.Empty(result)) {
return null;
}
return `${result[0].min}`;
}
/**
* Sum all numbers of a given column.
*/
async sum(column) {
await this.client.asPromise();
const pipeline = this.createPipeline();
pipeline.push({
$group: { [this.primaryKey]: null, sum: { $sum: `$${column}` } }
});
pipeline.push({ $project: { [this.primaryKey]: 0, sum: 1 } });
const result = await this.qb
.aggregate(pipeline, { session: this.session })
.toArray();
if (Is.Empty(result)) {
return null;
}
return `${result[0].sum}`;
}
/**
* Sum all numbers of a given column in distinct mode.
*/
async sumDistinct(column) {
await this.client.asPromise();
const pipeline = this.createPipeline();
pipeline.push({
$group: { [this.primaryKey]: null, set: { $addToSet: `$${column}` } }
});
pipeline.push({ $project: { [this.primaryKey]: 0, sum: { $sum: '$set' } } });
const result = await this.qb
.aggregate(pipeline, { session: this.session })
.toArray();
if (Is.Empty(result)) {
return null;
}
return `${result[0].sum}`;
}
/**
* Increment a value of a given column.
*/
async increment(column) {
await this.client.asPromise();
const where = this.createWhere();
await this.qb.updateMany(where, { $inc: { [column]: 1 } }, { session: this.session, upsert: false });
}
/**
* Decrement a value of a given column.
*/
async decrement(column) {
await this.client.asPromise();
const where = this.createWhere();
await this.qb.updateMany(where, { $inc: { [column]: -1 } }, { session: this.session, upsert: false });
}
/**
* Calculate the average of a given column using distinct.
*/
async count(column = '*') {
await this.client.asPromise();
const pipeline = this.createPipeline();
if (column !== '*') {
pipeline.push({ $match: { [column]: { $ne: null } } });
}
pipeline.push({ $group: { [this.primaryKey]: null, count: { $sum: 1 } } });
pipeline.push({ $project: { [this.primaryKey]: 0, count: 1 } });
const result = await this.qb
.aggregate(pipeline, { session: this.session })
.toArray();
return `${result[0]?.count || 0}`;
}
/**
* Calculate the average of a given column using distinct.
*/
async countDistinct(column) {
await this.client.asPromise();
const pipeline = this.createPipeline();
if (column !== '*') {
pipeline.push({ $match: { [column]: { $ne: null } } });
}
pipeline.push({
$group: { [this.primaryKey]: null, set: { $addToSet: `$${column}` } }
});
pipeline.push({
$project: { [this.primaryKey]: 0, count: { $size: `$set` } }
});
const [{ count }] = await this.qb
.aggregate(pipeline, { session: this.session })
.toArray();
return `${count}`;
}
/**
* Find a value in database.
*/
async find() {
await this.client.asPromise();
const pipeline = this.createPipeline();
const data = await this.qb
.aggregate(pipeline, { session: this.session })
.toArray();
return data[0];
}
/**
* Find many values in database.
*/
async findMany() {
await this.client.asPromise();
const pipeline = this.createPipeline();
return this.qb
.aggregate(pipeline, { session: this.session })
.toArray();
}
/**
* Find many values in database and return as paginated response.
*/
async paginate(page = { page: 0, limit: 10, resourceUrl: '/' }, limit = 10, resourceUrl = '/') {
await this.client.asPromise();
if (Is.Number(page)) {
page = { page, limit, resourceUrl };
}
const pipeline = this.createPipeline({
clearWhere: false,
clearOrWhere: false,
clearPipeline: false
});
pipeline.push({ $group: { [this.primaryKey]: null, count: { $sum: 1 } } });
pipeline.push({ $project: { [this.primaryKey]: 0, count: 1 } });
const result = await this.qb
.aggregate(pipeline, { session: this.session })
.toArray();
const count = result[0]?.count || 0;
const data = await this.offset(page.page * page.limit)
.limit(page.limit)
.findMany();
return Exec.pagination(data, count, page);
}
/**
* Create a value in database.
*/
async create(data = {}) {
if (Is.Array(data)) {
throw new WrongMethodException('create', 'createMany');
}
await this.client.asPromise();
const created = await this.createMany([data]);
return created[0];
}
/**
* Create many values in database.
*/
async createMany(data = []) {
if (!Is.Array(data)) {
throw new WrongMethodException('createMany', 'create');
}
await this.client.asPromise();
const { insertedIds } = await this.qb.insertMany(data, {
session: this.session
});
const insertedIdsArray = [];
Object.keys(insertedIds).forEach(key => insertedIdsArray.push(insertedIds[key]));
return this.whereIn(this.primaryKey, insertedIdsArray).findMany();
}
/**
* Create data or update if already exists.
*/
async createOrUpdate(data = {}) {
await this.client.asPromise();
const pipeline = this.createPipeline();
const hasValue = (await this.qb.aggregate(pipeline, { session: this.session }).toArray())[0];
if (hasValue) {
return this.where(this.primaryKey, hasValue[this.primaryKey]).update(data);
}
return this.create(data);
}
/**
* Update a value in database.
*/
async update(data) {
await this.client.asPromise();
const where = this.createWhere({ clearWhere: false, clearOrWhere: false });
const pipeline = this.createPipeline();
await this.qb.updateMany(where, { $set: data }, { upsert: false, session: this.session });
const result = await this.qb
.aggregate(pipeline, { session: this.session })
.toArray();
if (result.length === 1) {
return result[0];
}
return result;
}
/**
* Delete one value in database.
*/
async delete() {
await this.client.asPromise();
await this.qb.deleteMany(this.createWhere(), { session: this.session });
}
/**
* Set the table that this query will be executed.
*/
table(table) {
if (!this.isConnected) {
throw new NotConnectedDatabaseException();
}
this.tableName = table;
this.qb = this.query();
return this;
}
/**
* Log in console the actual query built.
*/
dump() {
console.log({
where: this._where,
orWhere: this._orWhere,
pipeline: this.pipeline
});
return this;
}
/**
* Set the columns that should be selected on query.
*/
select(...columns) {
if (columns.includes('*')) {
return this;
}
if (!columns.includes('_id')) {
const isAlreadyHide = !!this.pipeline
.map(step => {
if (!step.$project) {
return false;
}
if (!step.$project._id) {
return false;
}
if (step.$project._id === 0) {
return false;
}
return true;
})
.find(value => value === true);
if (!isAlreadyHide) {
this.pipeline.push({ $project: { _id: 0 } });
}
}
const $project = columns.reduce((previous, column) => {
if (column.includes(`${this.tableName}.`)) {
column = column.replace(`${this.tableName}.`, '');
}
if (column.includes(' as ')) {
const [select, alias] = column.split(' as ');
previous[select] = 0;
previous[alias] = `$${select}`;
return previous;
}
previous[column] = 1;
return previous;
}, {});
this.pipeline.push({ $project });
return this;
}
/**
* Set the columns that should be selected on query raw.
*/
selectRaw() {
throw new NotImplementedMethodException(this.selectRaw.name, 'mongo');
}
/**
* Set the table that should be used on query.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
from() {
throw new NotImplementedMethodException(this.from.name, 'mongo');
}
/**
* Set the table that should be used on query raw.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
fromRaw() {
throw new NotImplementedMethodException(this.selectRaw.name, 'mongo');
}
/**
* Set a join statement in your query.
*/
join(table, column1, operation, column2) {
let foreignField = column2 || operation || this.primaryKey;
if (foreignField.includes('.')) {
foreignField = foreignField.split('.')[1];
}
let localField = column1 || this.primaryKey;
if (localField.includes('.')) {
localField = localField.split('.')[1];
}
this.pipeline.push({
$lookup: { from: table, localField, foreignField, as: table }
});
return this;
}
/**
* Set a left join statement in your query.
*/
leftJoin(table, column1, operation, column2) {
return this.join(table, column1, operation, column2);
}
/**
* Set a right join statement in your query.
*/
rightJoin(table, column1, operation, column2) {
return this.join(table, column1, operation, column2);
}
/**
* Set a cross join statement in your query.
*/
crossJoin(table, column1, operation, column2) {
return this.join(table, column1, operation, column2);
}
/**
* Set a full outer join statement in your query.
*/
fullOuterJoin(table, column1, operation, column2) {
return this.join(table, column1, operation, column2);
}
/**
* Set a left outer join statement in your query.
*/
leftOuterJoin(table, column1, operation, column2) {
return this.join(table, column1, operation, column2);
}
/**
* Set a right outer join statement in your query.
*/
rightOuterJoin(table, column1, operation, column2) {
return this.join(table, column1, operation, column2);
}
/**
* Set a join raw statement in your query.
*/
joinRaw() {
throw new NotImplementedMethodException(this.joinRaw.name, 'mongo');
}
/**
* Set a group by statement in your query.
*/
groupBy(...columns) {
const $group = { [this.primaryKey]: {} };
columns.forEach(column => ($group[this.primaryKey][column] = `$${column}`));
this.pipeline.push({ $group });
this.pipeline.push({ $replaceRoot: { newRoot: `$${this.primaryKey}` } });
return this;
}
/**
* Set a group by raw statement in your query.
*/
groupByRaw() {
throw new NotImplementedMethodException(this.groupByRaw.name, 'mongo');
}
/**
* Set a having statement in your query.
*/
having(column, operation, value) {
return this.where(column, operation, value);
}
/**
* Set a having raw statement in your query.
*/
havingRaw() {
throw new NotImplementedMethodException(this.havingRaw.name, 'mongo');
}
/**
* Set a having exists statement in your query.
*/
havingExists() {
throw new NotImplementedMethodException(this.havingExists.name, 'mongo');
}
/**
* Set a having not exists statement in your query.
*/
havingNotExists() {
throw new NotImplementedMethodException(this.havingNotExists.name, 'mongo');
}
/**
* Set a having in statement in your query.
*/
havingIn(column, values) {
return this.whereIn(column, values);
}
/**
* Set a having not in statement in your query.
*/
havingNotIn(column, values) {
return this.whereNotIn(column, values);
}
/**
* Set a having between statement in your query.
*/
havingBetween(column, values) {
return this.whereBetween(column, values);
}
/**
* Set a having not between statement in your query.
*/
havingNotBetween(column, values) {
return this.whereNotBetween(column, values);
}
/**
* Set a having null statement in your query.
*/
havingNull(column) {
return this.whereNull(column);
}
/**
* Set a having not null statement in your query.
*/
havingNotNull(column) {
return this.whereNotNull(column);
}
/**
* Set an or having statement in your query.
*/
orHaving(column, operation, value) {
return this.orWhere(column, operation, value);
}
/**
* Set an or having raw statement in your query.
*/
orHavingRaw() {
throw new NotImplementedMethodException(this.orHavingRaw.name, 'mongo');
}
/**
* Set an or having exists statement in your query.
*/
orHavingExists() {
throw new NotImplementedMethodException(this.orHavingExists.name, 'mongo');
}
/**
* Set an or having not exists statement in your query.
*/
orHavingNotExists() {
throw new NotImplementedMethodException(this.orHavingNotExists.name, 'mongo');
}
/**
* Set an or having in statement in your query.
*/
orHavingIn(column, values) {
return this.orWhereIn(column, values);
}
/**
* Set an or having not in statement in your query.
*/
orHavingNotIn(column, values) {
return this.orWhereNotIn(column, values);
}
/**
* Set an or having between statement in your query.
*/
orHavingBetween(column, values) {
return this.orWhereBetween(column, values);
}
/**
* Set an or having not between statement in your query.
*/
orHavingNotBetween(column, values) {
return this.orWhereNotBetween(column, values);
}
/**
* Set an or having null statement in your query.
*/
orHavingNull(column) {
return this.whereNull(column);
}
/**
* Set an or having not null statement in your query.
*/
orHavingNotNull(column) {
return this.whereNotNull(column);
}
/**
* Set a where statement in your query.
*/
where(statement, operation, value) {
if (Is.Function(statement)) {
statement(this);
return this;
}
if (operation === undefined) {
this._where.push(statement);
return this;
}
if (value === undefined) {
this._where.push({
[statement]: this.setOperator(operation, '=')
});
return this;
}
this._where.push({ [statement]: this.setOperator(value, operation) });
return this;
}
/**
* Set a where not statement in your query.
*/
whereNot(statement, value) {
return this.where(statement, '<>', value);
}
/**
* Set a where raw statement in your query.
*/
whereRaw() {
throw new NotImplementedMethodException(this.whereRaw.name, 'mongo');
}
/**
* Set a where exists statement in your query.
*/
whereExists() {
throw new NotImplementedMethodException(this.whereExists.name, 'mongo');
}
/**
* Set a where not exists statement in your query.
*/
whereNotExists() {
throw new NotImplementedMethodException(this.whereNotExists.name, 'mongo');
}
/**
* Set a where like statement in your query.
*/
whereLike(column, value) {
return this.where(column, 'like', value);
}
/**
* Set a where ILike statement in your query.
*/
whereILike(column, value) {
return this.where(column, 'ilike', value);
}
/**
* Set a where in statement in your query.
*/
whereIn(column, values) {
values = values.flatMap(value => {
if (ObjectId.isValidStringOrObject(value)) {
return [String(value), new ObjectId(value)];
}
return [value];
});
this._where.push({ [column]: { $in: values } });
return this;
}
/**
* Set a where not in statement in your query.
*/
whereNotIn(column, values) {
values = values.flatMap(value => {
if (ObjectId.isValidStringOrObject(value)) {
return [String(value), new ObjectId(value)];
}
return [value];
});
this._where.push({ [column]: { $nin: values } });
return this;
}
/**
* Set a where between statement in your query.
*/
whereBetween(column, values) {
this._where.push({ [column]: { $gte: values[0], $lte: values[1] } });
return this;
}
/**
* Set a where not between statement in your query.
*/
whereNotBetween(column, values) {
this._where.push({
[column]: { $not: { $gte: values[0], $lte: values[1] } }
});
return this;
}
/**
* Set a where null statement in your query.
*/
whereNull(column) {
this._where.push({ [column]: null });
return this;
}
/**
* Set a where not null statement in your query.
*/
whereNotNull(column) {
this._where.push({ [column]: { $ne: null } });
return this;
}
/**
* Set a or where statement in your query.
*/
orWhere(statement, operation, value) {
if (Is.Function(statement)) {
statement(this);
return this;
}
if (operation === undefined) {
this._orWhere.push(statement);
return this;
}
if (value === undefined) {
this._orWhere.push({ [statement]: this.setOperator(operation, '=') });
return this;
}
this._orWhere.push({ [statement]: this.setOperator(value, operation) });
return this;
}
/**
* Set an or where not statement in your query.
*/
orWhereNot(statement, value) {
return this.orWhere(statement, '<>', value);
}
/**
* Set a or where raw statement in your query.
*/
orWhereRaw() {
throw new NotImplementedMethodException(this.orWhereRaw.name, 'mongo');
}
/**
* Set an or where exists statement in your query.
*/
orWhereExists() {
throw new NotImplementedMethodException(this.orWhereExists.name, 'mongo');
}
/**
* Set an or where not exists statement in your query.
*/
orWhereNotExists() {
throw new NotImplementedMethodException(this.orWhereNotExists.name, 'mongo');
}
/**
* Set an or where like statement in your query.
*/
orWhereLike(column, value) {
return this.orWhere(column, 'like', value);
}
/**
* Set an or where ILike statement in your query.
*/
orWhereILike(column, value) {
return this.orWhere(column, 'ilike', value);
}
/**
* Set an or where in statement in your query.
*/
orWhereIn(column, values) {
values = values.flatMap(value => {
if (ObjectId.isValidStringOrObject(value)) {
return [String(value), new ObjectId(value)];
}
return [value];
});
this._orWhere.push({ [column]: { $in: values } });
return this;
}
/**
* Set an or where not in statement in your query.
*/
orWhereNotIn(column, values) {
values = values.flatMap(value => {
if (ObjectId.isValidStringOrObject(value)) {
return [String(value), new ObjectId(value)];
}
return [value];
});
this._orWhere.push({ [column]: { $nin: values } });
return this;
}
/**
* Set an or where between statement in your query.
*/
orWhereBetween(column, values) {
this._orWhere.push({ [column]: { $gte: values[0], $lte: values[1] } });
return this;
}
/**
* Set an or where not between statement in your query.
*/
orWhereNotBetween(column, values) {
this._orWhere.push({
[column]: { $not: { $gte: values[0], $lte: values[1] } }
});
return this;
}
/**
* Set an or where null statement in your query.
*/
orWhereNull(column) {
this._orWhere.push({ [column]: null });
return this;
}
/**
* Set an or where not null statement in your query.
*/
orWhereNotNull(column) {
this._orWhere.push({ [column]: { $ne: null } });
return this;
}
/**
* Set an order by statement in your query.
*/
orderBy(column, direction = 'ASC') {
this.pipeline.push({
$sort: { [column]: direction.toLowerCase() === 'asc' ? 1 : -1 }
});
return this;
}
/**
* Set an order by raw statement in your query.
*/
orderByRaw() {
throw new NotImplementedMethodException(this.orderByRaw.name, 'mongo');
}
/**
* Order the results easily by the latest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
latest(column = 'createdAt') {
return this.orderBy(column, 'DESC');
}
/**
* Order the results easily by the oldest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
oldest(column = 'createdAt') {
return this.orderBy(column, 'ASC');
}
/**
* Set the skip number in your query.
*/
offset(number) {
this.pipeline.push({ $skip: number });
return this;
}
/**
* Set the limit number in your query.
*/
limit(number) {
this.pipeline.push({ $limit: number });
return this;
}
/**
* Set the mongo operation in value.
*/
setOperator(value, operator) {
if (operator === '=') {
return value;
}
const mongoOperator = MONGO_OPERATIONS_DICTIONARY[operator];
const object = { [mongoOperator]: value };
if (operator === 'like' || operator === 'ilike') {
let valueRegexString = value.replace(/%/g, '');
if (!value.startsWith('%') && value.endsWith('%')) {
valueRegexString = `^${valueRegexString}`;
}
else if (value.startsWith('%') && !value.endsWith('%')) {
valueRegexString = `${valueRegexString}$`;
}
object[mongoOperator] = new RegExp(valueRegexString);
}
if (operator === 'ilike') {
object.$options = 'i';
}
return object;
}
/**
* Creates the where clause with where and orWhere.
*/
createWhere(options = {}) {
options = Options.create(options, {
clearWhere: true,
clearOrWhere: true
});
const where = {};
if (!Is.Empty(this._where)) {
where.$and = Json.copy(this._where).map(condition => {
const keysToSwap = Object.keys(condition).filter(key => {
const value = condition[key];
if (ObjectId.isValidStringOrObject(value)) {
return true;
}
return false;
});
keysToSwap.forEach(key => {
if (!condition.$or) {
condition.$or = [];
}
const objectId = condition[key];
condition.$or.push({ [key]: String(objectId) }, { [key]: new ObjectId(objectId) });
delete condition[key];
});
return condition;
});
}
if (!Is.Empty(this._orWhere)) {
where.$or = Json.copy(this._orWhere).map(condition => {
const keysToSwap = Object.keys(condition).filter(key => {
const value = condition[key];
if (ObjectId.isValidStringOrObject(value)) {
return true;
}
return false;
});
keysToSwap.forEach(key => {
if (!condition.$or) {
condition.$or = [];
}
const objectId = condition[key];
condition.$or.push({ [key]: String(objectId) }, { [key]: new ObjectId(objectId) });
delete condition[key];
});
return condition;
});
}
if (options.clearWhere) {
this._where = [];
}
if (options.clearOrWhere) {
this._orWhere = [];
}
return where;
}
/**
* Creates the aggregation pipeline.
*/
createPipeline(options = {}) {
options = Options.create(options, {
clearWhere: true,
clearOrWhere: true,
clearPipeline: true
});
const pipeline = Json.copy(this.pipeline);
if (options.clearPipeline) {
this.pipeline = [];
}
pipeline.push({ $match: this.createWhere(options) });
return pipeline;
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { type PaginatedResponse, type PaginationOptions } from '@athenna/common';
import type { Knex } from 'knex';
import { Driver } from '#src/database/drivers/Driver';
import { Transaction } from '#src/database/transactions/Transaction';
import type { ConnectionOptions, Direction, Operations } from '#src/types';
export declare class MySqlDriver extends Driver<Knex, Knex.QueryBuilder> {
/**
* Connect to database.
*/
connect(options?: ConnectionOptions): void;
/**
* Close the connection with database in this instance.
*/
close(): Promise<void>;
/**
* Creates a new instance of query builder.
*/
query(): Knex.QueryBuilder;
/**
* Sync a model schema with database.
*/
sync(): Promise<void>;
/**
* Create a new transaction.
*/
startTransaction(): Promise<Transaction<Knex.Transaction, Knex.QueryBuilder>>;
/**
* Commit the transaction.
*/
commitTransaction(): Promise<void>;
/**
* Rollback the transaction.
*/
rollbackTransaction(): Promise<void>;
/**
* Run database migrations.
*/
runMigrations(): Promise<void>;
/**
* Revert database migrations.
*/
revertMigrations(): Promise<void>;
/**
* List all databases available.
*/
getDatabases(): Promise<string[]>;
/**
* Get the current database name.
*/
getCurrentDatabase(): Promise<string | undefined>;
/**
* Verify if database exists.
*/
hasDatabase(database: string): Promise<boolean>;
/**
* Create a new database.
*/
createDatabase(database: string): Promise<void>;
/**
* Drop some database.
*/
dropDatabase(database: string): Promise<void>;
/**
* List all tables available.
*/
getTables(): Promise<string[]>;
/**
* Verify if table exists.
*/
hasTable(table: string): Promise<boolean>;
/**
* Create a new table in database.
*/
createTable(table: string, closure: (builder: Knex.TableBuilder) => void | Promise<void>): Promise<void>;
/**
* Alter a table in database.
*/
alterTable(table: string, closure: (builder: Knex.TableBuilder) => void | Promise<void>): Promise<void>;
/**
* Drop a table in database.
*/
dropTable(table: string): Promise<void>;
/**
* Remove all data inside some database table
* and restart the identity of the table.
*/
truncate(table: string): Promise<void>;
/**
* Make a raw query in database.
*/
raw<T = any>(sql: string, bindings?: any): T;
/**
* Calculate the average of a given column.
*/
avg(column: string): Promise<string>;
/**
* Calculate the average of a given column using distinct.
*/
avgDistinct(column: string): Promise<string>;
/**
* Get the max number of a given column.
*/
max(column: string): Promise<string>;
/**
* Get the min number of a given column.
*/
min(column: string): Promise<string>;
/**
* Sum all numbers of a given column.
*/
sum(column: string): Promise<string>;
/**
* Sum all numbers of a given column in distinct mode.
*/
sumDistinct(column: string): Promise<string>;
/**
* Increment a value of a given column.
*/
increment(column: string): Promise<void>;
/**
* Decrement a value of a given column.
*/
decrement(column: string): Promise<void>;
/**
* Calculate the average of a given column using distinct.
*/
count(column?: string): Promise<string>;
/**
* Calculate the average of a given column using distinct.
*/
countDistinct(column: string): Promise<string>;
/**
* Find a value in database.
*/
find<T = any>(): Promise<T>;
/**
* Find many values in database.
*/
findMany<T = any>(): Promise<T[]>;
/**
* Find many values in database and return as paginated response.
*/
paginate<T = any>(page?: PaginationOptions | number, limit?: number, resourceUrl?: string): Promise<PaginatedResponse<T>>;
/**
* Create a value in database.
*/
create<T = any>(data?: Partial<T>): Promise<T>;
/**
* Create many values in database.
*/
createMany<T = any>(data?: Partial<T>[]): Promise<T[]>;
/**
* Create data or update if already exists.
*/
createOrUpdate<T = any>(data?: Partial<T>): Promise<T | T[]>;
/**
* Update a value in database.
*/
update<T = any>(data: Partial<T>): Promise<T | T[]>;
/**
* Delete one value in database.
*/
delete(): Promise<void>;
/**
* Set the table that this query will be executed.
*/
table(table: string): this;
/**
* Log in console the actual query built.
*/
dump(): this;
/**
* Set the columns that should be selected on query.
*/
select(...columns: string[]): this;
/**
* Set the columns that should be selected on query raw.
*/
selectRaw(sql: string, bindings?: any): this;
/**
* Set the table that should be used on query.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
from(table: string): this;
/**
* Set the table that should be used on query raw.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
fromRaw(sql: string, bindings?: any): this;
/**
* Set a join statement in your query.
*/
join(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a left join statement in your query.
*/
leftJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a right join statement in your query.
*/
rightJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a cross join statement in your query.
*/
crossJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a full outer join statement in your query.
*/
fullOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a left outer join statement in your query.
*/
leftOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a right outer join statement in your query.
*/
rightOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a join raw statement in your query.
*/
joinRaw(sql: string, bindings?: any): this;
/**
* Set a group by statement in your query.
*/
groupBy(...columns: string[]): this;
/**
* Set a group by raw statement in your query.
*/
groupByRaw(sql: string, bindings?: any): this;
having(column: string): this;
having(column: string, value: any): this;
having(column: string, operation: Operations, value: any): this;
/**
* Set a having raw statement in your query.
*/
havingRaw(sql: string, bindings?: any): this;
/**
* Set a having exists statement in your query.
*/
havingExists(closure: (query: MySqlDriver) => void): this;
/**
* Set a having not exists statement in your query.
*/
havingNotExists(closure: (query: MySqlDriver) => void): this;
/**
* Set a having in statement in your query.
*/
havingIn(column: string, values: any[]): this;
/**
* Set a having not in statement in your query.
*/
havingNotIn(column: string, values: any[]): this;
/**
* Set a having between statement in your query.
*/
havingBetween(column: string, values: [any, any]): this;
/**
* Set a having not between statement in your query.
*/
havingNotBetween(column: string, values: [any, any]): this;
/**
* Set a having null statement in your query.
*/
havingNull(column: string): this;
/**
* Set a having not null statement in your query.
*/
havingNotNull(column: string): this;
orHaving(column: string): this;
orHaving(column: string, value: any): this;
orHaving(column: string, operation: Operations, value: any): this;
/**
* Set an or having raw statement in your query.
*/
orHavingRaw(sql: string, bindings?: any): this;
/**
* Set an or having exists statement in your query.
*/
orHavingExists(closure: (query: MySqlDriver) => void): this;
/**
* Set an or having not exists statement in your query.
*/
orHavingNotExists(closure: (query: MySqlDriver) => void): this;
/**
* Set an or having in statement in your query.
*/
orHavingIn(column: string, values: any[]): this;
/**
* Set an or having not in statement in your query.
*/
orHavingNotIn(column: string, values: any[]): this;
/**
* Set an or having between statement in your query.
*/
orHavingBetween(column: string, values: [any, any]): this;
/**
* Set an or having not between statement in your query.
*/
orHavingNotBetween(column: string, values: [any, any]): this;
/**
* Set an or having null statement in your query.
*/
orHavingNull(column: string): this;
/**
* Set an or having not null statement in your query.
*/
orHavingNotNull(column: string): this;
where(statement: Record<string, any>): this;
where(key: string, value: any): this;
where(key: string, operation: Operations, value: any): this;
whereNot(statement: Record<string, any>): this;
whereNot(key: string, value: any): this;
/**
* Set a where raw statement in your query.
*/
whereRaw(sql: string, bindings?: any): this;
/**
* Set a where exists statement in your query.
*/
whereExists(closure: (query: MySqlDriver) => void): this;
/**
* Set a where not exists statement in your query.
*/
whereNotExists(closure: (query: MySqlDriver) => void): this;
/**
* Set a where like statement in your query.
*/
whereLike(column: string, value: any): this;
/**
* Set a where ILike statement in your query.
*/
whereILike(column: string, value: any): this;
/**
* Set a where in statement in your query.
*/
whereIn(column: string, values: any[]): this;
/**
* Set a where not in statement in your query.
*/
whereNotIn(column: string, values: any[]): this;
/**
* Set a where between statement in your query.
*/
whereBetween(column: string, values: [any, any]): this;
/**
* Set a where not between statement in your query.
*/
whereNotBetween(column: string, values: [any, any]): this;
/**
* Set a where null statement in your query.
*/
whereNull(column: string): this;
/**
* Set a where not null statement in your query.
*/
whereNotNull(column: string): this;
orWhere(statement: Record<string, any>): this;
orWhere(key: string, value: any): this;
orWhere(key: string, operation: Operations, value: any): this;
orWhereNot(statement: Record<string, any>): this;
orWhereNot(key: string, value: any): this;
/**
* Set a or where raw statement in your query.
*/
orWhereRaw(sql: string, bindings?: any): this;
/**
* Set an or where exists statement in your query.
*/
orWhereExists(closure: (query: MySqlDriver) => void): this;
/**
* Set an or where not exists statement in your query.
*/
orWhereNotExists(closure: (query: MySqlDriver) => void): this;
/**
* Set an or where like statement in your query.
*/
orWhereLike(column: string, value: any): this;
/**
* Set an or where ILike statement in your query.
*/
orWhereILike(column: string, value: any): this;
/**
* Set an or where in statement in your query.
*/
orWhereIn(column: string, values: any[]): this;
/**
* Set an or where not in statement in your query.
*/
orWhereNotIn(column: string, values: any[]): this;
/**
* Set an or where between statement in your query.
*/
orWhereBetween(column: string, values: [any, any]): this;
/**
* Set an or where not between statement in your query.
*/
orWhereNotBetween(column: string, values: [any, any]): this;
/**
* Set an or where null statement in your query.
*/
orWhereNull(column: string): this;
/**
* Set an or where not null statement in your query.
*/
orWhereNotNull(column: string): this;
/**
* Set an order by statement in your query.
*/
orderBy(column: string, direction?: Direction): this;
/**
* Set an order by raw statement in your query.
*/
orderByRaw(sql: string, bindings?: any): this;
/**
* Order the results easily by the latest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
latest(column?: string): this;
/**
* Order the results easily by the oldest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
oldest(column?: string): this;
/**
* Set the skip number in your query.
*/
offset(number: number): this;
/**
* Set the limit number in your query.
*/
limit(number: number): this;
}
/* eslint-disable @typescript-eslint/ban-ts-comment */
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Exec, Is, Json, Options } from '@athenna/common';
import { debug } from '#src/debug';
import { Log } from '@athenna/logger';
import { Driver } from '#src/database/drivers/Driver';
import { ConnectionFactory } from '#src/factories/ConnectionFactory';
import { Transaction } from '#src/database/transactions/Transaction';
import { MigrationSource } from '#src/database/migrations/MigrationSource';
import { WrongMethodException } from '#src/exceptions/WrongMethodException';
import { PROTECTED_QUERY_METHODS } from '#src/constants/ProtectedQueryMethods';
import { NotConnectedDatabaseException } from '#src/exceptions/NotConnectedDatabaseException';
export class MySqlDriver extends Driver {
/**
* Connect to database.
*/
connect(options = {}) {
options = Options.create(options, {
force: false,
saveOnFactory: true,
connect: true
});
if (!options.connect) {
return;
}
if (this.isConnected && !options.force) {
return;
}
const knex = this.getKnex();
const configs = Config.get(`database.connections.${this.connection}`, {});
const knexOpts = {
client: 'mysql2',
migrations: {
tableName: 'migrations'
},
pool: {
min: 2,
max: 20,
acquireTimeoutMillis: 60 * 1000
},
debug: false,
useNullAsDefault: false,
...Json.omit(configs, ['driver', 'validations'])
};
debug('creating new connection using Knex. options defined: %o', knexOpts);
if (Config.is('rc.bootLogs', true)) {
Log.channelOrVanilla('application').success(`Successfully connected to ({yellow} ${this.connection}) database connection`);
}
this.client = knex.default(knexOpts);
this.isConnected = true;
this.isSavedOnFactory = options.saveOnFactory;
if (this.isSavedOnFactory) {
ConnectionFactory.setClient(this.connection, this.client);
}
this.qb = this.query();
}
/**
* Close the connection with database in this instance.
*/
async close() {
if (!this.isConnected) {
return;
}
await this.client.destroy();
this.qb = null;
this.tableName = null;
this.client = null;
this.isConnected = false;
ConnectionFactory.setClient(this.connection, null);
}
/**
* Creates a new instance of query builder.
*/
query() {
if (!this.isConnected) {
throw new NotConnectedDatabaseException();
}
const query = this.useSetQB
? this.qb.table(this.tableName)
: this.client.queryBuilder().table(this.tableName);
const handler = {
get: (target, propertyKey) => {
if (PROTECTED_QUERY_METHODS.includes(propertyKey)) {
this.qb = this.query();
}
return target[propertyKey];
}
};
return new Proxy(query, handler);
}
/**
* Sync a model schema with database.
*/
async sync() {
debug(`database sync with ${MySqlDriver.name} is not available yet, use migration instead.`);
}
/**
* Create a new transaction.
*/
async startTransaction() {
const trx = await this.client.transaction();
return new Transaction(this.clone().setClient(trx));
}
/**
* Commit the transaction.
*/
async commitTransaction() {
const client = this.client;
await client.commit();
this.tableName = null;
this.client = null;
this.isConnected = false;
}
/**
* Rollback the transaction.
*/
async rollbackTransaction() {
const client = this.client;
await client.rollback();
this.tableName = null;
this.client = null;
this.isConnected = false;
}
/**
* Run database migrations.
*/
async runMigrations() {
await this.client.migrate.latest({
migrationSource: new MigrationSource(this.connection)
});
}
/**
* Revert database migrations.
*/
async revertMigrations() {
await this.client.migrate.rollback({
migrationSource: new MigrationSource(this.connection)
});
}
/**
* List all databases available.
*/
async getDatabases() {
const [databases] = await this.raw('SHOW DATABASES');
return databases.map(database => database.Database);
}
/**
* Get the current database name.
*/
async getCurrentDatabase() {
return this.client.client.database();
}
/**
* Verify if database exists.
*/
async hasDatabase(database) {
const databases = await this.getDatabases();
return databases.includes(database);
}
/**
* Create a new database.
*/
async createDatabase(database) {
await this.raw('CREATE DATABASE IF NOT EXISTS ??', database);
}
/**
* Drop some database.
*/
async dropDatabase(database) {
await this.raw('DROP DATABASE IF EXISTS ??', database);
}
/**
* List all tables available.
*/
async getTables() {
const [tables] = await this.raw('SELECT table_name FROM information_schema.tables WHERE table_schema = ?', await this.getCurrentDatabase());
return tables.map(table => table.TABLE_NAME);
}
/**
* Verify if table exists.
*/
async hasTable(table) {
return this.client.schema.hasTable(table);
}
/**
* Create a new table in database.
*/
async createTable(table, closure) {
await this.client.schema.createTable(table, closure);
}
/**
* Alter a table in database.
*/
async alterTable(table, closure) {
await this.client.schema.alterTable(table, closure);
}
/**
* Drop a table in database.
*/
async dropTable(table) {
await this.client.schema.dropTableIfExists(table);
}
/**
* Remove all data inside some database table
* and restart the identity of the table.
*/
async truncate(table) {
try {
await this.raw('SET FOREIGN_KEY_CHECKS = 0');
await this.raw('TRUNCATE TABLE ??', table);
}
finally {
await this.raw('SET FOREIGN_KEY_CHECKS = 1');
}
}
/**
* Make a raw query in database.
*/
raw(sql, bindings) {
return this.client.raw(sql, bindings);
}
/**
* Calculate the average of a given column.
*/
async avg(column) {
const [{ avg }] = await this.qb.avg({ avg: column });
return avg;
}
/**
* Calculate the average of a given column using distinct.
*/
async avgDistinct(column) {
const [{ avg }] = await this.qb.avgDistinct({ avg: column });
return avg;
}
/**
* Get the max number of a given column.
*/
async max(column) {
const [{ max }] = await this.qb.max({ max: column });
return max;
}
/**
* Get the min number of a given column.
*/
async min(column) {
const [{ min }] = await this.qb.min({ min: column });
return min;
}
/**
* Sum all numbers of a given column.
*/
async sum(column) {
const [{ sum }] = await this.qb.sum({ sum: column });
return sum;
}
/**
* Sum all numbers of a given column in distinct mode.
*/
async sumDistinct(column) {
const [{ sum }] = await this.qb.sumDistinct({ sum: column });
return sum;
}
/**
* Increment a value of a given column.
*/
async increment(column) {
await this.qb.increment(column);
}
/**
* Decrement a value of a given column.
*/
async decrement(column) {
await this.qb.decrement(column);
}
/**
* Calculate the average of a given column using distinct.
*/
async count(column = '*') {
const [{ count }] = await this.qb.count({ count: column });
return `${count}`;
}
/**
* Calculate the average of a given column using distinct.
*/
async countDistinct(column) {
const [{ count }] = await this.qb.countDistinct({ count: column });
return `${count}`;
}
/**
* Find a value in database.
*/
async find() {
return this.qb.first();
}
/**
* Find many values in database.
*/
async findMany() {
const data = await this.qb;
this.qb = this.query();
return data;
}
/**
* Find many values in database and return as paginated response.
*/
async paginate(page = { page: 0, limit: 10, resourceUrl: '/' }, limit = 10, resourceUrl = '/') {
if (Is.Number(page)) {
page = { page, limit, resourceUrl };
}
const [{ count }] = await this.qb
.clone()
.clearOrder()
.clearSelect()
.count({ count: '*' });
const data = await this.offset(page.page * page.limit)
.limit(page.limit)
.findMany();
return Exec.pagination(data, parseInt(count), page);
}
/**
* Create a value in database.
*/
async create(data = {}) {
if (Is.Array(data)) {
throw new WrongMethodException('create', 'createMany');
}
const created = await this.createMany([data]);
return created[0];
}
/**
* Create many values in database.
*/
async createMany(data = []) {
if (!Is.Array(data)) {
throw new WrongMethodException('createMany', 'create');
}
const ids = [];
const promises = data.map(data => {
return this.qb
.clone()
.insert(data)
.then(([id]) => ids.push(data[this.primaryKey] || id));
});
await Promise.all(promises);
return this.whereIn(this.primaryKey, ids).findMany();
}
/**
* Create data or update if already exists.
*/
async createOrUpdate(data = {}) {
const query = this.qb.clone();
const hasValue = await query.first();
if (hasValue) {
await this.qb
.where(this.primaryKey, hasValue[this.primaryKey])
.update(data);
return this.where(this.primaryKey, hasValue[this.primaryKey]).find();
}
return this.create(data);
}
/**
* Update a value in database.
*/
async update(data) {
await this.qb.clone().update(data);
const result = await this.findMany();
if (result.length === 1) {
return result[0];
}
return result;
}
/**
* Delete one value in database.
*/
async delete() {
await this.qb.delete();
}
/**
* Set the table that this query will be executed.
*/
table(table) {
if (!this.isConnected) {
throw new NotConnectedDatabaseException();
}
this.tableName = table;
this.qb = this.query();
return this;
}
/**
* Log in console the actual query built.
*/
dump() {
console.log(this.qb.toSQL().toNative());
return this;
}
/**
* Set the columns that should be selected on query.
*/
select(...columns) {
this.qb.select(...columns);
return this;
}
/**
* Set the columns that should be selected on query raw.
*/
selectRaw(sql, bindings) {
return this.select(this.raw(sql, bindings));
}
/**
* Set the table that should be used on query.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
from(table) {
this.qb.from(table);
return this;
}
/**
* Set the table that should be used on query raw.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
fromRaw(sql, bindings) {
return this.from(this.raw(sql, bindings));
}
/**
* Set a join statement in your query.
*/
join(table, column1, operation, column2) {
return this.joinByType('join', table, column1, operation, column2);
}
/**
* Set a left join statement in your query.
*/
leftJoin(table, column1, operation, column2) {
return this.joinByType('leftJoin', table, column1, operation, column2);
}
/**
* Set a right join statement in your query.
*/
rightJoin(table, column1, operation, column2) {
return this.joinByType('rightJoin', table, column1, operation, column2);
}
/**
* Set a cross join statement in your query.
*/
crossJoin(table, column1, operation, column2) {
return this.joinByType('crossJoin', table, column1, operation, column2);
}
/**
* Set a full outer join statement in your query.
*/
fullOuterJoin(table, column1, operation, column2) {
// TODO https://github.com/knex/knex/issues/3949
return this.joinByType('leftJoin', table, column1, operation, column2);
}
/**
* Set a left outer join statement in your query.
*/
leftOuterJoin(table, column1, operation, column2) {
return this.joinByType('leftOuterJoin', table, column1, operation, column2);
}
/**
* Set a right outer join statement in your query.
*/
rightOuterJoin(table, column1, operation, column2) {
return this.joinByType('rightOuterJoin', table, column1, operation, column2);
}
/**
* Set a join raw statement in your query.
*/
joinRaw(sql, bindings) {
this.qb.joinRaw(sql, bindings);
return this;
}
/**
* Set a group by statement in your query.
*/
groupBy(...columns) {
this.qb.groupBy(...columns);
return this;
}
/**
* Set a group by raw statement in your query.
*/
groupByRaw(sql, bindings) {
this.qb.groupByRaw(sql, bindings);
return this;
}
/**
* Set a having statement in your query.
*/
having(column, operation, value) {
if (operation === undefined) {
this.qb.having(column);
return this;
}
if (value === undefined) {
this.qb.having(column, '=', operation);
return this;
}
this.qb.having(column, operation, value);
return this;
}
/**
* Set a having raw statement in your query.
*/
havingRaw(sql, bindings) {
this.qb.havingRaw(sql, bindings);
return this;
}
/**
* Set a having exists statement in your query.
*/
havingExists(closure) {
const driver = this.clone();
// @ts-ignore
this.qb.havingExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set a having not exists statement in your query.
*/
havingNotExists(closure) {
const driver = this.clone();
// @ts-ignore
this.qb.havingNotExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set a having in statement in your query.
*/
havingIn(column, values) {
this.qb.havingIn(column, values);
return this;
}
/**
* Set a having not in statement in your query.
*/
havingNotIn(column, values) {
this.qb.havingNotIn(column, values);
return this;
}
/**
* Set a having between statement in your query.
*/
havingBetween(column, values) {
this.qb.havingBetween(column, values);
return this;
}
/**
* Set a having not between statement in your query.
*/
havingNotBetween(column, values) {
this.qb.havingNotBetween(column, values);
return this;
}
/**
* Set a having null statement in your query.
*/
havingNull(column) {
this.qb.havingNull(column);
return this;
}
/**
* Set a having not null statement in your query.
*/
havingNotNull(column) {
this.qb.havingNotNull(column);
return this;
}
/**
* Set an or having statement in your query.
*/
orHaving(column, operation, value) {
if (operation === undefined) {
this.qb.orHaving(column);
return this;
}
if (value === undefined) {
this.qb.orHaving(column, '=', operation);
return this;
}
this.qb.orHaving(column, operation, value);
return this;
}
/**
* Set an or having raw statement in your query.
*/
orHavingRaw(sql, bindings) {
this.qb.orHavingRaw(sql, bindings);
return this;
}
/**
* Set an or having exists statement in your query.
*/
orHavingExists(closure) {
const driver = this.clone();
// @ts-ignore
this.qb.orHavingExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set an or having not exists statement in your query.
*/
orHavingNotExists(closure) {
const driver = this.clone();
// @ts-ignore
this.qb.orHavingNotExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set an or having in statement in your query.
*/
orHavingIn(column, values) {
// @ts-ignore
this.qb.orHavingIn(column, values);
return this;
}
/**
* Set an or having not in statement in your query.
*/
orHavingNotIn(column, values) {
this.qb.orHavingNotIn(column, values);
return this;
}
/**
* Set an or having between statement in your query.
*/
orHavingBetween(column, values) {
this.qb.orHavingBetween(column, values);
return this;
}
/**
* Set an or having not between statement in your query.
*/
orHavingNotBetween(column, values) {
this.qb.orHavingNotBetween(column, values);
return this;
}
/**
* Set an or having null statement in your query.
*/
orHavingNull(column) {
// @ts-ignore
this.qb.orHavingNull(column);
return this;
}
/**
* Set an or having not null statement in your query.
*/
orHavingNotNull(column) {
// @ts-ignore
this.qb.orHavingNotNull(column);
return this;
}
/**
* Set a where statement in your query.
*/
where(statement, operation, value) {
if (Is.Function(statement)) {
const driver = this.clone();
this.qb.where(function () {
statement(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
if (operation === undefined) {
if (Is.Array(statement)) {
throw new Error('Arrays as statement are not supported.');
}
if (Is.String(statement)) {
throw new Error(`The value for the "${statement}" column is undefined and where will not work.`);
}
this.qb.where(statement);
return this;
}
if (value === undefined) {
this.qb.where(statement, operation);
return this;
}
this.qb.where(statement, operation, value);
return this;
}
/**
* Set a where not statement in your query.
*/
whereNot(statement, value) {
if (Is.Function(statement)) {
const driver = this.clone();
this.qb.whereNot(function () {
statement(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
if (value === undefined) {
this.qb.whereNot(statement);
return this;
}
this.qb.whereNot(statement, value);
return this;
}
/**
* Set a where raw statement in your query.
*/
whereRaw(sql, bindings) {
this.qb.whereRaw(sql, bindings);
return this;
}
/**
* Set a where exists statement in your query.
*/
whereExists(closure) {
const driver = this.clone();
this.qb.whereExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set a where not exists statement in your query.
*/
whereNotExists(closure) {
const driver = this.clone();
this.qb.whereNotExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set a where like statement in your query.
*/
whereLike(column, value) {
this.qb.whereLike(column, value);
return this;
}
/**
* Set a where ILike statement in your query.
*/
whereILike(column, value) {
this.qb.whereILike(column, value);
return this;
}
/**
* Set a where in statement in your query.
*/
whereIn(column, values) {
this.qb.whereIn(column, values);
return this;
}
/**
* Set a where not in statement in your query.
*/
whereNotIn(column, values) {
this.qb.whereNotIn(column, values);
return this;
}
/**
* Set a where between statement in your query.
*/
whereBetween(column, values) {
this.qb.whereBetween(column, values);
return this;
}
/**
* Set a where not between statement in your query.
*/
whereNotBetween(column, values) {
this.qb.whereNotBetween(column, values);
return this;
}
/**
* Set a where null statement in your query.
*/
whereNull(column) {
this.qb.whereNull(column);
return this;
}
/**
* Set a where not null statement in your query.
*/
whereNotNull(column) {
this.qb.whereNotNull(column);
return this;
}
/**
* Set a or where statement in your query.
*/
orWhere(statement, operation, value) {
if (Is.Function(statement)) {
const driver = this.clone();
this.qb.orWhere(function () {
statement(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
if (operation === undefined) {
this.qb.orWhere(statement);
return this;
}
if (value === undefined) {
this.qb.orWhere(statement, operation);
return this;
}
this.qb.orWhere(statement, operation, value);
return this;
}
/**
* Set an or where not statement in your query.
*/
orWhereNot(statement, value) {
if (Is.Function(statement)) {
const driver = this.clone();
this.qb.orWhereNot(function () {
statement(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
if (value === undefined) {
this.qb.orWhereNot(statement);
return this;
}
this.qb.orWhereNot(statement, value);
return this;
}
/**
* Set a or where raw statement in your query.
*/
orWhereRaw(sql, bindings) {
this.qb.orWhereRaw(sql, bindings);
return this;
}
/**
* Set an or where exists statement in your query.
*/
orWhereExists(closure) {
const driver = this.clone();
this.qb.orWhereExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set an or where not exists statement in your query.
*/
orWhereNotExists(closure) {
const driver = this.clone();
this.qb.orWhereNotExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set an or where like statement in your query.
*/
orWhereLike(column, value) {
this.qb.orWhereLike(column, value);
return this;
}
/**
* Set an or where ILike statement in your query.
*/
orWhereILike(column, value) {
this.qb.orWhereILike(column, value);
return this;
}
/**
* Set an or where in statement in your query.
*/
orWhereIn(column, values) {
this.qb.orWhereIn(column, values);
return this;
}
/**
* Set an or where not in statement in your query.
*/
orWhereNotIn(column, values) {
this.qb.orWhereNotIn(column, values);
return this;
}
/**
* Set an or where between statement in your query.
*/
orWhereBetween(column, values) {
this.qb.orWhereBetween(column, values);
return this;
}
/**
* Set an or where not between statement in your query.
*/
orWhereNotBetween(column, values) {
this.qb.orWhereNotBetween(column, values);
return this;
}
/**
* Set an or where null statement in your query.
*/
orWhereNull(column) {
this.qb.orWhereNull(column);
return this;
}
/**
* Set an or where not null statement in your query.
*/
orWhereNotNull(column) {
this.qb.orWhereNotNull(column);
return this;
}
/**
* Set an order by statement in your query.
*/
orderBy(column, direction = 'ASC') {
this.qb.orderBy(column, direction.toUpperCase());
return this;
}
/**
* Set an order by raw statement in your query.
*/
orderByRaw(sql, bindings) {
this.qb.orderByRaw(sql, bindings);
return this;
}
/**
* Order the results easily by the latest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
latest(column = 'createdAt') {
return this.orderBy(column, 'DESC');
}
/**
* Order the results easily by the oldest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
oldest(column = 'createdAt') {
return this.orderBy(column, 'ASC');
}
/**
* Set the skip number in your query.
*/
offset(number) {
this.qb.offset(number);
return this;
}
/**
* Set the limit number in your query.
*/
limit(number) {
this.qb.limit(number);
return this;
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { type PaginatedResponse, type PaginationOptions } from '@athenna/common';
import type { Knex } from 'knex';
import { Driver } from '#src/database/drivers/Driver';
import { Transaction } from '#src/database/transactions/Transaction';
import type { ConnectionOptions, Direction, Operations } from '#src/types';
export declare class PostgresDriver extends Driver<Knex, Knex.QueryBuilder> {
/**
* Connect to database.
*/
connect(options?: ConnectionOptions): void;
/**
* Close the connection with database in this instance.
*/
close(): Promise<void>;
/**
* Creates a new instance of query builder.
*/
query(): Knex.QueryBuilder;
/**
* Sync a model schema with database.
*/
sync(): Promise<void>;
/**
* Create a new transaction.
*/
startTransaction(): Promise<Transaction<Knex.Transaction, Knex.QueryBuilder>>;
/**
* Commit the transaction.
*/
commitTransaction(): Promise<void>;
/**
* Rollback the transaction.
*/
rollbackTransaction(): Promise<void>;
/**
* Run database migrations.
*/
runMigrations(): Promise<void>;
/**
* Revert database migrations.
*/
revertMigrations(): Promise<void>;
/**
* List all databases available.
*/
getDatabases(): Promise<string[]>;
/**
* Get the current database name.
*/
getCurrentDatabase(): Promise<string | undefined>;
/**
* Verify if database exists.
*/
hasDatabase(database: string): Promise<boolean>;
/**
* Create a new database.
*/
createDatabase(database: string): Promise<void>;
/**
* Drop some database.
*/
dropDatabase(database: string): Promise<void>;
/**
* List all tables available.
*/
getTables(): Promise<string[]>;
/**
* Verify if table exists.
*/
hasTable(table: string): Promise<boolean>;
/**
* Create a new table in database.
*/
createTable(table: string, closure: (builder: Knex.TableBuilder) => void | Promise<void>): Promise<void>;
/**
* Alter a table in database.
*/
alterTable(table: string, closure: (builder: Knex.TableBuilder) => void | Promise<void>): Promise<void>;
/**
* Drop a table in database.
*/
dropTable(table: string): Promise<void>;
/**
* Remove all data inside some database table
* and restart the identity of the table.
*/
truncate(table: string): Promise<void>;
/**
* Make a raw query in database.
*/
raw<T = any>(sql: string, bindings?: any): T;
/**
* Calculate the average of a given column.
*/
avg(column: string): Promise<string>;
/**
* Calculate the average of a given column using distinct.
*/
avgDistinct(column: string): Promise<string>;
/**
* Get the max number of a given column.
*/
max(column: string): Promise<string>;
/**
* Get the min number of a given column.
*/
min(column: string): Promise<string>;
/**
* Sum all numbers of a given column.
*/
sum(column: string): Promise<string>;
/**
* Sum all numbers of a given column in distinct mode.
*/
sumDistinct(column: string): Promise<string>;
/**
* Increment a value of a given column.
*/
increment(column: string): Promise<void>;
/**
* Decrement a value of a given column.
*/
decrement(column: string): Promise<void>;
/**
* Calculate the average of a given column using distinct.
*/
count(column?: string): Promise<string>;
/**
* Calculate the average of a given column using distinct.
*/
countDistinct(column: string): Promise<string>;
/**
* Find a value in database.
*/
find<T = any>(): Promise<T>;
/**
* Find many values in database.
*/
findMany<T = any>(): Promise<T[]>;
/**
* Find many values in database and return as paginated response.
*/
paginate<T = any>(page?: PaginationOptions | number, limit?: number, resourceUrl?: string): Promise<PaginatedResponse<T>>;
/**
* Create a value in database.
*/
create<T = any>(data?: Partial<T>): Promise<T>;
/**
* Create many values in database.
*/
createMany<T = any>(data?: Partial<T>[]): Promise<T[]>;
/**
* Create data or update if already exists.
*/
createOrUpdate<T = any>(data?: Partial<T>): Promise<T | T[]>;
/**
* Update a value in database.
*/
update<T = any>(data: Partial<T>): Promise<T | T[]>;
/**
* Delete one value in database.
*/
delete(): Promise<void>;
/**
* Set the table that this query will be executed.
*/
table(table: string): this;
/**
* Log in console the actual query built.
*/
dump(): this;
/**
* Set the columns that should be selected on query.
*/
select(...columns: string[]): this;
/**
* Set the columns that should be selected on query raw.
*/
selectRaw(sql: string, bindings?: any): this;
/**
* Set the table that should be used on query.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
from(table: string): this;
/**
* Set the table that should be used on query raw.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
fromRaw(sql: string, bindings?: any): this;
/**
* Set a join statement in your query.
*/
join(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a left join statement in your query.
*/
leftJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a right join statement in your query.
*/
rightJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a cross join statement in your query.
*/
crossJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a full outer join statement in your query.
*/
fullOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a left outer join statement in your query.
*/
leftOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a right outer join statement in your query.
*/
rightOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a join raw statement in your query.
*/
joinRaw(sql: string, bindings?: any): this;
/**
* Set a group by statement in your query.
*/
groupBy(...columns: string[]): this;
/**
* Set a group by raw statement in your query.
*/
groupByRaw(sql: string, bindings?: any): this;
having(column: string): this;
having(column: string, value: any): this;
having(column: string, operation: Operations, value: any): this;
/**
* Set a having raw statement in your query.
*/
havingRaw(sql: string, bindings?: any): this;
/**
* Set a having exists statement in your query.
*/
havingExists(closure: (query: PostgresDriver) => void): this;
/**
* Set a having not exists statement in your query.
*/
havingNotExists(closure: (query: PostgresDriver) => void): this;
/**
* Set a having in statement in your query.
*/
havingIn(column: string, values: any[]): this;
/**
* Set a having not in statement in your query.
*/
havingNotIn(column: string, values: any[]): this;
/**
* Set a having between statement in your query.
*/
havingBetween(column: string, values: [any, any]): this;
/**
* Set a having not between statement in your query.
*/
havingNotBetween(column: string, values: [any, any]): this;
/**
* Set a having null statement in your query.
*/
havingNull(column: string): this;
/**
* Set a having not null statement in your query.
*/
havingNotNull(column: string): this;
orHaving(column: string): this;
orHaving(column: string, value: any): this;
orHaving(column: string, operation: Operations, value: any): this;
/**
* Set an or having raw statement in your query.
*/
orHavingRaw(sql: string, bindings?: any): this;
/**
* Set an or having exists statement in your query.
*/
orHavingExists(closure: (query: PostgresDriver) => void): this;
/**
* Set an or having not exists statement in your query.
*/
orHavingNotExists(closure: (query: PostgresDriver) => void): this;
/**
* Set an or having in statement in your query.
*/
orHavingIn(column: string, values: any[]): this;
/**
* Set an or having not in statement in your query.
*/
orHavingNotIn(column: string, values: any[]): this;
/**
* Set an or having between statement in your query.
*/
orHavingBetween(column: string, values: [any, any]): this;
/**
* Set an or having not between statement in your query.
*/
orHavingNotBetween(column: string, values: [any, any]): this;
/**
* Set an or having null statement in your query.
*/
orHavingNull(column: string): this;
/**
* Set an or having not null statement in your query.
*/
orHavingNotNull(column: string): this;
where(statement: Record<string, any>): this;
where(key: string, value: any): this;
where(key: string, operation: Operations, value: any): this;
whereNot(statement: Record<string, any>): this;
whereNot(key: string, value: any): this;
/**
* Set a where raw statement in your query.
*/
whereRaw(sql: string, bindings?: any): this;
/**
* Set a where exists statement in your query.
*/
whereExists(closure: (query: PostgresDriver) => void): this;
/**
* Set a where not exists statement in your query.
*/
whereNotExists(closure: (query: PostgresDriver) => void): this;
/**
* Set a where like statement in your query.
*/
whereLike(column: string, value: any): this;
/**
* Set a where ILike statement in your query.
*/
whereILike(column: string, value: any): this;
/**
* Set a where in statement in your query.
*/
whereIn(column: string, values: any[]): this;
/**
* Set a where not in statement in your query.
*/
whereNotIn(column: string, values: any[]): this;
/**
* Set a where between statement in your query.
*/
whereBetween(column: string, values: [any, any]): this;
/**
* Set a where not between statement in your query.
*/
whereNotBetween(column: string, values: [any, any]): this;
/**
* Set a where null statement in your query.
*/
whereNull(column: string): this;
/**
* Set a where not null statement in your query.
*/
whereNotNull(column: string): this;
orWhere(statement: Record<string, any>): this;
orWhere(key: string, value: any): this;
orWhere(key: string, operation: Operations, value: any): this;
orWhereNot(statement: Record<string, any>): this;
orWhereNot(key: string, value: any): this;
/**
* Set a or where raw statement in your query.
*/
orWhereRaw(sql: string, bindings?: any): this;
/**
* Set an or where exists statement in your query.
*/
orWhereExists(closure: (query: PostgresDriver) => void): this;
/**
* Set an or where not exists statement in your query.
*/
orWhereNotExists(closure: (query: PostgresDriver) => void): this;
/**
* Set an or where like statement in your query.
*/
orWhereLike(column: string, value: any): this;
/**
* Set an or where ILike statement in your query.
*/
orWhereILike(column: string, value: any): this;
/**
* Set an or where in statement in your query.
*/
orWhereIn(column: string, values: any[]): this;
/**
* Set an or where not in statement in your query.
*/
orWhereNotIn(column: string, values: any[]): this;
/**
* Set an or where between statement in your query.
*/
orWhereBetween(column: string, values: [any, any]): this;
/**
* Set an or where not between statement in your query.
*/
orWhereNotBetween(column: string, values: [any, any]): this;
/**
* Set an or where null statement in your query.
*/
orWhereNull(column: string): this;
/**
* Set an or where not null statement in your query.
*/
orWhereNotNull(column: string): this;
/**
* Set an order by statement in your query.
*/
orderBy(column: string, direction?: Direction): this;
/**
* Set an order by raw statement in your query.
*/
orderByRaw(sql: string, bindings?: any): this;
/**
* Order the results easily by the latest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
latest(column?: string): this;
/**
* Order the results easily by the oldest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
oldest(column?: string): this;
/**
* Set the skip number in your query.
*/
offset(number: number): this;
/**
* Set the limit number in your query.
*/
limit(number: number): this;
}
/* eslint-disable @typescript-eslint/ban-ts-comment */
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Exec, Is, Json, Options } from '@athenna/common';
import { debug } from '#src/debug';
import { Log } from '@athenna/logger';
import { Driver } from '#src/database/drivers/Driver';
import { ConnectionFactory } from '#src/factories/ConnectionFactory';
import { Transaction } from '#src/database/transactions/Transaction';
import { MigrationSource } from '#src/database/migrations/MigrationSource';
import { WrongMethodException } from '#src/exceptions/WrongMethodException';
import { PROTECTED_QUERY_METHODS } from '#src/constants/ProtectedQueryMethods';
import { NotConnectedDatabaseException } from '#src/exceptions/NotConnectedDatabaseException';
export class PostgresDriver extends Driver {
/**
* Connect to database.
*/
connect(options = {}) {
options = Options.create(options, {
force: false,
saveOnFactory: true,
connect: true
});
if (!options.connect) {
return;
}
if (this.isConnected && !options.force) {
return;
}
const knex = this.getKnex();
const configs = Config.get(`database.connections.${this.connection}`, {});
const knexOpts = {
client: 'pg',
migrations: {
tableName: 'migrations'
},
pool: {
min: 2,
max: 20,
acquireTimeoutMillis: 60 * 1000
},
debug: false,
useNullAsDefault: false,
...Json.omit(configs, ['driver', 'validations'])
};
debug('creating new connection using Knex. options defined: %o', knexOpts);
if (Config.is('rc.bootLogs', true)) {
Log.channelOrVanilla('application').success(`Successfully connected to ({yellow} ${this.connection}) database connection`);
}
this.client = knex.default(knexOpts);
this.isConnected = true;
this.isSavedOnFactory = options.saveOnFactory;
if (this.isSavedOnFactory) {
ConnectionFactory.setClient(this.connection, this.client);
}
this.qb = this.query();
}
/**
* Close the connection with database in this instance.
*/
async close() {
if (!this.isConnected) {
return;
}
await this.client.destroy();
this.qb = null;
this.tableName = null;
this.client = null;
this.isConnected = false;
ConnectionFactory.setClient(this.connection, null);
}
/**
* Creates a new instance of query builder.
*/
query() {
if (!this.isConnected) {
throw new NotConnectedDatabaseException();
}
const query = this.useSetQB
? this.qb.table(this.tableName)
: this.client.queryBuilder().table(this.tableName);
const handler = {
get: (target, propertyKey) => {
if (PROTECTED_QUERY_METHODS.includes(propertyKey)) {
this.qb = this.query();
}
return target[propertyKey];
}
};
return new Proxy(query, handler);
}
/**
* Sync a model schema with database.
*/
async sync() {
debug(`database sync with ${PostgresDriver.name} is not available yet, use migration instead.`);
}
/**
* Create a new transaction.
*/
async startTransaction() {
const trx = await this.client.transaction();
return new Transaction(this.clone().setClient(trx));
}
/**
* Commit the transaction.
*/
async commitTransaction() {
const client = this.client;
await client.commit();
this.tableName = null;
this.client = null;
this.isConnected = false;
}
/**
* Rollback the transaction.
*/
async rollbackTransaction() {
const client = this.client;
await client.rollback();
this.tableName = null;
this.client = null;
this.isConnected = false;
}
/**
* Run database migrations.
*/
async runMigrations() {
await this.client.migrate.latest({
migrationSource: new MigrationSource(this.connection)
});
}
/**
* Revert database migrations.
*/
async revertMigrations() {
await this.client.migrate.rollback({
migrationSource: new MigrationSource(this.connection)
});
}
/**
* List all databases available.
*/
async getDatabases() {
const { rows: databases } = await this.raw('SELECT datname FROM pg_database');
return databases.map(database => database.datname);
}
/**
* Get the current database name.
*/
async getCurrentDatabase() {
return this.client.client.database();
}
/**
* Verify if database exists.
*/
async hasDatabase(database) {
const databases = await this.getDatabases();
return databases.includes(database);
}
/**
* Create a new database.
*/
async createDatabase(database) {
/**
* Catching the error to simulate IF NOT EXISTS
*/
try {
await this.raw('CREATE DATABASE ??', database);
}
catch (_err) { }
}
/**
* Drop some database.
*/
async dropDatabase(database) {
/**
* Catching the error to simulate IF EXISTS
*/
try {
await this.raw('DROP DATABASE ??', database);
}
catch (_err) { }
}
/**
* List all tables available.
*/
async getTables() {
const { rows: tables } = await this.raw('SELECT table_name FROM information_schema.tables WHERE table_schema = current_schema() AND table_catalog = ?', await this.getCurrentDatabase());
return tables.map(table => table.table_name);
}
/**
* Verify if table exists.
*/
async hasTable(table) {
return this.client.schema.hasTable(table);
}
/**
* Create a new table in database.
*/
async createTable(table, closure) {
await this.client.schema.createTable(table, closure);
}
/**
* Alter a table in database.
*/
async alterTable(table, closure) {
await this.client.schema.alterTable(table, closure);
}
/**
* Drop a table in database.
*/
async dropTable(table) {
await this.client.schema.dropTableIfExists(table);
}
/**
* Remove all data inside some database table
* and restart the identity of the table.
*/
async truncate(table) {
await this.raw('TRUNCATE TABLE ?? CASCADE', table);
}
/**
* Make a raw query in database.
*/
raw(sql, bindings) {
return this.client.raw(sql, bindings);
}
/**
* Calculate the average of a given column.
*/
async avg(column) {
const [{ avg }] = await this.qb.avg({ avg: column });
return avg;
}
/**
* Calculate the average of a given column using distinct.
*/
async avgDistinct(column) {
const [{ avg }] = await this.qb.avgDistinct({ avg: column });
return avg;
}
/**
* Get the max number of a given column.
*/
async max(column) {
const [{ max }] = await this.qb.max({ max: column });
return max;
}
/**
* Get the min number of a given column.
*/
async min(column) {
const [{ min }] = await this.qb.min({ min: column });
return min;
}
/**
* Sum all numbers of a given column.
*/
async sum(column) {
const [{ sum }] = await this.qb.sum({ sum: column });
return sum;
}
/**
* Sum all numbers of a given column in distinct mode.
*/
async sumDistinct(column) {
const [{ sum }] = await this.qb.sumDistinct({ sum: column });
return sum;
}
/**
* Increment a value of a given column.
*/
async increment(column) {
await this.qb.increment(column);
}
/**
* Decrement a value of a given column.
*/
async decrement(column) {
await this.qb.decrement(column);
}
/**
* Calculate the average of a given column using distinct.
*/
async count(column = '*') {
const [{ count }] = await this.qb.count({ count: column });
return `${count}`;
}
/**
* Calculate the average of a given column using distinct.
*/
async countDistinct(column) {
const [{ count }] = await this.qb.countDistinct({ count: column });
return `${count}`;
}
/**
* Find a value in database.
*/
async find() {
return this.qb.first();
}
/**
* Find many values in database.
*/
async findMany() {
const data = await this.qb;
this.qb = this.query();
return data;
}
/**
* Find many values in database and return as paginated response.
*/
async paginate(page = { page: 0, limit: 10, resourceUrl: '/' }, limit = 10, resourceUrl = '/') {
if (Is.Number(page)) {
page = { page, limit, resourceUrl };
}
const [{ count }] = await this.qb
.clone()
.clearOrder()
.clearSelect()
.count({ count: '*' });
const data = await this.offset(page.page * page.limit)
.limit(page.limit)
.findMany();
return Exec.pagination(data, parseInt(count), page);
}
/**
* Create a value in database.
*/
async create(data = {}) {
if (Is.Array(data)) {
throw new WrongMethodException('create', 'createMany');
}
const created = await this.createMany([data]);
return created[0];
}
/**
* Create many values in database.
*/
async createMany(data = []) {
if (!Is.Array(data)) {
throw new WrongMethodException('createMany', 'create');
}
return this.qb.insert(data, '*');
}
/**
* Create data or update if already exists.
*/
async createOrUpdate(data = {}) {
const query = this.qb.clone();
const hasValue = await query.first();
if (hasValue) {
await this.qb
.where(this.primaryKey, hasValue[this.primaryKey])
.update(data);
return this.where(this.primaryKey, hasValue[this.primaryKey]).find();
}
return this.create(data);
}
/**
* Update a value in database.
*/
async update(data) {
await this.qb.clone().update(data);
const result = await this.findMany();
if (result.length === 1) {
return result[0];
}
return result;
}
/**
* Delete one value in database.
*/
async delete() {
await this.qb.delete();
}
/**
* Set the table that this query will be executed.
*/
table(table) {
if (!this.isConnected) {
throw new NotConnectedDatabaseException();
}
this.tableName = table;
this.qb = this.query();
return this;
}
/**
* Log in console the actual query built.
*/
dump() {
console.log(this.qb.toSQL().toNative());
return this;
}
/**
* Set the columns that should be selected on query.
*/
select(...columns) {
this.qb.select(...columns);
return this;
}
/**
* Set the columns that should be selected on query raw.
*/
selectRaw(sql, bindings) {
return this.select(this.raw(sql, bindings));
}
/**
* Set the table that should be used on query.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
from(table) {
this.qb.from(table);
return this;
}
/**
* Set the table that should be used on query raw.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
fromRaw(sql, bindings) {
return this.from(this.raw(sql, bindings));
}
/**
* Set a join statement in your query.
*/
join(table, column1, operation, column2) {
return this.joinByType('join', table, column1, operation, column2);
}
/**
* Set a left join statement in your query.
*/
leftJoin(table, column1, operation, column2) {
return this.joinByType('leftJoin', table, column1, operation, column2);
}
/**
* Set a right join statement in your query.
*/
rightJoin(table, column1, operation, column2) {
return this.joinByType('rightJoin', table, column1, operation, column2);
}
/**
* Set a cross join statement in your query.
*/
crossJoin(table, column1, operation, column2) {
return this.joinByType('crossJoin', table, column1, operation, column2);
}
/**
* Set a full outer join statement in your query.
*/
fullOuterJoin(table, column1, operation, column2) {
return this.joinByType('fullOuterJoin', table, column1, operation, column2);
}
/**
* Set a left outer join statement in your query.
*/
leftOuterJoin(table, column1, operation, column2) {
return this.joinByType('leftOuterJoin', table, column1, operation, column2);
}
/**
* Set a right outer join statement in your query.
*/
rightOuterJoin(table, column1, operation, column2) {
return this.joinByType('rightOuterJoin', table, column1, operation, column2);
}
/**
* Set a join raw statement in your query.
*/
joinRaw(sql, bindings) {
this.qb.joinRaw(sql, bindings);
return this;
}
/**
* Set a group by statement in your query.
*/
groupBy(...columns) {
this.qb.groupBy(...columns);
return this;
}
/**
* Set a group by raw statement in your query.
*/
groupByRaw(sql, bindings) {
this.qb.groupByRaw(sql, bindings);
return this;
}
/**
* Set a having statement in your query.
*/
having(column, operation, value) {
if (operation === undefined) {
this.qb.having(column);
return this;
}
if (value === undefined) {
this.qb.having(column, '=', operation);
return this;
}
this.qb.having(column, operation, value);
return this;
}
/**
* Set a having raw statement in your query.
*/
havingRaw(sql, bindings) {
this.qb.havingRaw(sql, bindings);
return this;
}
/**
* Set a having exists statement in your query.
*/
havingExists(closure) {
const driver = this.clone();
// @ts-ignore
this.qb.havingExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set a having not exists statement in your query.
*/
havingNotExists(closure) {
const driver = this.clone();
// @ts-ignore
this.qb.havingNotExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set a having in statement in your query.
*/
havingIn(column, values) {
this.qb.havingIn(column, values);
return this;
}
/**
* Set a having not in statement in your query.
*/
havingNotIn(column, values) {
this.qb.havingNotIn(column, values);
return this;
}
/**
* Set a having between statement in your query.
*/
havingBetween(column, values) {
this.qb.havingBetween(column, values);
return this;
}
/**
* Set a having not between statement in your query.
*/
havingNotBetween(column, values) {
this.qb.havingNotBetween(column, values);
return this;
}
/**
* Set a having null statement in your query.
*/
havingNull(column) {
this.qb.havingNull(column);
return this;
}
/**
* Set a having not null statement in your query.
*/
havingNotNull(column) {
this.qb.havingNotNull(column);
return this;
}
/**
* Set an or having statement in your query.
*/
orHaving(column, operation, value) {
if (operation === undefined) {
this.qb.orHaving(column);
return this;
}
if (value === undefined) {
this.qb.orHaving(column, '=', operation);
return this;
}
this.qb.orHaving(column, operation, value);
return this;
}
/**
* Set an or having raw statement in your query.
*/
orHavingRaw(sql, bindings) {
this.qb.orHavingRaw(sql, bindings);
return this;
}
/**
* Set an or having exists statement in your query.
*/
orHavingExists(closure) {
const driver = this.clone();
// @ts-ignore
this.qb.orHavingExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set an or having not exists statement in your query.
*/
orHavingNotExists(closure) {
const driver = this.clone();
// @ts-ignore
this.qb.orHavingNotExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set an or having in statement in your query.
*/
orHavingIn(column, values) {
// @ts-ignore
this.qb.orHavingIn(column, values);
return this;
}
/**
* Set an or having not in statement in your query.
*/
orHavingNotIn(column, values) {
this.qb.orHavingNotIn(column, values);
return this;
}
/**
* Set an or having between statement in your query.
*/
orHavingBetween(column, values) {
this.qb.orHavingBetween(column, values);
return this;
}
/**
* Set an or having not between statement in your query.
*/
orHavingNotBetween(column, values) {
this.qb.orHavingNotBetween(column, values);
return this;
}
/**
* Set an or having null statement in your query.
*/
orHavingNull(column) {
// @ts-ignore
this.qb.orHavingNull(column);
return this;
}
/**
* Set an or having not null statement in your query.
*/
orHavingNotNull(column) {
// @ts-ignore
this.qb.orHavingNotNull(column);
return this;
}
/**
* Set a where statement in your query.
*/
where(statement, operation, value) {
if (Is.Function(statement)) {
const driver = this.clone();
this.qb.where(function () {
statement(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
if (operation === undefined) {
if (Is.Array(statement)) {
throw new Error('Arrays as statement are not supported.');
}
if (Is.String(statement)) {
throw new Error(`The value for the "${statement}" column is undefined and where will not work.`);
}
this.qb.where(statement);
return this;
}
if (value === undefined) {
this.qb.where(statement, operation);
return this;
}
this.qb.where(statement, operation, value);
return this;
}
/**
* Set a where not statement in your query.
*/
whereNot(statement, value) {
if (Is.Function(statement)) {
const driver = this.clone();
this.qb.whereNot(function () {
statement(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
if (value === undefined) {
this.qb.whereNot(statement);
return this;
}
this.qb.whereNot(statement, value);
return this;
}
/**
* Set a where raw statement in your query.
*/
whereRaw(sql, bindings) {
this.qb.whereRaw(sql, bindings);
return this;
}
/**
* Set a where exists statement in your query.
*/
whereExists(closure) {
const driver = this.clone();
this.qb.whereExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set a where not exists statement in your query.
*/
whereNotExists(closure) {
const driver = this.clone();
this.qb.whereNotExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set a where like statement in your query.
*/
whereLike(column, value) {
this.qb.whereLike(column, value);
return this;
}
/**
* Set a where ILike statement in your query.
*/
whereILike(column, value) {
this.qb.whereILike(column, value);
return this;
}
/**
* Set a where in statement in your query.
*/
whereIn(column, values) {
this.qb.whereIn(column, values);
return this;
}
/**
* Set a where not in statement in your query.
*/
whereNotIn(column, values) {
this.qb.whereNotIn(column, values);
return this;
}
/**
* Set a where between statement in your query.
*/
whereBetween(column, values) {
this.qb.whereBetween(column, values);
return this;
}
/**
* Set a where not between statement in your query.
*/
whereNotBetween(column, values) {
this.qb.whereNotBetween(column, values);
return this;
}
/**
* Set a where null statement in your query.
*/
whereNull(column) {
this.qb.whereNull(column);
return this;
}
/**
* Set a where not null statement in your query.
*/
whereNotNull(column) {
this.qb.whereNotNull(column);
return this;
}
/**
* Set a or where statement in your query.
*/
orWhere(statement, operation, value) {
if (Is.Function(statement)) {
const driver = this.clone();
this.qb.orWhere(function () {
statement(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
if (operation === undefined) {
this.qb.orWhere(statement);
return this;
}
if (value === undefined) {
this.qb.orWhere(statement, operation);
return this;
}
this.qb.orWhere(statement, operation, value);
return this;
}
/**
* Set an or where not statement in your query.
*/
orWhereNot(statement, value) {
if (Is.Function(statement)) {
const driver = this.clone();
this.qb.orWhereNot(function () {
statement(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
if (value === undefined) {
this.qb.orWhereNot(statement);
return this;
}
this.qb.orWhereNot(statement, value);
return this;
}
/**
* Set a or where raw statement in your query.
*/
orWhereRaw(sql, bindings) {
this.qb.orWhereRaw(sql, bindings);
return this;
}
/**
* Set an or where exists statement in your query.
*/
orWhereExists(closure) {
const driver = this.clone();
this.qb.orWhereExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set an or where not exists statement in your query.
*/
orWhereNotExists(closure) {
const driver = this.clone();
this.qb.orWhereNotExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set an or where like statement in your query.
*/
orWhereLike(column, value) {
this.qb.orWhereLike(column, value);
return this;
}
/**
* Set an or where ILike statement in your query.
*/
orWhereILike(column, value) {
this.qb.orWhereILike(column, value);
return this;
}
/**
* Set an or where in statement in your query.
*/
orWhereIn(column, values) {
this.qb.orWhereIn(column, values);
return this;
}
/**
* Set an or where not in statement in your query.
*/
orWhereNotIn(column, values) {
this.qb.orWhereNotIn(column, values);
return this;
}
/**
* Set an or where between statement in your query.
*/
orWhereBetween(column, values) {
this.qb.orWhereBetween(column, values);
return this;
}
/**
* Set an or where not between statement in your query.
*/
orWhereNotBetween(column, values) {
this.qb.orWhereNotBetween(column, values);
return this;
}
/**
* Set an or where null statement in your query.
*/
orWhereNull(column) {
this.qb.orWhereNull(column);
return this;
}
/**
* Set an or where not null statement in your query.
*/
orWhereNotNull(column) {
this.qb.orWhereNotNull(column);
return this;
}
/**
* Set an order by statement in your query.
*/
orderBy(column, direction = 'ASC') {
this.qb.orderBy(column, direction.toUpperCase());
return this;
}
/**
* Set an order by raw statement in your query.
*/
orderByRaw(sql, bindings) {
this.qb.orderByRaw(sql, bindings);
return this;
}
/**
* Order the results easily by the latest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
latest(column = 'createdAt') {
return this.orderBy(column, 'DESC');
}
/**
* Order the results easily by the oldest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
oldest(column = 'createdAt') {
return this.orderBy(column, 'ASC');
}
/**
* Set the skip number in your query.
*/
offset(number) {
this.qb.offset(number);
return this;
}
/**
* Set the limit number in your query.
*/
limit(number) {
this.qb.limit(number);
return this;
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { type PaginatedResponse, type PaginationOptions } from '@athenna/common';
import type { Knex } from 'knex';
import { Driver } from '#src/database/drivers/Driver';
import { Transaction } from '#src/database/transactions/Transaction';
import type { ConnectionOptions, Direction, Operations } from '#src/types';
export declare class SqliteDriver extends Driver<Knex, Knex.QueryBuilder> {
/**
* Connect to database.
*/
connect(options?: ConnectionOptions): void;
/**
* Close the connection with database in this instance.
*/
close(): Promise<void>;
/**
* Creates a new instance of query builder.
*/
query(): Knex.QueryBuilder;
/**
* Sync a model schema with database.
*/
sync(): Promise<void>;
/**
* Create a new transaction.
*/
startTransaction(): Promise<Transaction<Knex.Transaction, Knex.QueryBuilder>>;
/**
* Commit the transaction.
*/
commitTransaction(): Promise<void>;
/**
* Rollback the transaction.
*/
rollbackTransaction(): Promise<void>;
/**
* Run database migrations.
*/
runMigrations(): Promise<void>;
/**
* Revert database migrations.
*/
revertMigrations(): Promise<void>;
/**
* List all databases available.
*/
getDatabases(): Promise<string[]>;
/**
* Get the current database name.
*/
getCurrentDatabase(): Promise<string | undefined>;
/**
* Verify if database exists.
*/
hasDatabase(database: string): Promise<boolean>;
/**
* Create a new database.
*/
createDatabase(database: string): Promise<void>;
/**
* Drop some database.
*/
dropDatabase(database: string): Promise<void>;
/**
* List all tables available.
*/
getTables(): Promise<string[]>;
/**
* Verify if table exists.
*/
hasTable(table: string): Promise<boolean>;
/**
* Create a new table in database.
*/
createTable(table: string, closure: (builder: Knex.TableBuilder) => void | Promise<void>): Promise<void>;
/**
* Alter a table in database.
*/
alterTable(table: string, closure: (builder: Knex.TableBuilder) => void | Promise<void>): Promise<void>;
/**
* Drop a table in database.
*/
dropTable(table: string): Promise<void>;
/**
* Remove all data inside some database table
* and restart the identity of the table.
*/
truncate(table: string): Promise<void>;
/**
* Make a raw query in database.
*/
raw<T = any>(sql: string, bindings?: any): T;
/**
* Calculate the average of a given column.
*/
avg(column: string): Promise<string>;
/**
* Calculate the average of a given column using distinct.
*/
avgDistinct(column: string): Promise<string>;
/**
* Get the max number of a given column.
*/
max(column: string): Promise<string>;
/**
* Get the min number of a given column.
*/
min(column: string): Promise<string>;
/**
* Sum all numbers of a given column.
*/
sum(column: string): Promise<string>;
/**
* Sum all numbers of a given column in distinct mode.
*/
sumDistinct(column: string): Promise<string>;
/**
* Increment a value of a given column.
*/
increment(column: string): Promise<void>;
/**
* Decrement a value of a given column.
*/
decrement(column: string): Promise<void>;
/**
* Calculate the average of a given column using distinct.
*/
count(column?: string): Promise<string>;
/**
* Calculate the average of a given column using distinct.
*/
countDistinct(column: string): Promise<string>;
/**
* Find a value in database.
*/
find<T = any>(): Promise<T>;
/**
* Find many values in database.
*/
findMany<T = any>(): Promise<T[]>;
/**
* Find many values in database and return as paginated response.
*/
paginate<T = any>(page?: PaginationOptions | number, limit?: number, resourceUrl?: string): Promise<PaginatedResponse<T>>;
/**
* Create a value in database.
*/
create<T = any>(data?: Partial<T>): Promise<T>;
/**
* Create many values in database.
*/
createMany<T = any>(data?: Partial<T>[]): Promise<T[]>;
/**
* Create data or update if already exists.
*/
createOrUpdate<T = any>(data?: Partial<T>): Promise<T | T[]>;
/**
* Update a value in database.
*/
update<T = any>(data: Partial<T>): Promise<T | T[]>;
/**
* Delete one value in database.
*/
delete(): Promise<void>;
/**
* Set the table that this query will be executed.
*/
table(table: string): this;
/**
* Log in console the actual query built.
*/
dump(): this;
/**
* Set the columns that should be selected on query.
*/
select(...columns: string[]): this;
/**
* Set the columns that should be selected on query raw.
*/
selectRaw(sql: string, bindings?: any): this;
/**
* Set the table that should be used on query.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
from(table: string): this;
/**
* Set the table that should be used on query raw.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
fromRaw(sql: string, bindings?: any): this;
/**
* Set a join statement in your query.
*/
join(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a left join statement in your query.
*/
leftJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a right join statement in your query.
*/
rightJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a cross join statement in your query.
*/
crossJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a full outer join statement in your query.
*/
fullOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a left outer join statement in your query.
*/
leftOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a right outer join statement in your query.
*/
rightOuterJoin(table: any, column1?: any, operation?: any | Operations, column2?: any): this;
/**
* Set a join raw statement in your query.
*/
joinRaw(sql: string, bindings?: any): this;
/**
* Set a group by statement in your query.
*/
groupBy(...columns: string[]): this;
/**
* Set a group by raw statement in your query.
*/
groupByRaw(sql: string, bindings?: any): this;
having(column: string): this;
having(column: string, value: any): this;
having(column: string, operation: Operations, value: any): this;
/**
* Set a having raw statement in your query.
*/
havingRaw(sql: string, bindings?: any): this;
/**
* Set a having exists statement in your query.
*/
havingExists(closure: (query: SqliteDriver) => void): this;
/**
* Set a having not exists statement in your query.
*/
havingNotExists(closure: (query: SqliteDriver) => void): this;
/**
* Set a having in statement in your query.
*/
havingIn(column: string, values: any[]): this;
/**
* Set a having not in statement in your query.
*/
havingNotIn(column: string, values: any[]): this;
/**
* Set a having between statement in your query.
*/
havingBetween(column: string, values: [any, any]): this;
/**
* Set a having not between statement in your query.
*/
havingNotBetween(column: string, values: [any, any]): this;
/**
* Set a having null statement in your query.
*/
havingNull(column: string): this;
/**
* Set a having not null statement in your query.
*/
havingNotNull(column: string): this;
orHaving(column: string): this;
orHaving(column: string, value: any): this;
orHaving(column: string, operation: Operations, value: any): this;
/**
* Set an or having raw statement in your query.
*/
orHavingRaw(sql: string, bindings?: any): this;
/**
* Set an or having exists statement in your query.
*/
orHavingExists(closure: (query: SqliteDriver) => void): this;
/**
* Set an or having not exists statement in your query.
*/
orHavingNotExists(closure: (query: SqliteDriver) => void): this;
/**
* Set an or having in statement in your query.
*/
orHavingIn(column: string, values: any[]): this;
/**
* Set an or having not in statement in your query.
*/
orHavingNotIn(column: string, values: any[]): this;
/**
* Set an or having between statement in your query.
*/
orHavingBetween(column: string, values: [any, any]): this;
/**
* Set an or having not between statement in your query.
*/
orHavingNotBetween(column: string, values: [any, any]): this;
/**
* Set an or having null statement in your query.
*/
orHavingNull(column: string): this;
/**
* Set an or having not null statement in your query.
*/
orHavingNotNull(column: string): this;
where(statement: Record<string, any>): this;
where(key: string, value: any): this;
where(key: string, operation: Operations, value: any): this;
whereNot(statement: Record<string, any>): this;
whereNot(key: string, value: any): this;
/**
* Set a where raw statement in your query.
*/
whereRaw(sql: string, bindings?: any): this;
/**
* Set a where exists statement in your query.
*/
whereExists(closure: (query: SqliteDriver) => void): this;
/**
* Set a where not exists statement in your query.
*/
whereNotExists(closure: (query: SqliteDriver) => void): this;
/**
* Set a where like statement in your query.
*/
whereLike(column: string, value: any): this;
/**
* Set a where ILike statement in your query.
*/
whereILike(column: string, value: any): this;
/**
* Set a where in statement in your query.
*/
whereIn(column: string, values: any[]): this;
/**
* Set a where not in statement in your query.
*/
whereNotIn(column: string, values: any[]): this;
/**
* Set a where between statement in your query.
*/
whereBetween(column: string, values: [any, any]): this;
/**
* Set a where not between statement in your query.
*/
whereNotBetween(column: string, values: [any, any]): this;
/**
* Set a where null statement in your query.
*/
whereNull(column: string): this;
/**
* Set a where not null statement in your query.
*/
whereNotNull(column: string): this;
orWhere(statement: Record<string, any>): this;
orWhere(key: string, value: any): this;
orWhere(key: string, operation: Operations, value: any): this;
orWhereNot(statement: Record<string, any>): this;
orWhereNot(key: string, value: any): this;
/**
* Set a or where raw statement in your query.
*/
orWhereRaw(sql: string, bindings?: any): this;
/**
* Set an or where exists statement in your query.
*/
orWhereExists(closure: (query: SqliteDriver) => void): this;
/**
* Set an or where not exists statement in your query.
*/
orWhereNotExists(closure: (query: SqliteDriver) => void): this;
/**
* Set an or where like statement in your query.
*/
orWhereLike(column: string, value: any): this;
/**
* Set an or where ILike statement in your query.
*/
orWhereILike(column: string, value: any): this;
/**
* Set an or where in statement in your query.
*/
orWhereIn(column: string, values: any[]): this;
/**
* Set an or where not in statement in your query.
*/
orWhereNotIn(column: string, values: any[]): this;
/**
* Set an or where between statement in your query.
*/
orWhereBetween(column: string, values: [any, any]): this;
/**
* Set an or where not between statement in your query.
*/
orWhereNotBetween(column: string, values: [any, any]): this;
/**
* Set an or where null statement in your query.
*/
orWhereNull(column: string): this;
/**
* Set an or where not null statement in your query.
*/
orWhereNotNull(column: string): this;
/**
* Set an order by statement in your query.
*/
orderBy(column: string, direction?: Direction): this;
/**
* Set an order by raw statement in your query.
*/
orderByRaw(sql: string, bindings?: any): this;
/**
* Order the results easily by the latest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
latest(column?: string): this;
/**
* Order the results easily by the oldest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
oldest(column?: string): this;
/**
* Set the skip number in your query.
*/
offset(number: number): this;
/**
* Set the limit number in your query.
*/
limit(number: number): this;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { Exec, Is, Json, Options } from '@athenna/common';
import { debug } from '#src/debug';
import { Log } from '@athenna/logger';
import { Driver } from '#src/database/drivers/Driver';
import { ConnectionFactory } from '#src/factories/ConnectionFactory';
import { Transaction } from '#src/database/transactions/Transaction';
import { MigrationSource } from '#src/database/migrations/MigrationSource';
import { WrongMethodException } from '#src/exceptions/WrongMethodException';
import { PROTECTED_QUERY_METHODS } from '#src/constants/ProtectedQueryMethods';
import { NotConnectedDatabaseException } from '#src/exceptions/NotConnectedDatabaseException';
export class SqliteDriver extends Driver {
/**
* Connect to database.
*/
connect(options = {}) {
options = Options.create(options, {
force: false,
saveOnFactory: true,
connect: true
});
if (!options.connect) {
return;
}
if (this.isConnected && !options.force) {
return;
}
const knex = this.getKnex();
const configs = Config.get(`database.connections.${this.connection}`, {});
const knexOpts = {
client: 'better-sqlite3',
migrations: {
tableName: 'migrations'
},
pool: {
min: 2,
max: 20,
acquireTimeoutMillis: 60 * 1000
},
debug: false,
useNullAsDefault: false,
...Json.omit(configs, ['driver', 'validations'])
};
debug('creating new connection using Knex. options defined: %o', knexOpts);
if (Config.is('rc.bootLogs', true)) {
Log.channelOrVanilla('application').success(`Successfully connected to ({yellow} ${this.connection}) database connection`);
}
this.client = knex.default(knexOpts);
this.isConnected = true;
this.isSavedOnFactory = options.saveOnFactory;
if (this.isSavedOnFactory) {
ConnectionFactory.setClient(this.connection, this.client);
}
this.qb = this.query();
}
/**
* Close the connection with database in this instance.
*/
async close() {
if (!this.isConnected) {
return;
}
await this.client.destroy();
this.qb = null;
this.tableName = null;
this.client = null;
this.isConnected = false;
ConnectionFactory.setClient(this.connection, null);
}
/**
* Creates a new instance of query builder.
*/
query() {
if (!this.isConnected) {
throw new NotConnectedDatabaseException();
}
const query = this.useSetQB
? this.qb.table(this.tableName)
: this.client.queryBuilder().table(this.tableName);
const handler = {
get: (target, propertyKey) => {
if (PROTECTED_QUERY_METHODS.includes(propertyKey)) {
this.qb = this.query();
}
return target[propertyKey];
}
};
return new Proxy(query, handler);
}
/**
* Sync a model schema with database.
*/
async sync() {
debug(`database sync with ${SqliteDriver.name} is not available yet, use migration instead.`);
}
/**
* Create a new transaction.
*/
async startTransaction() {
const trx = await this.client.transaction();
return new Transaction(this.clone().setClient(trx));
}
/**
* Commit the transaction.
*/
async commitTransaction() {
const client = this.client;
await client.commit();
this.tableName = null;
this.client = null;
this.isConnected = false;
}
/**
* Rollback the transaction.
*/
async rollbackTransaction() {
const client = this.client;
await client.rollback();
this.tableName = null;
this.client = null;
this.isConnected = false;
}
/**
* Run database migrations.
*/
async runMigrations() {
await this.client.migrate.latest({
migrationSource: new MigrationSource(this.connection)
});
}
/**
* Revert database migrations.
*/
async revertMigrations() {
await this.client.migrate.rollback({
migrationSource: new MigrationSource(this.connection)
});
}
/**
* List all databases available.
*/
async getDatabases() {
const databases = await this.raw('PRAGMA database_list');
return databases.map(database => database.name);
}
/**
* Get the current database name.
*/
async getCurrentDatabase() {
return this.client.client.database();
}
/**
* Verify if database exists.
*/
async hasDatabase(database) {
const databases = await this.getDatabases();
return databases.includes(database);
}
/**
* Create a new database.
*/
async createDatabase(database) {
/**
* Catching the error to simulate IF NOT EXISTS
*/
try {
await this.raw('CREATE DATABASE ??', database);
}
catch (_err) { }
}
/**
* Drop some database.
*/
async dropDatabase(database) {
/**
* Catching the error to simulate IF EXISTS
*/
try {
await this.raw('DROP DATABASE ??', database);
}
catch (_err) { }
}
/**
* List all tables available.
*/
async getTables() {
const tables = await this.raw("SELECT name FROM sqlite_schema WHERE type = 'table' AND name NOT LIKE 'sqlite_%'", await this.getCurrentDatabase());
return tables.map(table => table.name);
}
/**
* Verify if table exists.
*/
async hasTable(table) {
return this.client.schema.hasTable(table);
}
/**
* Create a new table in database.
*/
async createTable(table, closure) {
await this.client.schema.createTable(table, closure);
}
/**
* Alter a table in database.
*/
async alterTable(table, closure) {
await this.client.schema.alterTable(table, closure);
}
/**
* Drop a table in database.
*/
async dropTable(table) {
await this.client.schema.dropTableIfExists(table);
}
/**
* Remove all data inside some database table
* and restart the identity of the table.
*/
async truncate(table) {
await this.raw('DELETE FROM ??', table);
}
/**
* Make a raw query in database.
*/
raw(sql, bindings) {
return this.client.raw(sql, bindings);
}
/**
* Calculate the average of a given column.
*/
async avg(column) {
const [{ avg }] = await this.qb.avg({ avg: column });
return avg;
}
/**
* Calculate the average of a given column using distinct.
*/
async avgDistinct(column) {
const [{ avg }] = await this.qb.avgDistinct({ avg: column });
return avg;
}
/**
* Get the max number of a given column.
*/
async max(column) {
const [{ max }] = await this.qb.max({ max: column });
return max;
}
/**
* Get the min number of a given column.
*/
async min(column) {
const [{ min }] = await this.qb.min({ min: column });
return min;
}
/**
* Sum all numbers of a given column.
*/
async sum(column) {
const [{ sum }] = await this.qb.sum({ sum: column });
return sum;
}
/**
* Sum all numbers of a given column in distinct mode.
*/
async sumDistinct(column) {
const [{ sum }] = await this.qb.sumDistinct({ sum: column });
return sum;
}
/**
* Increment a value of a given column.
*/
async increment(column) {
await this.qb.increment(column);
}
/**
* Decrement a value of a given column.
*/
async decrement(column) {
await this.qb.decrement(column);
}
/**
* Calculate the average of a given column using distinct.
*/
async count(column = '*') {
const [{ count }] = await this.qb.count({ count: column });
return `${count}`;
}
/**
* Calculate the average of a given column using distinct.
*/
async countDistinct(column) {
const [{ count }] = await this.qb.countDistinct({ count: column });
return `${count}`;
}
/**
* Find a value in database.
*/
async find() {
return this.qb.first();
}
/**
* Find many values in database.
*/
async findMany() {
const data = await this.qb;
this.qb = this.query();
return data;
}
/**
* Find many values in database and return as paginated response.
*/
async paginate(page = { page: 0, limit: 10, resourceUrl: '/' }, limit = 10, resourceUrl = '/') {
if (Is.Number(page)) {
page = { page, limit, resourceUrl };
}
const [{ count }] = await this.qb
.clone()
.clearOrder()
.clearSelect()
.count({ count: '*' });
const data = await this.offset(page.page * page.limit)
.limit(page.limit)
.findMany();
return Exec.pagination(data, parseInt(count), page);
}
/**
* Create a value in database.
*/
async create(data = {}) {
if (Is.Array(data)) {
throw new WrongMethodException('create', 'createMany');
}
const created = await this.createMany([data]);
return created[0];
}
/**
* Create many values in database.
*/
async createMany(data = []) {
if (!Is.Array(data)) {
throw new WrongMethodException('createMany', 'create');
}
return this.qb.insert(data, '*');
}
/**
* Create data or update if already exists.
*/
async createOrUpdate(data = {}) {
const query = this.qb.clone();
const hasValue = await query.first();
if (hasValue) {
await this.qb
.where(this.primaryKey, hasValue[this.primaryKey])
.update(data);
return this.where(this.primaryKey, hasValue[this.primaryKey]).find();
}
return this.create(data);
}
/**
* Update a value in database.
*/
async update(data) {
await this.qb.clone().update(data);
const result = await this.findMany();
if (result.length === 1) {
return result[0];
}
return result;
}
/**
* Delete one value in database.
*/
async delete() {
await this.qb.delete();
}
/**
* Set the table that this query will be executed.
*/
table(table) {
if (!this.isConnected) {
throw new NotConnectedDatabaseException();
}
this.tableName = table;
this.qb = this.query();
return this;
}
/**
* Log in console the actual query built.
*/
dump() {
console.log(this.qb.toSQL().toNative());
return this;
}
/**
* Set the columns that should be selected on query.
*/
select(...columns) {
this.qb.select(...columns);
return this;
}
/**
* Set the columns that should be selected on query raw.
*/
selectRaw(sql, bindings) {
return this.select(this.raw(sql, bindings));
}
/**
* Set the table that should be used on query.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
from(table) {
this.qb.from(table);
return this;
}
/**
* Set the table that should be used on query raw.
* Different from `table()` method, this method
* doesn't change the driver table.
*/
fromRaw(sql, bindings) {
return this.from(this.raw(sql, bindings));
}
/**
* Set a join statement in your query.
*/
join(table, column1, operation, column2) {
return this.joinByType('join', table, column1, operation, column2);
}
/**
* Set a left join statement in your query.
*/
leftJoin(table, column1, operation, column2) {
return this.joinByType('leftJoin', table, column1, operation, column2);
}
/**
* Set a right join statement in your query.
*/
rightJoin(table, column1, operation, column2) {
return this.joinByType('rightJoin', table, column1, operation, column2);
}
/**
* Set a cross join statement in your query.
*/
crossJoin(table, column1, operation, column2) {
return this.joinByType('crossJoin', table, column1, operation, column2);
}
/**
* Set a full outer join statement in your query.
*/
fullOuterJoin(table, column1, operation, column2) {
return this.joinByType('fullOuterJoin', table, column1, operation, column2);
}
/**
* Set a left outer join statement in your query.
*/
leftOuterJoin(table, column1, operation, column2) {
return this.joinByType('leftOuterJoin', table, column1, operation, column2);
}
/**
* Set a right outer join statement in your query.
*/
rightOuterJoin(table, column1, operation, column2) {
return this.joinByType('rightOuterJoin', table, column1, operation, column2);
}
/**
* Set a join raw statement in your query.
*/
joinRaw(sql, bindings) {
this.qb.joinRaw(sql, bindings);
return this;
}
/**
* Set a group by statement in your query.
*/
groupBy(...columns) {
this.qb.groupBy(...columns);
return this;
}
/**
* Set a group by raw statement in your query.
*/
groupByRaw(sql, bindings) {
this.qb.groupByRaw(sql, bindings);
return this;
}
/**
* Set a having statement in your query.
*/
having(column, operation, value) {
if (operation === undefined) {
this.qb.having(column);
return this;
}
if (value === undefined) {
this.qb.having(column, '=', operation);
return this;
}
this.qb.having(column, operation, value);
return this;
}
/**
* Set a having raw statement in your query.
*/
havingRaw(sql, bindings) {
this.qb.havingRaw(sql, bindings);
return this;
}
/**
* Set a having exists statement in your query.
*/
havingExists(closure) {
const driver = this.clone();
// @ts-ignore
this.qb.havingExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set a having not exists statement in your query.
*/
havingNotExists(closure) {
const driver = this.clone();
// @ts-ignore
this.qb.havingNotExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set a having in statement in your query.
*/
havingIn(column, values) {
this.qb.havingIn(column, values);
return this;
}
/**
* Set a having not in statement in your query.
*/
havingNotIn(column, values) {
this.qb.havingNotIn(column, values);
return this;
}
/**
* Set a having between statement in your query.
*/
havingBetween(column, values) {
this.qb.havingBetween(column, values);
return this;
}
/**
* Set a having not between statement in your query.
*/
havingNotBetween(column, values) {
this.qb.havingNotBetween(column, values);
return this;
}
/**
* Set a having null statement in your query.
*/
havingNull(column) {
this.qb.havingNull(column);
return this;
}
/**
* Set a having not null statement in your query.
*/
havingNotNull(column) {
this.qb.havingNotNull(column);
return this;
}
/**
* Set an or having statement in your query.
*/
orHaving(column, operation, value) {
if (operation === undefined) {
this.qb.orHaving(column);
return this;
}
if (value === undefined) {
this.qb.orHaving(column, '=', operation);
return this;
}
this.qb.orHaving(column, operation, value);
return this;
}
/**
* Set an or having raw statement in your query.
*/
orHavingRaw(sql, bindings) {
this.qb.orHavingRaw(sql, bindings);
return this;
}
/**
* Set an or having exists statement in your query.
*/
orHavingExists(closure) {
const driver = this.clone();
// @ts-ignore
this.qb.orHavingExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set an or having not exists statement in your query.
*/
orHavingNotExists(closure) {
const driver = this.clone();
// @ts-ignore
this.qb.orHavingNotExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set an or having in statement in your query.
*/
orHavingIn(column, values) {
// @ts-ignore
this.qb.orHavingIn(column, values);
return this;
}
/**
* Set an or having not in statement in your query.
*/
orHavingNotIn(column, values) {
this.qb.orHavingNotIn(column, values);
return this;
}
/**
* Set an or having between statement in your query.
*/
orHavingBetween(column, values) {
this.qb.orHavingBetween(column, values);
return this;
}
/**
* Set an or having not between statement in your query.
*/
orHavingNotBetween(column, values) {
this.qb.orHavingNotBetween(column, values);
return this;
}
/**
* Set an or having null statement in your query.
*/
orHavingNull(column) {
// @ts-ignore
this.qb.orHavingNull(column);
return this;
}
/**
* Set an or having not null statement in your query.
*/
orHavingNotNull(column) {
// @ts-ignore
this.qb.orHavingNotNull(column);
return this;
}
/**
* Set a where statement in your query.
*/
where(statement, operation, value) {
if (Is.Function(statement)) {
const driver = this.clone();
this.qb.where(function () {
statement(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
if (operation === undefined) {
if (Is.Array(statement)) {
throw new Error('Arrays as statement are not supported.');
}
if (Is.String(statement)) {
throw new Error(`The value for the "${statement}" column is undefined and where will not work.`);
}
this.qb.where(statement);
return this;
}
if (value === undefined) {
this.qb.where(statement, operation);
return this;
}
this.qb.where(statement, operation, value);
return this;
}
/**
* Set a where not statement in your query.
*/
whereNot(statement, value) {
if (Is.Function(statement)) {
const driver = this.clone();
this.qb.whereNot(function () {
statement(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
if (value === undefined) {
this.qb.whereNot(statement);
return this;
}
this.qb.whereNot(statement, value);
return this;
}
/**
* Set a where raw statement in your query.
*/
whereRaw(sql, bindings) {
this.qb.whereRaw(sql, bindings);
return this;
}
/**
* Set a where exists statement in your query.
*/
whereExists(closure) {
const driver = this.clone();
this.qb.whereExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set a where not exists statement in your query.
*/
whereNotExists(closure) {
const driver = this.clone();
this.qb.whereNotExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set a where like statement in your query.
*/
whereLike(column, value) {
this.qb.whereLike(column, value);
return this;
}
/**
* Set a where ILike statement in your query.
*/
whereILike(column, value) {
this.qb.whereLike(column, value);
return this;
}
/**
* Set a where in statement in your query.
*/
whereIn(column, values) {
this.qb.whereIn(column, values);
return this;
}
/**
* Set a where not in statement in your query.
*/
whereNotIn(column, values) {
this.qb.whereNotIn(column, values);
return this;
}
/**
* Set a where between statement in your query.
*/
whereBetween(column, values) {
this.qb.whereBetween(column, values);
return this;
}
/**
* Set a where not between statement in your query.
*/
whereNotBetween(column, values) {
this.qb.whereNotBetween(column, values);
return this;
}
/**
* Set a where null statement in your query.
*/
whereNull(column) {
this.qb.whereNull(column);
return this;
}
/**
* Set a where not null statement in your query.
*/
whereNotNull(column) {
this.qb.whereNotNull(column);
return this;
}
/**
* Set a or where statement in your query.
*/
orWhere(statement, operation, value) {
if (Is.Function(statement)) {
const driver = this.clone();
this.qb.orWhere(function () {
statement(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
if (operation === undefined) {
this.qb.orWhere(statement);
return this;
}
if (value === undefined) {
this.qb.orWhere(statement, operation);
return this;
}
this.qb.orWhere(statement, operation, value);
return this;
}
/**
* Set an or where not statement in your query.
*/
orWhereNot(statement, value) {
if (Is.Function(statement)) {
const driver = this.clone();
this.qb.orWhereNot(function () {
statement(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
if (value === undefined) {
this.qb.orWhereNot(statement);
return this;
}
this.qb.orWhereNot(statement, value);
return this;
}
/**
* Set a or where raw statement in your query.
*/
orWhereRaw(sql, bindings) {
this.qb.orWhereRaw(sql, bindings);
return this;
}
/**
* Set an or where exists statement in your query.
*/
orWhereExists(closure) {
const driver = this.clone();
this.qb.orWhereExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set an or where not exists statement in your query.
*/
orWhereNotExists(closure) {
const driver = this.clone();
this.qb.orWhereNotExists(function () {
closure(driver.setQueryBuilder(this, { useSetQB: true }));
});
return this;
}
/**
* Set an or where like statement in your query.
*/
orWhereLike(column, value) {
this.qb.orWhereLike(column, value);
return this;
}
/**
* Set an or where ILike statement in your query.
*/
orWhereILike(column, value) {
this.qb.orWhereLike(column, value);
return this;
}
/**
* Set an or where in statement in your query.
*/
orWhereIn(column, values) {
this.qb.orWhereIn(column, values);
return this;
}
/**
* Set an or where not in statement in your query.
*/
orWhereNotIn(column, values) {
this.qb.orWhereNotIn(column, values);
return this;
}
/**
* Set an or where between statement in your query.
*/
orWhereBetween(column, values) {
this.qb.orWhereBetween(column, values);
return this;
}
/**
* Set an or where not between statement in your query.
*/
orWhereNotBetween(column, values) {
this.qb.orWhereNotBetween(column, values);
return this;
}
/**
* Set an or where null statement in your query.
*/
orWhereNull(column) {
this.qb.orWhereNull(column);
return this;
}
/**
* Set an or where not null statement in your query.
*/
orWhereNotNull(column) {
this.qb.orWhereNotNull(column);
return this;
}
/**
* Set an order by statement in your query.
*/
orderBy(column, direction = 'ASC') {
this.qb.orderBy(column, direction.toUpperCase());
return this;
}
/**
* Set an order by raw statement in your query.
*/
orderByRaw(sql, bindings) {
this.qb.orderByRaw(sql, bindings);
return this;
}
/**
* Order the results easily by the latest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
latest(column = 'createdAt') {
return this.orderBy(column, 'DESC');
}
/**
* Order the results easily by the oldest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
oldest(column = 'createdAt') {
return this.orderBy(column, 'ASC');
}
/**
* Set the skip number in your query.
*/
offset(number) {
this.qb.offset(number);
return this;
}
/**
* Set the limit number in your query.
*/
limit(number) {
this.qb.limit(number);
return this;
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { DatabaseImpl } from '#src/database/DatabaseImpl';
export declare abstract class BaseMigration {
/**
* Define the database connection that the
* migration will use.
*/
static connection(): string;
/**
* Run the migrations.
*/
abstract up(db: DatabaseImpl): Promise<void>;
/**
* Reverse the migrations.
*/
abstract down(db: DatabaseImpl): Promise<void>;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Config } from '@athenna/config';
export class BaseMigration {
/**
* Define the database connection that the
* migration will use.
*/
static connection() {
return Config.get('database.default');
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { BaseMigration } from '#src/database/migrations/BaseMigration';
type Source = {
name: string;
Migration: new (...args: any[]) => BaseMigration;
};
export declare class MigrationSource {
connection: any;
constructor(connection: string);
/**
* Verify if migration is able to run by connection.
*/
private isAbleToRun;
/**
* Get all the migrations from migrations path and import
* as modules. This method will be used by "getMigration"
* method later to get the migrations "up"/"down" methods.
*/
getMigrations(): Promise<Source[]>;
/**
* Get the migration name that will be used to set in
* migrations table.
*/
getMigrationName(source: Source): string;
/**
* Creates a new migration instance and return the up/down
* methods in object.
*/
getMigration(source: Source): Promise<{
up: (knex: any) => any;
down: (knex: any) => any;
}>;
}
export {};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { debug } from '#src/debug';
import { Module, Path } from '@athenna/common';
import { DatabaseImpl } from '#src/database/DatabaseImpl';
import { ConnectionFactory } from '#src/factories/ConnectionFactory';
export class MigrationSource {
constructor(connection) {
this.connection = null;
this.connection = connection;
}
/**
* Verify if migration is able to run by connection.
*/
isAbleToRun(migration) {
return migration.connection() === this.connection;
}
/**
* Get all the migrations from migrations path and import
* as modules. This method will be used by "getMigration"
* method later to get the migrations "up"/"down" methods.
*/
async getMigrations() {
const migrations = [];
const files = await Module.getAllJSFilesFrom(Path.migrations());
for (const file of files) {
const Migration = await Module.getFrom(file.path);
if (this.isAbleToRun(Migration)) {
migrations.push({ name: file.base, Migration });
}
}
return migrations;
}
/**
* Get the migration name that will be used to set in
* migrations table.
*/
getMigrationName(source) {
return source.name;
}
/**
* Creates a new migration instance and return the up/down
* methods in object.
*/
async getMigration(source) {
const migration = new source.Migration();
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const connection = source.Migration.connection();
const database = new DatabaseImpl({ connect: false });
debug('configuring database connection %s to run migration %s', connection, source.Migration.name);
database.connectionName = connection;
database.driver = ConnectionFactory.fabricate(connection);
const bind = (method, knex) => {
database.driver = database.driver.setClient(knex);
return migration[method].bind(migration)(database);
};
return {
up: knex => bind('up', knex),
down: knex => bind('down', knex)
};
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { type DatabaseImpl } from '#src/database/DatabaseImpl';
export declare abstract class BaseSeeder {
/**
* Run the database seeders.
*/
abstract run(db?: DatabaseImpl): void | Promise<void>;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import {} from '#src/database/DatabaseImpl';
export class BaseSeeder {
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { Knex } from 'knex';
import { Macroable } from '@athenna/common';
import type { Driver } from '#src/database/drivers/Driver';
import { QueryBuilder } from '#src/database/builders/QueryBuilder';
export declare class Transaction<Client = any, QB = any> extends Macroable {
/**
* The drivers responsible for handling database operations.
*/
driver: Driver<Client, QB>;
/**
* Creates a new instance of transaction.
*/
constructor(driver: Driver);
/**
* Return the client of driver.
*/
getClient(): Client;
/**
* Return the query builder of driver.
*/
getQueryBuilder(): QB;
/**
* Commit the transaction.
*/
commitTransaction(): Promise<void>;
/**
* Rollback the transaction.
*/
rollbackTransaction(): Promise<void>;
/**
* Run database migrations.
*/
runMigrations(): Promise<void>;
/**
* Revert database migrations.
*/
revertMigrations(): Promise<void>;
/**
* List all databases available.
*/
getDatabases(): Promise<string[]>;
/**
* Get the current database name.
*/
getCurrentDatabase(): Promise<string | undefined>;
/**
* Verify if database exists.
*/
hasDatabase(database: string): Promise<boolean>;
/**
* Create a new database.
*/
createDatabase(database: string): Promise<void>;
/**
* Drop some database.
*/
dropDatabase(database: string): Promise<void>;
/**
* List all tables available.
*/
getTables(): Promise<string[]>;
/**
* Verify if table exists.
*/
hasTable(table: string): Promise<boolean>;
/**
* Create a new table in database.
*/
createTable(table: string, closure?: (builder: Knex.TableBuilder) => void | Promise<void>): Promise<void>;
/**
* Alter a table in database.
*/
alterTable(table: string, closure?: (builder: Knex.TableBuilder) => void | Promise<void>): Promise<void>;
/**
* Drop a table in database.
*/
dropTable(table: string): Promise<void>;
/**
* Remove all data inside some database table
* and restart the identity of the table.
*/
truncate(table: string): Promise<void>;
/**
* Make a raw query in database.
*/
raw<T = any>(sql: string, bindings?: any): Knex.Raw<T>;
/**
* Creates a new instance of QueryBuilder for this table.
*/
table(table: string | any): QueryBuilder;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Macroable } from '@athenna/common';
import { QueryBuilder } from '#src/database/builders/QueryBuilder';
export class Transaction extends Macroable {
/**
* Creates a new instance of transaction.
*/
constructor(driver) {
super();
/**
* The drivers responsible for handling database operations.
*/
this.driver = null;
this.driver = driver;
}
/**
* Return the client of driver.
*/
getClient() {
return this.driver.getClient();
}
/**
* Return the query builder of driver.
*/
getQueryBuilder() {
return this.driver.getQueryBuilder();
}
/**
* Commit the transaction.
*/
async commitTransaction() {
return this.driver.commitTransaction();
}
/**
* Rollback the transaction.
*/
async rollbackTransaction() {
return this.driver.rollbackTransaction();
}
/**
* Run database migrations.
*/
async runMigrations() {
await this.driver.runMigrations();
}
/**
* Revert database migrations.
*/
async revertMigrations() {
await this.driver.revertMigrations();
}
/**
* List all databases available.
*/
async getDatabases() {
return this.driver.getDatabases();
}
/**
* Get the current database name.
*/
async getCurrentDatabase() {
return this.driver.getCurrentDatabase();
}
/**
* Verify if database exists.
*/
async hasDatabase(database) {
return this.driver.hasDatabase(database);
}
/**
* Create a new database.
*/
async createDatabase(database) {
return this.driver.createDatabase(database);
}
/**
* Drop some database.
*/
async dropDatabase(database) {
return this.driver.dropDatabase(database);
}
/**
* List all tables available.
*/
async getTables() {
return this.driver.getTables();
}
/**
* Verify if table exists.
*/
async hasTable(table) {
return this.driver.hasTable(table);
}
/**
* Create a new table in database.
*/
async createTable(table, closure) {
return this.driver.createTable(table, closure);
}
/**
* Alter a table in database.
*/
async alterTable(table, closure) {
return this.driver.alterTable(table, closure);
}
/**
* Drop a table in database.
*/
async dropTable(table) {
return this.driver.dropTable(table);
}
/**
* Remove all data inside some database table
* and restart the identity of the table.
*/
async truncate(table) {
return this.driver.truncate(table);
}
/**
* Make a raw query in database.
*/
raw(sql, bindings) {
return this.driver.raw(sql, bindings);
}
/**
* Creates a new instance of QueryBuilder for this table.
*/
table(table) {
return new QueryBuilder(this.driver, table);
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export declare const debug: import("util").DebugLogger;
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { debuglog } from 'node:util';
export const debug = debuglog('athenna:database');
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Exception } from '@athenna/common';
export declare class NotConnectedDatabaseException extends Exception {
constructor();
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Exception } from '@athenna/common';
export class NotConnectedDatabaseException extends Exception {
constructor() {
const message = 'Database is not connected to perform queries.';
super({
message,
code: 'E_NOT_CONNECTED_ERROR',
help: `Remember to call "connect()" method before starting performing queries.`
});
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Exception } from '@athenna/common';
export declare class NotFoundDataException extends Exception {
constructor(con: string);
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Exception } from '@athenna/common';
export class NotFoundDataException extends Exception {
constructor(con) {
const message = 'Data not found in database.';
super({
message,
status: 404,
code: 'E_NOT_FOUND_DATA_ERROR',
help: `Data not found in database using the using ${con} connection.`
});
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Exception } from '@athenna/common';
export declare class NotFoundDriverException extends Exception {
constructor(driver: string);
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Path, Exception } from '@athenna/common';
import { ConnectionFactory } from '#src/factories/ConnectionFactory';
export class NotFoundDriverException extends Exception {
constructor(driver) {
const message = `The driver ${driver} has not been found.`;
const availableDrivers = ConnectionFactory.availableDrivers().join(', ');
super({
message,
code: 'E_NOT_FOUND_DRIVER_ERROR',
help: `Available drivers are: ${availableDrivers}. Look into your config/database.${Path.ext()} file if ${driver} driver is implemented by database.`
});
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Exception } from '@athenna/common';
export declare class NotImplementedConfigException extends Exception {
constructor(con: string);
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Path, Exception, Is } from '@athenna/common';
export class NotImplementedConfigException extends Exception {
constructor(con) {
let help = '';
const connections = Config.get('database.connections');
if (connections && !Is.Empty(connections)) {
const availableConfigs = Object.keys(connections).join(', ');
help += `Available configurations are: ${availableConfigs}.`;
}
else {
help += `The "Config.get('database.connections')" is empty, maybe your configuration files are not loaded?`;
}
help += ` Create your configuration inside connections object to use it. Or load your configuration files using "Config.safeLoad(Path.config('database.${Path.ext()}'))`;
super({
status: 500,
code: 'E_NOT_IMPLEMENTED_CONFIG_ERROR',
message: `Connection ${con} is not configured inside database.connections object from config/database.${Path.ext()} file.`,
help
});
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Exception } from '@athenna/common';
export declare class NotImplementedMethodException extends Exception {
constructor(method: string, driver: string);
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Exception } from '@athenna/common';
export class NotImplementedMethodException extends Exception {
constructor(method, driver) {
super({
status: 500,
code: 'E_NOT_IMPLEMENTED_METHOD_ERROR',
message: `The method ${method} is not available when using the ${driver} driver.`,
help: `Rely in the documentation to check how you could get the behavior of ${method} method working for your ${driver} driver. For example, when using mongo driver, it doesn't make sense to run the raw method since mongo queries are create from JS code`
});
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Exception } from '@athenna/common';
export declare class NotImplementedRelationException extends Exception {
constructor(relation: string, model: string, available: string);
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Exception } from '@athenna/common';
export class NotImplementedRelationException extends Exception {
constructor(relation, model, available) {
super({
status: 500,
code: 'E_NOT_IMPLEMENTED_RELATION_ERROR',
message: `The relation ${relation} is not available in model ${model}.`,
help: `Available relations are: ${available}.`
});
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Exception } from '@athenna/common';
export declare class NullableValueException extends Exception {
constructor(records: any);
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Exception } from '@athenna/common';
export class NullableValueException extends Exception {
constructor(records) {
if (records.length > 1) {
records = records.join(', ');
}
else {
records = records[0];
}
super({
status: 400,
code: 'E_NULLABLE_VALUE_ERROR',
message: `The properties [${records}] are undefined or null.`,
help: `Your properties [${records}] has the option isNullable set to true in it's model. Try setting values to these properties or set the isNullable property to false.`
});
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Exception } from '@athenna/common';
export declare class UniqueValueException extends Exception {
constructor(records: Record<string, any>);
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Exception } from '@athenna/common';
export class UniqueValueException extends Exception {
constructor(records) {
let values = Object.values(records);
let properties = Object.keys(records);
if (properties.length > 1) {
values = values.join(', ');
properties = properties.join(', ');
}
else {
values = values[0];
properties = properties[0];
}
super({
status: 400,
code: 'E_UNIQUE_VALUE_ERROR',
message: `The properties [${properties}] is unique in database and cannot be replicated.`,
help: `Your properties [${properties}] has the option isUnique set to true in it's model, meaning that the values [${values}] could not be used because there is another record with it in your database. Try creating your record with a different value, or set the isUnique property to false.`
});
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Exception } from '@athenna/common';
export declare class WrongMethodException extends Exception {
constructor(wrong: string, right: string);
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Exception } from '@athenna/common';
export class WrongMethodException extends Exception {
constructor(wrong, right) {
const message = `The method ${wrong} should not be used to perform operations using this type of data.`;
super({
message,
code: 'E_WRONG_METHOD_ERROR',
help: `Instead try using the ${right} method.`
});
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { DatabaseImpl } from '#src/database/DatabaseImpl';
export declare const Database: import("@athenna/ioc").FacadeType<DatabaseImpl<any>>;
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Facade } from '@athenna/ioc';
import { DatabaseImpl } from '#src/database/DatabaseImpl';
export const Database = Facade.createFor('Athenna/Core/Database');
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { Driver } from '#src/database/drivers/Driver';
import { FakeDriver } from '#src/database/drivers/FakeDriver';
import { MongoDriver } from '#src/database/drivers/MongoDriver';
import { MySqlDriver } from '#src/database/drivers/MySqlDriver';
import { SqliteDriver } from '#src/database/drivers/SqliteDriver';
import { PostgresDriver } from '#src/database/drivers/PostgresDriver';
export declare class ConnectionFactory {
/**
* Holds all the open connections.
*/
static connections: Map<string, any>;
/**
* Holds all the Athenna drivers implementations available.
*/
static drivers: Map<string, any>;
static fabricate(con: 'fake'): typeof FakeDriver;
static fabricate(con: 'mongo'): MongoDriver;
static fabricate(con: 'mysql'): MySqlDriver;
static fabricate(con: 'sqlite'): SqliteDriver;
static fabricate(con: 'postgres'): PostgresDriver;
static fabricate(con: 'fake' | 'mongo' | 'mysql' | 'sqlite' | 'postgres' | string): typeof FakeDriver | MongoDriver | MySqlDriver | SqliteDriver | PostgresDriver;
/**
* Verify if client is present on a driver connection.
*/
static hasClient(con: string): boolean;
/**
* Get client of a connection.
*/
static getClient(con: string): any;
/**
* Set connection client on driver.
*/
static setClient(con: string, client: any): void;
/**
* Return all available drivers.
*/
static availableDrivers(): any[];
/**
* Return all available connections.
*/
static availableConnections(): any[];
/**
* Define your own database driver implementation to use
* within Database facade.
*
* @example
* ```ts
* import { Driver, ConnectionFactory } from '@athenna/database'
*
* class TestDriver extends Driver {}
*
* ConnectionFactory.createDriver('test', TestDriver)
* ```
*/
static createDriver(name: string, impl: typeof Driver<any, any>): void;
/**
* Parse connection config name if is default
*/
private static parseConName;
/**
* Get the connection configuration of config/database file.
*/
private static getConnectionDriver;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { debug } from '#src/debug';
import { FakeDriver } from '#src/database/drivers/FakeDriver';
import { MongoDriver } from '#src/database/drivers/MongoDriver';
import { MySqlDriver } from '#src/database/drivers/MySqlDriver';
import { SqliteDriver } from '#src/database/drivers/SqliteDriver';
import { PostgresDriver } from '#src/database/drivers/PostgresDriver';
import { NotFoundDriverException } from '#src/exceptions/NotFoundDriverException';
import { NotImplementedConfigException } from '#src/exceptions/NotImplementedConfigException';
export class ConnectionFactory {
/**
* Holds all the open connections.
*/
static { this.connections = new Map(); }
/**
* Holds all the Athenna drivers implementations available.
*/
static { this.drivers = new Map()
.set('fake', { driver: FakeDriver })
.set('mongo', { driver: MongoDriver })
.set('mysql', { driver: MySqlDriver })
.set('sqlite', { driver: SqliteDriver })
.set('postgres', { driver: PostgresDriver }); }
/**
* Fabricate a new connection for a specific driver.
*/
static fabricate(con) {
con = this.parseConName(con);
const driverName = this.getConnectionDriver(con);
const Driver = this.drivers.get(driverName).driver;
const connection = this.connections.get(con);
if (!connection) {
this.connections.set(con, { client: null });
return new Driver(con);
}
if (connection.client) {
debug('client found for connection %s using driver %s, using it as default', con, driverName);
return new Driver(con, connection.client);
}
return new Driver(con);
}
/**
* Verify if client is present on a driver connection.
*/
static hasClient(con) {
return !!this.connections.get(con)?.client;
}
/**
* Get client of a connection.
*/
static getClient(con) {
return this.connections.get(con)?.client;
}
/**
* Set connection client on driver.
*/
static setClient(con, client) {
const connection = this.connections.get(con) || {};
connection.client = client;
this.connections.set(con, connection);
}
/**
* Return all available drivers.
*/
static availableDrivers() {
const availableDrivers = [];
for (const key of this.drivers.keys()) {
availableDrivers.push(key);
}
return availableDrivers;
}
/**
* Return all available connections.
*/
static availableConnections() {
const availableConnections = [];
for (const key of this.connections.keys()) {
availableConnections.push(key);
}
return availableConnections;
}
/**
* Define your own database driver implementation to use
* within Database facade.
*
* @example
* ```ts
* import { Driver, ConnectionFactory } from '@athenna/database'
*
* class TestDriver extends Driver {}
*
* ConnectionFactory.createDriver('test', TestDriver)
* ```
*/
static createDriver(name, impl) {
this.drivers.set(name, { driver: impl });
}
/**
* Parse connection config name if is default
*/
static parseConName(con) {
if (con === 'default') {
return Config.get('database.default');
}
return con;
}
/**
* Get the connection configuration of config/database file.
*/
static getConnectionDriver(con) {
const config = Config.get(`database.connections.${con}`);
if (!config) {
throw new NotImplementedConfigException(con);
}
if (!this.drivers.has(config.driver)) {
throw new NotFoundDriverException(config.driver);
}
return config.driver;
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { RelationOptions, ColumnOptions, HasOneOptions, HasManyOptions, BelongsToOptions, BelongsToManyOptions } from '#src/types';
export declare class Annotation {
static getColumnsMeta(target: any): ColumnOptions[];
static defineColumnMeta(target: any, options: ColumnOptions): void;
static getRelationsMeta(target: any): RelationOptions[];
static getHasOnesMeta(target: any): HasOneOptions[];
static defineHasOneMeta(target: any, options: HasOneOptions): void;
static getHasManyMeta(target: any): HasManyOptions[];
static defineHasManyMeta(target: any, options: HasManyOptions): void;
static getBelongsToMeta(target: any): BelongsToOptions[];
static defineBelongsToMeta(target: any, options: BelongsToOptions): void;
static getBelongsToManyMeta(target: any): BelongsToManyOptions[];
static defineBelongsToManyMeta(target: any, options: BelongsToManyOptions): void;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { COLUMNS_KEY, HAS_ONE_KEY, HAS_MANY_KEY, BELONGS_TO_KEY, BELONGS_TO_MANY_KEY } from '#src/constants/MetadataKeys';
export class Annotation {
static getColumnsMeta(target) {
return Reflect.getMetadata(COLUMNS_KEY, target) || [];
}
static defineColumnMeta(target, options) {
const columns = Reflect.getMetadata(COLUMNS_KEY, target) || [];
columns.push(options);
Reflect.defineMetadata(COLUMNS_KEY, columns, target);
}
static getRelationsMeta(target) {
return [
...this.getHasOnesMeta(target),
...this.getHasManyMeta(target),
...this.getBelongsToMeta(target),
...this.getBelongsToManyMeta(target)
];
}
static getHasOnesMeta(target) {
return Reflect.getMetadata(HAS_ONE_KEY, target) || [];
}
static defineHasOneMeta(target, options) {
const hasOne = Reflect.getMetadata(HAS_ONE_KEY, target) || [];
hasOne.push(options);
Reflect.defineMetadata(HAS_ONE_KEY, hasOne, target);
}
static getHasManyMeta(target) {
return Reflect.getMetadata(HAS_MANY_KEY, target) || [];
}
static defineHasManyMeta(target, options) {
const hasMany = Reflect.getMetadata(HAS_MANY_KEY, target) || [];
hasMany.push(options);
Reflect.defineMetadata(HAS_MANY_KEY, hasMany, target);
}
static getBelongsToMeta(target) {
return Reflect.getMetadata(BELONGS_TO_KEY, target) || [];
}
static defineBelongsToMeta(target, options) {
const belongsTo = Reflect.getMetadata(BELONGS_TO_KEY, target) || [];
belongsTo.push(options);
Reflect.defineMetadata(BELONGS_TO_KEY, belongsTo, target);
}
static getBelongsToManyMeta(target) {
return Reflect.getMetadata(BELONGS_TO_MANY_KEY, target) || [];
}
static defineBelongsToManyMeta(target, options) {
const belongsToMany = Reflect.getMetadata(BELONGS_TO_MANY_KEY, target) || [];
belongsToMany.push(options);
Reflect.defineMetadata(BELONGS_TO_MANY_KEY, belongsToMany, target);
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export declare class ObjectId {
/**
* Validate if is a valid object id.
*/
static isValid(objectId: any): boolean;
/**
* Validate if is a valid object id string or ObjectID object.
*/
static isValidStringOrObject(objectId: any): boolean;
/**
* Validate if is a valid object id string.
*/
static isValidString(objectId: any): boolean;
/**
* Swap the value for an objectId instance if valid.
* If not valid, return the value.
*/
static ifValidSwap(value: any): any;
/**
* Validate if is a valid object id object.
*/
static isValidObject(objectId: any): boolean;
constructor(value: any);
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Is, Module } from '@athenna/common';
const mongoose = await Module.safeImport('mongoose');
export class ObjectId {
/**
* Validate if is a valid object id.
*/
static isValid(objectId) {
if (!mongoose) {
return false;
}
// eslint-disable-next-line
return mongoose.isValidObjectId(objectId) && new mongoose.Types.ObjectId(objectId) == objectId;
}
/**
* Validate if is a valid object id string or ObjectID object.
*/
static isValidStringOrObject(objectId) {
return this.isValidString(objectId) || this.isValidObject(objectId);
}
/**
* Validate if is a valid object id string.
*/
static isValidString(objectId) {
if (!mongoose) {
return false;
}
return Is.String(objectId) && this.isValid(objectId);
}
/**
* Swap the value for an objectId instance if valid.
* If not valid, return the value.
*/
static ifValidSwap(value) {
if (ObjectId.isValidString(value)) {
return new ObjectId(value);
}
return value;
}
/**
* Validate if is a valid object id object.
*/
static isValidObject(objectId) {
if (!mongoose) {
return false;
}
return (!Is.Number(objectId) &&
!Is.String(objectId) &&
mongoose.isValidObjectId(objectId));
}
constructor(value) {
return new mongoose.Types.ObjectId(value);
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export * from '#src/types';
export * from '#src/models/BaseModel';
export * from '#src/models/builders/ModelQueryBuilder';
export * from '#src/models/schemas/ModelSchema';
export * from '#src/models/annotations/Column';
export * from '#src/models/annotations/HasOne';
export * from '#src/models/annotations/HasMany';
export * from '#src/models/annotations/BelongsTo';
export * from '#src/models/annotations/BelongsToMany';
export * from '#src/database/DatabaseImpl';
export * from '#src/database/seeders/BaseSeeder';
export * from '#src/database/migrations/BaseMigration';
export * from '#src/database/builders/QueryBuilder';
export * from '#src/database/transactions/Transaction';
export * from '#src/database/migrations/MigrationSource';
export * from '#src/database/drivers/Driver';
export * from '#src/factories/ConnectionFactory';
export * from '#src/helpers/Annotation';
export * from '#src/helpers/ObjectId';
export * from '#src/facades/Database';
export * from '#src/providers/DatabaseProvider';
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export * from '#src/types';
export * from '#src/models/BaseModel';
export * from '#src/models/builders/ModelQueryBuilder';
export * from '#src/models/schemas/ModelSchema';
export * from '#src/models/annotations/Column';
export * from '#src/models/annotations/HasOne';
export * from '#src/models/annotations/HasMany';
export * from '#src/models/annotations/BelongsTo';
export * from '#src/models/annotations/BelongsToMany';
export * from '#src/database/DatabaseImpl';
export * from '#src/database/seeders/BaseSeeder';
export * from '#src/database/migrations/BaseMigration';
export * from '#src/database/builders/QueryBuilder';
export * from '#src/database/transactions/Transaction';
export * from '#src/database/migrations/MigrationSource';
export * from '#src/database/drivers/Driver';
export * from '#src/factories/ConnectionFactory';
export * from '#src/helpers/Annotation';
export * from '#src/helpers/ObjectId';
export * from '#src/facades/Database';
export * from '#src/providers/DatabaseProvider';
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'reflect-metadata';
import type { BaseModel } from '#src/models/BaseModel';
import type { BelongsToOptions } from '#src/types/relations/BelongsToOptions';
/**
* Create belongs to relation for model class.
*/
export declare function BelongsTo<T extends BaseModel = any, R extends BaseModel = any>(model: (() => new () => R) | string, options?: Omit<BelongsToOptions<T, R>, 'type' | 'model' | 'property'>): (target: T, key: any) => void;
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'reflect-metadata';
import { debug } from '#src/debug';
import { Is, Options } from '@athenna/common';
import { Annotation } from '#src/helpers/Annotation';
/**
* Create belongs to relation for model class.
*/
export function BelongsTo(model, options = {}) {
return (target, key) => {
const Target = target.constructor;
options = Options.create(options, {
isIncluded: false,
// Default will be set later as: RelationModel.schema().getMainPrimaryKeyProperty()
primaryKey: undefined,
// Default will be set later as: `${String.toCamelCase(RelationModel.name)}Id`
foreignKey: undefined
});
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
options.type = 'belongsTo';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
options.model = Is.String(model)
? () => ioc.safeUse(`App/Models/${model}`).constructor
: model;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
options.property = key;
debug('registering belongsTo metadata for model %s: %o', Target.name, options);
Annotation.defineBelongsToMeta(Target, options);
};
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'reflect-metadata';
import type { BaseModel } from '#src/models/BaseModel';
import type { BelongsToManyOptions } from '#src/types/relations/BelongsToManyOptions';
/**
* Create belongs to many relation for model class.
*/
export declare function BelongsToMany<T extends BaseModel = any, R extends BaseModel = any, P extends BaseModel = any>(model: (() => new () => R) | string, pivotModel: (() => new () => P) | string, options?: Omit<BelongsToManyOptions<T, R, P>, 'type' | 'model' | 'property'>): (target: T, key: any) => void;
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'reflect-metadata';
import { debug } from '#src/debug';
import { Annotation } from '#src/helpers/Annotation';
import { Is, Options, String } from '@athenna/common';
/**
* Create belongs to many relation for model class.
*/
export function BelongsToMany(model, pivotModel, options = {}) {
return (target, key) => {
const Target = target.constructor;
options = Options.create(options, {
isIncluded: false,
primaryKey: Target.schema().getMainPrimaryKeyName(),
foreignKey: `${String.toCamelCase(Target.name)}Id`,
// Default value will be set later as: `${Model.table()}_${RelationModel.table()}`
pivotTable: undefined,
// Default value will be set later as: Relation.schema().getMainPrimaryKeyName()
relationPrimaryKey: undefined,
// Default value will be set later as: `${String.toCamelCase(Relation.name)}Id`
relationForeignKey: undefined
});
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
options.type = 'belongsToMany';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
options.model = Is.String(model)
? () => ioc.safeUse(`App/Models/${model}`).constructor
: model;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
options.pivotModel = Is.String(pivotModel)
? () => ioc.safeUse(`App/Models/${pivotModel}`).constructor
: pivotModel;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
options.property = key;
debug('registering belongsToMany metadata for model %s: %o', Target.name, options);
Annotation.defineBelongsToManyMeta(Target, options);
};
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'reflect-metadata';
import type { ColumnOptions } from '#src/types/columns/ColumnOptions';
/**
* Create column for model class.
*/
export declare function Column(options?: Omit<ColumnOptions, 'property' | 'hasSetName'>): PropertyDecorator;
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'reflect-metadata';
import { debug } from '#src/debug';
import { Options } from '@athenna/common';
import { Annotation } from '#src/helpers/Annotation';
/**
* Create column for model class.
*/
export function Column(options = {}) {
return (target, key) => {
const hasSetName = !!options.name;
options = Options.create(options, {
name: key,
type: Reflect.getMetadata('design:type', target, key),
defaultTo: null,
isPrimary: false,
isHidden: false,
isUnique: false,
isNullable: true,
isIndex: false,
isSparse: false,
persist: true,
isMainPrimary: false,
isCreateDate: false,
isUpdateDate: false,
isDeleteDate: false
});
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
options.property = key;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
options.hasSetName = hasSetName;
if (options.isMainPrimary) {
options.isPrimary = true;
}
const Target = target.constructor;
debug('registering column metadata for model %s: %o', Target.name, options);
Annotation.defineColumnMeta(Target, options);
};
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'reflect-metadata';
import type { BaseModel } from '#src/models/BaseModel';
import type { HasManyOptions } from '#src/types/relations/HasManyOptions';
/**
* Create has many relation for model class.
*/
export declare function HasMany<T extends BaseModel = any, R extends BaseModel = any>(model: (() => new () => R) | string, options?: Omit<HasManyOptions, 'type' | 'model' | 'property'>): (target: T, key: any) => void;
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'reflect-metadata';
import { debug } from '#src/debug';
import { Annotation } from '#src/helpers/Annotation';
import { Is, Options, String } from '@athenna/common';
/**
* Create has many relation for model class.
*/
export function HasMany(model, options = {}) {
return (target, key) => {
const Target = target.constructor;
options = Options.create(options, {
isIncluded: false,
primaryKey: Target.schema().getMainPrimaryKeyProperty(),
foreignKey: `${String.toCamelCase(Target.name)}Id`
});
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
options.type = 'hasMany';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
options.model = Is.String(model)
? () => ioc.safeUse(`App/Models/${model}`).constructor
: model;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
options.property = key;
debug('registering hasMany metadata for model %s: %o', Target.name, options);
Annotation.defineHasManyMeta(Target, options);
};
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'reflect-metadata';
import type { BaseModel } from '#src/models/BaseModel';
import type { HasOneOptions } from '#src/types/relations/HasOneOptions';
/**
* Create has one relation for model class.
*/
export declare function HasOne<T extends BaseModel = any, R extends BaseModel = any>(model: (() => new () => R) | string, options?: Omit<HasOneOptions<T, R>, 'type' | 'model' | 'property'>): (target: T, key: string) => void;
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import 'reflect-metadata';
import { debug } from '#src/debug';
import { Annotation } from '#src/helpers/Annotation';
import { Is, Options, String } from '@athenna/common';
/**
* Create has one relation for model class.
*/
export function HasOne(model, options = {}) {
return (target, key) => {
const Target = target.constructor;
options = Options.create(options, {
isIncluded: false,
primaryKey: Target.schema().getMainPrimaryKeyProperty(),
foreignKey: `${String.toCamelCase(Target.name)}Id`
});
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
options.type = 'hasOne';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
options.model = Is.String(model)
? () => ioc.safeUse(`App/Models/${model}`).constructor
: model;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
options.property = key;
debug('registering hasOne metadata for model %s: %o', Target.name, options);
Annotation.defineHasOneMeta(Target, options);
};
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Collection, type PaginatedResponse, type PaginationOptions } from '@athenna/common';
import type { ModelRelations } from '#src/types';
import { type Faker } from '@faker-js/faker';
import { ModelSchema } from '#src/models/schemas/ModelSchema';
import { ORIGINAL_SYMBOL } from '#src/constants/OriginalSymbol';
import { ModelFactory } from '#src/models/factories/ModelFactory';
import { ModelQueryBuilder } from '#src/models/builders/ModelQueryBuilder';
export declare class BaseModel {
/**
* Set if the `attributes` method should be called or not.
*/
private static get isToSetAttributes();
/**
* Set if the option annotation `isUnique`
* should be verified or not.
*/
private static get isToValidateUnique();
/**
* Set if the option annotation `isNullable`
* should be verified or not.
*/
private static get isToValidateNullable();
/**
* The faker instance to create fake data in
* definition instance.
*/
static get faker(): Faker;
/**
* Set the connection name that model will use
* to access database.
*/
static connection(): any;
/**
* Set if model should automatically be sync with
* database when running DatabaseProvider.
*
* @default true
*/
static sync(): boolean;
/**
* Set the table name of this model instance.
*/
static table(): string;
/**
* Set the default values that should be set when creating or
* updating the model.
*/
static attributes(): Record<string, unknown>;
/**
* Set the definition data that will be used when fabricating
* instances of your model using factories.
*/
static definition(): Promise<Record<string, unknown>>;
/**
* Create a new ModelSchema instance from your model.
*/
static schema<T extends typeof BaseModel>(this: T): ModelSchema<InstanceType<T>>;
/**
* Create a new ModelFactory instance from your model.
*/
static factory<T extends typeof BaseModel>(this: T): ModelFactory<InstanceType<T>, InstanceType<T>>;
/**
* Enable/disable setting the default attributes properties
* when creating/updating models.
*/
static setAttributes<T extends typeof BaseModel>(this: T, value: boolean): T;
/**
* Enable/disable the `isUnique` property validation of
* models columns.
*/
static uniqueValidation<T extends typeof BaseModel>(this: T, value: boolean): T;
/**
* Enable/disable the `isNullable` property validation of
* models columns.
*/
static nullableValidation<T extends typeof BaseModel>(this: T, value: boolean): T;
/**
* Create a query builder for the model.
*/
static query<T extends typeof BaseModel>(this: T): ModelQueryBuilder<InstanceType<T>, import("../database/drivers/MongoDriver.js").MongoDriver>;
/**
* Remove all data inside model table
* and restart the identity of the table.
*/
static truncate(): Promise<void>;
/**
* Find value in database but returns only the value of
* selected column directly.
*/
static pluck<T extends typeof BaseModel, K extends keyof InstanceType<T> = keyof InstanceType<T>>(this: T, key: K, where?: Partial<InstanceType<T>>): Promise<InstanceType<T>[K]>;
/**
* Find many values in database but returns only the
* values of selected column directly.
*/
static pluckMany<T extends typeof BaseModel, K extends keyof InstanceType<T> = keyof InstanceType<T>>(this: T, key: K, where?: Partial<InstanceType<T>>): Promise<InstanceType<T>[K][]>;
/**
* Find a value in database.
*/
static find<T extends typeof BaseModel>(this: T, where?: Partial<InstanceType<T>>): Promise<InstanceType<T>>;
/**
* Find a value in database.
*/
static exists<T extends typeof BaseModel>(this: T, where?: Partial<InstanceType<T>>): Promise<boolean>;
/**
* Find a value in database or throw exception if undefined.
*/
static findOrFail<T extends typeof BaseModel>(this: T, where?: Partial<InstanceType<T>>): Promise<InstanceType<T>>;
/**
* Return a single data or, if no results are found,
* execute the given closure.
*/
static findOr<T extends typeof BaseModel>(this: T, where: Partial<InstanceType<T>>, closure: () => any | Promise<any>): Promise<InstanceType<T> | any>;
/**
* Find many values in database.
*/
static findMany<T extends typeof BaseModel>(this: T, where?: Partial<InstanceType<T>>): Promise<InstanceType<T>[]>;
/**
* Find many values in database and return paginated.
*/
static paginate<T extends typeof BaseModel>(this: T, options?: PaginationOptions, where?: Partial<InstanceType<T>>): Promise<PaginatedResponse<InstanceType<T>>>;
/**
* Find many values in database and return
* as a collection instance.
*/
static collection<T extends typeof BaseModel>(this: T, where?: Partial<InstanceType<T>>): Promise<Collection<InstanceType<T>>>;
/**
* Create a value in database.
*/
static create<T extends typeof BaseModel>(this: T, data?: Partial<InstanceType<T>>, cleanPersist?: boolean): Promise<InstanceType<T>>;
/**
* Create many values in database.
*/
static createMany<T extends typeof BaseModel>(this: T, data: Partial<InstanceType<T>>[], cleanPersist?: boolean): Promise<InstanceType<T>[]>;
/**
* Create or update a value in database.
*/
static createOrUpdate<T extends typeof BaseModel>(this: T, where: Partial<InstanceType<T>>, data: Partial<InstanceType<T>>, cleanPersist?: boolean): Promise<InstanceType<T> | InstanceType<T>[]>;
/**
* Update a value in database.
*/
static update<T extends typeof BaseModel>(this: T, where: Partial<InstanceType<T>>, data: Partial<InstanceType<T>>, cleanPersist?: boolean): Promise<InstanceType<T> | InstanceType<T>[]>;
/**
* Delete or soft delete a value in database.
*/
static delete<T extends typeof BaseModel>(this: T, where: Partial<InstanceType<T>>, force?: boolean): Promise<void>;
/**
* The original model values when it was fetched
* from database. If is undefined, means that model
* is a fresh instance and is not available in database
* yet.
*/
private [ORIGINAL_SYMBOL]?;
/**
* Set the original model values by deep copying
* the model state.
*/
setOriginal(): this;
/**
* Return a Json object from the actual subclass instance.
*/
toJSON(options?: {
withHidden?: boolean;
}): Record<string, any>;
load(relation: string): Promise<any>;
load<K extends ModelRelations<this>>(relation: K, closure?: (query: ModelQueryBuilder<Extract<this[K] extends BaseModel[] ? this[K][0] : this[K], BaseModel>>) => any): Promise<this[K]>;
/**
* Validate if model is persisted in database
* or if it's a fresh instance.
*/
isPersisted(): boolean;
/**
* Get values only that are different from
* the original symbol to avoid updating
* data that was not changed.
*/
dirty(): this | Record<string, any>;
/**
* Validate if model has been changed from
* it initial state when it was retrieved from
* database.
*/
isDirty(): boolean;
/**
* Save the changes done in the model in database.
*/
save(cleanPersist?: boolean): Promise<this>;
/**
* Create a new instance of the model from retrieving
* again the data from database. The existing
* model instance WILL NOT BE affected.
*/
fresh(): Promise<any>;
/**
* Refresh the model instance data retrieving
* model data using the main primary key. The
* existing model instance WILL BE affected.
*/
refresh(): Promise<void>;
/**
* Verify if model is soft deleted.
*/
isTrashed(): boolean;
/**
* Delete or soft delete your model from database.
*/
delete(force?: boolean): Promise<void>;
/**
* Restore a soft deleted model from database.
*/
restore(): Promise<this>;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Is, Json, String, Options, Collection } from '@athenna/common';
import { Database } from '#src/facades/Database';
import { faker } from '@faker-js/faker';
import { ModelSchema } from '#src/models/schemas/ModelSchema';
import { ORIGINAL_SYMBOL } from '#src/constants/OriginalSymbol';
import { ModelFactory } from '#src/models/factories/ModelFactory';
import { ModelGenerator } from '#src/models/factories/ModelGenerator';
import { ModelQueryBuilder } from '#src/models/builders/ModelQueryBuilder';
export class BaseModel {
/**
* Set if the `attributes` method should be called or not.
*/
static get isToSetAttributes() {
return Config.get(`database.connections.${this.connection()}.validations.isToSetAttributes`, true);
}
/**
* Set if the option annotation `isUnique`
* should be verified or not.
*/
static get isToValidateUnique() {
return Config.get(`database.connections.${this.connection()}.validations.isToValidateUnique`, true);
}
/**
* Set if the option annotation `isNullable`
* should be verified or not.
*/
static get isToValidateNullable() {
return Config.get(`database.connections.${this.connection()}.validations.isToValidateNullable`, true);
}
/**
* The faker instance to create fake data in
* definition instance.
*/
static get faker() {
return faker;
}
/**
* Set the connection name that model will use
* to access database.
*/
static connection() {
return Config.get('database.default');
}
/**
* Set if model should automatically be sync with
* database when running DatabaseProvider.
*
* @default true
*/
static sync() {
const connection = this.connection();
const driver = Config.get(`database.connections.${connection}.driver`);
if (driver === 'mongo') {
return true;
}
return false;
}
/**
* Set the table name of this model instance.
*/
static table() {
return String.pluralize(String.toSnakeCase(this.name).toLowerCase());
}
/**
* Set the default values that should be set when creating or
* updating the model.
*/
static attributes() {
return {};
}
/**
* Set the definition data that will be used when fabricating
* instances of your model using factories.
*/
static async definition() {
return {};
}
/**
* Create a new ModelSchema instance from your model.
*/
static schema() {
return new ModelSchema(this);
}
/**
* Create a new ModelFactory instance from your model.
*/
static factory() {
return new ModelFactory(this);
}
/**
* Enable/disable setting the default attributes properties
* when creating/updating models.
*/
static setAttributes(value) {
Config.set(`database.connections.${this.connection()}.validations.isToSetAttributes`, value);
return this;
}
/**
* Enable/disable the `isUnique` property validation of
* models columns.
*/
static uniqueValidation(value) {
Config.set(`database.connections.${this.connection()}.validations.isToValidateUnique`, value);
return this;
}
/**
* Enable/disable the `isNullable` property validation of
* models columns.
*/
static nullableValidation(value) {
Config.set(`database.connections.${this.connection()}.validations.isToValidateNullable`, value);
return this;
}
/**
* Create a query builder for the model.
*/
static query() {
const driver = Database.connection(this.connection()).driver;
return new ModelQueryBuilder(this, driver)
.setAttributes(this.isToSetAttributes)
.uniqueValidation(this.isToValidateUnique)
.nullableValidation(this.isToValidateNullable);
}
/**
* Remove all data inside model table
* and restart the identity of the table.
*/
static async truncate() {
await Database.connection(this.connection()).truncate(this.table());
}
/**
* Find value in database but returns only the value of
* selected column directly.
*/
static async pluck(key, where) {
const query = this.query();
if (where) {
query.where(where);
}
return query.pluck(key);
}
/**
* Find many values in database but returns only the
* values of selected column directly.
*/
static async pluckMany(key, where) {
const query = this.query();
if (where) {
query.where(where);
}
return query.pluckMany(key);
}
/**
* Find a value in database.
*/
static async find(where) {
const query = this.query();
if (where) {
query.where(where);
}
return query.find();
}
/**
* Find a value in database.
*/
static async exists(where) {
const query = this.query();
if (where) {
query.where(where);
}
return query.exists();
}
/**
* Find a value in database or throw exception if undefined.
*/
static async findOrFail(where) {
const query = this.query();
if (where) {
query.where(where);
}
return query.findOrFail();
}
/**
* Return a single data or, if no results are found,
* execute the given closure.
*/
static async findOr(where, closure) {
const query = this.query();
if (where) {
query.where(where);
}
return query.findOr(closure);
}
/**
* Find many values in database.
*/
static async findMany(where) {
const query = this.query();
if (where) {
query.where(where);
}
return query.findMany();
}
/**
* Find many values in database and return paginated.
*/
static async paginate(options, where) {
const query = this.query();
if (where) {
query.where(where);
}
return query.paginate(options);
}
/**
* Find many values in database and return
* as a collection instance.
*/
static async collection(where) {
const query = this.query();
if (where) {
query.where(where);
}
return query.collection();
}
/**
* Create a value in database.
*/
static async create(data = {}, cleanPersist = true) {
return this.query().create(data, cleanPersist);
}
/**
* Create many values in database.
*/
static async createMany(data, cleanPersist = true) {
return this.query().createMany(data, cleanPersist);
}
/**
* Create or update a value in database.
*/
static async createOrUpdate(where, data, cleanPersist = true) {
const query = this.query();
if (where) {
query.where(where);
}
return query.createOrUpdate(data, cleanPersist);
}
/**
* Update a value in database.
*/
static async update(where, data, cleanPersist = true) {
const query = this.query();
if (where) {
query.where(where);
}
return query.update(data, cleanPersist);
}
/**
* Delete or soft delete a value in database.
*/
static async delete(where, force = false) {
const query = this.query();
if (where) {
query.where(where);
}
return query.delete(force);
}
/**
* Set the original model values by deep copying
* the model state.
*/
setOriginal() {
this[ORIGINAL_SYMBOL] = {};
Object.keys(Json.copy(this)).forEach(key => {
const value = this[key];
if (Is.Array(value) && value[0] && ORIGINAL_SYMBOL in value[0]) {
return;
}
if (Is.Object(value) && value && ORIGINAL_SYMBOL in value) {
return;
}
this[ORIGINAL_SYMBOL][key] = value;
});
return this;
}
/**
* Return a Json object from the actual subclass instance.
*/
toJSON(options) {
options = Options.create(options, {
withHidden: false
});
const _Model = this.constructor;
const json = {};
const relations = _Model.schema().getRelationProperties();
/**
* Execute the toJSON of relations.
*/
Object.keys(this).forEach(key => {
if (relations.includes(key)) {
if (Is.Array(this[key])) {
json[key] = this[key].map(d => (d.toJSON ? d.toJSON() : d));
return;
}
json[key] = this[key]?.toJSON ? this[key].toJSON() : this[key];
return;
}
if (!options.withHidden &&
_Model.schema().getColumnByProperty(key)?.isHidden) {
return;
}
json[key] = this[key];
});
return json;
}
/**
* Eager load a model relation from model instance.
*/
async load(relation, closure) {
const Model = this.constructor;
const schema = Model.schema();
const generator = new ModelGenerator(Model, schema);
await generator.includeRelation(this, schema.includeRelation(relation, closure));
if (relation.includes('.')) {
return Json.get(this, relation);
}
return this[relation];
}
/**
* Validate if model is persisted in database
* or if it's a fresh instance.
*/
isPersisted() {
return !!this[ORIGINAL_SYMBOL];
}
/**
* Get values only that are different from
* the original symbol to avoid updating
* data that was not changed.
*/
dirty() {
if (!this.isPersisted()) {
return this;
}
const dirty = {};
Object.keys(this).forEach(key => {
const orig = this[ORIGINAL_SYMBOL][key];
const curr = this[key];
if (Json.isEqual(orig, curr)) {
return;
}
if (Is.Object(curr) || Is.Array(curr)) {
dirty[key] = Json.copy(curr);
return;
}
dirty[key] = Json.diff(orig, curr);
});
return dirty;
}
/**
* Validate if model has been changed from
* it initial state when it was retrieved from
* database.
*/
isDirty() {
return Object.keys(this.dirty()).length > 0;
}
/**
* Save the changes done in the model in database.
*/
async save(cleanPersist = true) {
const Model = this.constructor;
const schema = Model.schema();
const primaryKey = schema.getMainPrimaryKeyProperty();
const date = new Date();
const createdAt = schema.getCreatedAtColumn();
const updatedAt = schema.getUpdatedAtColumn();
const deletedAt = schema.getDeletedAtColumn();
const attributes = Model.isToSetAttributes ? Model.attributes() : {};
Object.keys(attributes).forEach(key => {
if (this[key]) {
return;
}
this[key] = attributes[key];
});
if (createdAt && this[createdAt.property] === undefined) {
this[createdAt.property] = date;
}
if (updatedAt && this[updatedAt.property] === undefined) {
this[updatedAt.property] = date;
}
if (deletedAt && this[deletedAt.property] === undefined) {
this[deletedAt.property] = null;
}
const data = this.dirty();
if (!this.isPersisted()) {
const created = await Model.create(data, cleanPersist);
Object.keys(created).forEach(key => (this[key] = created[key]));
return this.setOriginal();
}
/**
* Means data is not dirty because there are any
* value that is different from original symbol.
*/
if (!Object.keys(data).length) {
return this;
}
const where = { [primaryKey]: this[primaryKey] };
const updated = await Model.update(where, data, cleanPersist);
Object.keys(updated).forEach(key => (this[key] = updated[key]));
return this.setOriginal();
}
/**
* Create a new instance of the model from retrieving
* again the data from database. The existing
* model instance WILL NOT BE affected.
*/
async fresh() {
const Model = this.constructor;
const primaryKey = Model.schema().getMainPrimaryKeyProperty();
return Model.query()
.where(primaryKey, this[primaryKey])
.withTrashed()
.find();
}
/**
* Refresh the model instance data retrieving
* model data using the main primary key. The
* existing model instance WILL BE affected.
*/
async refresh() {
const Model = this.constructor;
const schema = Model.schema();
const relations = schema.getRelationProperties();
const primaryKey = schema.getMainPrimaryKeyProperty();
const query = Model.query()
.where(primaryKey, this[primaryKey])
.withTrashed();
Object.keys(this).forEach(key => {
if (!relations.includes(key)) {
return;
}
query.with(key);
});
const data = await query.find();
Object.keys(data).forEach(key => (this[key] = data[key]));
}
/**
* Verify if model is soft deleted.
*/
isTrashed() {
const Model = this.constructor;
const deletedAt = Model.schema().getDeletedAtColumn();
return !!this[deletedAt.property];
}
/**
* Delete or soft delete your model from database.
*/
async delete(force = false) {
const Model = this.constructor;
const primaryKey = Model.schema().getMainPrimaryKeyProperty();
await Model.query().where(primaryKey, this[primaryKey]).delete(force);
}
/**
* Restore a soft deleted model from database.
*/
async restore() {
const Model = this.constructor;
const primaryKey = Model.schema().getMainPrimaryKeyProperty();
const restored = await Model.query()
.where(primaryKey, this[primaryKey])
.restore();
Object.keys(restored).forEach(key => (this[key] = restored[key]));
return this;
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Collection, type PaginationOptions } from '@athenna/common';
import type { Direction, Operations, ModelColumns, ModelRelations } from '#src/types';
import type { BaseModel } from '#src/models/BaseModel';
import type { Driver } from '#src/database/drivers/Driver';
import { QueryBuilder } from '#src/database/builders/QueryBuilder';
import type { Transaction } from '#src/database/transactions/Transaction';
export declare class ModelQueryBuilder<M extends BaseModel = any, D extends Driver = any> extends QueryBuilder<M, D> {
private Model;
private schema;
private generator;
private primaryKeyName;
private primaryKeyProperty;
private isToSetAttributes;
private isToValidateUnique;
private isToValidateNullable;
private selectColumns;
private DELETED_AT_PROP;
private DELETED_AT_NAME;
private isSoftDelete;
private hasCustomSelect;
constructor(model: any, driver: D);
/**
* Define a transaction to be used by the model query builder.
*/
setTransaction(trx: Transaction): this;
/**
* Set a different driver to the model query builder.
*/
setDriver(driver: Driver, tableName?: string): this;
/**
* Calculate the average of a given column.
*/
avg(column: ModelColumns<M>): Promise<string>;
/**
* Calculate the average of a given column.
*/
avgDistinct(column: ModelColumns<M>): Promise<string>;
/**
* Get the max number of a given column.
*/
max(column: ModelColumns<M>): Promise<string>;
/**
* Get the min number of a given column.
*/
min(column: ModelColumns<M>): Promise<string>;
/**
* Sum all numbers of a given column.
*/
sum(column: ModelColumns<M>): Promise<string>;
/**
* Sum all numbers of a given column.
*/
sumDistinct(column: ModelColumns<M>): Promise<string>;
/**
* Increment a value of a given column.
*/
increment(column: ModelColumns<M>): Promise<void>;
/**
* Decrement a value of a given column.
*/
decrement(column: ModelColumns<M>): Promise<void>;
/**
* Calculate the average of a given column using distinct.
*/
count(column?: ModelColumns<M>): Promise<string>;
/**
* Calculate the average of a given column using distinct.
*/
countDistinct(column: ModelColumns<M>): Promise<string>;
/**
* Find value in database but returns only the value of
* selected column directly.
*/
pluck<K extends keyof M = ModelColumns<M>>(column: K): Promise<M[K]>;
/**
* Find many values in database but returns only the
* values of selected column directly.
*/
pluckMany<K extends keyof M = ModelColumns<M>>(column: K): Promise<M[K][]>;
/**
* Find a value in database.
*/
find(): Promise<M>;
/**
* Find a value in database or throw exception if undefined.
*/
findOrFail(): Promise<M>;
/**
* Return a single data or, if no results are found,
* execute the given closure.
*/
findOr<T = M>(closure: () => T | Promise<T>): Promise<T>;
/**
* Find many values in database.
*/
findMany(): Promise<M[]>;
/**
* Find many values in database and return paginated.
*/
paginate(page?: PaginationOptions | number, limit?: number, resourceUrl?: string): Promise<import("@athenna/common").PaginatedResponse<any>>;
/**
* Find many values in database and return
* as a collection instance.
*/
collection(): Promise<Collection<M>>;
/**
* Create a value in database.
*/
create(data?: Partial<M>, cleanPersist?: boolean): Promise<M>;
/**
* Create many values in database.
*/
createMany(data: Partial<M>[], cleanPersist?: boolean): Promise<M[]>;
/**
* Create or update a value in database.
*/
createOrUpdate(data: Partial<M>, cleanPersist?: boolean): Promise<M | M[]>;
/**
* Update a value in database.
*/
update(data: Partial<M>, cleanPersist?: boolean): Promise<M | M[]>;
/**
* Delete or soft delete a value in database.
*/
delete(force?: boolean): Promise<void>;
/**
* Restore one or multiple soft deleted models.
*/
restore(): Promise<M | M[]>;
/**
* Retrieve only the values that are soft deleted in
* database.
*/
onlyTrashed(): this;
/**
* Retrieve values that are soft deleted in database.
*/
withTrashed(): this;
/**
* Enable/disable setting the default attributes properties
* when creating/updating models.
*/
setAttributes(value: boolean): this;
/**
* Enable/disable the `isUnique` property validation of
* models columns.
*/
uniqueValidation(value: boolean): this;
/**
* Enable/disable the `isNullable` property validation of
* models columns.
*/
nullableValidation(value: boolean): this;
with(relation: string): this;
with<K extends ModelRelations<M>>(relation: K, closure?: (query: ModelQueryBuilder<Extract<M[K] extends BaseModel[] ? M[K][0] : M[K], BaseModel>, Driver>) => any): this;
/**
* Only returns the data if the closure returns a result.
*/
whereHas<K extends ModelRelations<M>>(relation: K | string, closure?: (query: ModelQueryBuilder<Extract<M[K] extends BaseModel[] ? M[K][0] : M[K], BaseModel>, Driver>) => any): this;
/**
* Executes the given closure when the first argument is true.
*/
when(criteria: any, closure: (query: this, criteriaValue: any) => any | Promise<any>): this;
/**
* Set the columns that should be selected on query.
*/
select(...columns: ModelColumns<M>[]): this;
/**
* Set a group by statement in your query.
*/
groupBy(...columns: ModelColumns<M>[]): this;
having(column: ModelColumns<M>): this;
having(column: ModelColumns<M>, value: any): this;
having(column: ModelColumns<M>, operation: Operations, value: any): this;
/**
* Set a having in statement in your query.
*/
havingIn(column: ModelColumns<M>, values: any[]): this;
/**
* Set a having not in statement in your query.
*/
havingNotIn(column: ModelColumns<M>, values: any[]): this;
/**
* Set a having between statement in your query.
*/
havingBetween(column: ModelColumns<M>, values: [any, any]): this;
/**
* Set a having not between statement in your query.
*/
havingNotBetween(column: ModelColumns<M>, values: [any, any]): this;
/**
* Set a having null statement in your query.
*/
havingNull(column: ModelColumns<M>): this;
/**
* Set a having not null statement in your query.
*/
havingNotNull(column: ModelColumns<M>): this;
orHaving(column: ModelColumns<M>): this;
orHaving(column: ModelColumns<M>, value: any): this;
orHaving(column: ModelColumns<M>, operation: Operations, value: any): this;
/**
* Set a orHaving in statement in your query.
*/
orHavingIn(column: ModelColumns<M>, values: any[]): this;
/**
* Set a orHaving not in statement in your query.
*/
orHavingNotIn(column: ModelColumns<M>, values: any[]): this;
/**
* Set a orHaving between statement in your query.
*/
orHavingBetween(column: ModelColumns<M>, values: [any, any]): this;
/**
* Set a orHaving not between statement in your query.
*/
orHavingNotBetween(column: ModelColumns<M>, values: [any, any]): this;
/**
* Set a orHaving null statement in your query.
*/
orHavingNull(column: ModelColumns<M>): this;
/**
* Set a orHaving not null statement in your query.
*/
orHavingNotNull(column: ModelColumns<M>): this;
where(statement: (query: this) => void): this;
where(statement: Partial<M>): this;
where(statement: Record<string, any>): this;
where(key: ModelColumns<M>, value: any): this;
where(key: ModelColumns<M>, operation: Operations, value: any): this;
whereNot(statement: (query: this) => void): this;
whereNot(statement: Partial<M>): this;
whereNot(statement: Record<string, any>): this;
whereNot(key: ModelColumns<M>, value: any): this;
/**
* Set a where like statement in your query.
*/
whereLike(column: ModelColumns<M>, value: any): this;
/**
* Set a where ILike statement in your query.
*/
whereILike(column: ModelColumns<M>, value: any): this;
/**
* Set a where in statement in your query.
*/
whereIn(column: ModelColumns<M>, values: any[]): this;
/**
* Set a where not in statement in your query.
*/
whereNotIn(column: ModelColumns<M>, values: any[]): this;
/**
* Set a where between statement in your query.
*/
whereBetween(column: ModelColumns<M>, values: [any, any]): this;
/**
* Set a where not between statement in your query.
*/
whereNotBetween(column: ModelColumns<M>, values: [any, any]): this;
/**
* Set a where null statement in your query.
*/
whereNull(column: ModelColumns<M>): this;
/**
* Set a where not null statement in your query.
*/
whereNotNull(column: ModelColumns<M>): this;
orWhere(statement: (query: this) => void): this;
orWhere(statement: Partial<M>): this;
orWhere(statement: Record<string, any>): this;
orWhere(key: ModelColumns<M>, value: any): this;
orWhere(key: ModelColumns<M>, operation: Operations, value: any): this;
orWhereNot(statement: (query: this) => void): this;
orWhereNot(statement: Partial<M>): this;
orWhereNot(statement: Record<string, any>): this;
orWhereNot(key: ModelColumns<M>, value: any): this;
orWhereLike(statement: Partial<M>): this;
orWhereLike(statement: Record<string, any>): this;
orWhereLike(key: ModelColumns<M>, value: any): this;
orWhereILike(statement: Partial<M>): this;
orWhereILike(statement: Record<string, any>): this;
orWhereILike(key: ModelColumns<M>, value: any): this;
/**
* Set a orWhere in statement in your query.
*/
orWhereIn(column: ModelColumns<M>, values: any[]): this;
/**
* Set a orWhere not in statement in your query.
*/
orWhereNotIn(column: ModelColumns<M>, values: any[]): this;
/**
* Set a orWhere between statement in your query.
*/
orWhereBetween(column: ModelColumns<M>, values: [any, any]): this;
/**
* Set a orWhere not between statement in your query.
*/
orWhereNotBetween(column: ModelColumns<M>, values: [any, any]): this;
/**
* Set a orWhere null statement in your query.
*/
orWhereNull(column: ModelColumns<M>): this;
/**
* Set a orWhere not null statement in your query.
*/
orWhereNotNull(column: ModelColumns<M>): this;
/**
* Set an order by statement in your query.
*/
orderBy(column: ModelColumns<M>, direction?: Direction): this;
/**
* Order the results easily by the latest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
latest(column?: ModelColumns<M>): this;
/**
* Order the results easily by the oldest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
oldest(column?: ModelColumns<M>): this;
/**
* Set the internal selected properties and soft delete
* queries.
*/
private setInternalQueries;
/**
* Verify that columns with `isNullable` property
* can be created in database.
*/
private validateNullable;
/**
* Verify that columns with isUnique property
* can be created in database.
*/
private validateUnique;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Collection, Is, Options } from '@athenna/common';
import { QueryBuilder } from '#src/database/builders/QueryBuilder';
import { ModelGenerator } from '#src/models/factories/ModelGenerator';
import { UniqueValueException } from '#src/exceptions/UniqueValueException';
import { HasOneRelation } from '#src/models/relations/HasOne/HasOneRelation';
import { NotFoundDataException } from '#src/exceptions/NotFoundDataException';
import { HasManyRelation } from '#src/models/relations/HasMany/HasManyRelation';
import { NullableValueException } from '#src/exceptions/NullableValueException';
import { BelongsToRelation } from '#src/models/relations/BelongsTo/BelongsToRelation';
import { BelongsToManyRelation } from '#src/models/relations/BelongsToMany/BelongsToManyRelation';
export class ModelQueryBuilder extends QueryBuilder {
constructor(model, driver) {
super(driver, model.table());
this.isToSetAttributes = true;
this.isToValidateUnique = true;
this.isToValidateNullable = true;
this.selectColumns = [];
this.DELETED_AT_PROP = null;
this.DELETED_AT_NAME = null;
this.isSoftDelete = false;
this.hasCustomSelect = false;
this.Model = model;
this.schema = model.schema();
this.generator = new ModelGenerator(this.Model, this.schema);
this.primaryKeyName = this.schema.getMainPrimaryKeyName();
this.primaryKeyProperty = this.schema.getMainPrimaryKeyProperty();
const deletedAtColumn = this.schema.getDeletedAtColumn();
if (deletedAtColumn) {
this.isSoftDelete = true;
this.DELETED_AT_NAME = deletedAtColumn.name;
this.DELETED_AT_PROP = deletedAtColumn.property;
}
this.selectColumns = this.schema.getAllColumnNames();
this.setPrimaryKey(this.primaryKeyName);
}
/**
* Define a transaction to be used by the model query builder.
*/
setTransaction(trx) {
return this.setDriver(trx.driver, this.Model.table());
}
/**
* Set a different driver to the model query builder.
*/
setDriver(driver, tableName) {
super.setDriver(driver, tableName);
return this;
}
/**
* Calculate the average of a given column.
*/
async avg(column) {
this.setInternalQueries();
const name = this.schema.getColumnNameByProperty(column);
return super.avg(name);
}
/**
* Calculate the average of a given column.
*/
async avgDistinct(column) {
this.setInternalQueries();
const name = this.schema.getColumnNameByProperty(column);
return super.avgDistinct(name);
}
/**
* Get the max number of a given column.
*/
async max(column) {
this.setInternalQueries();
const name = this.schema.getColumnNameByProperty(column);
return super.max(name);
}
/**
* Get the min number of a given column.
*/
async min(column) {
this.setInternalQueries();
const name = this.schema.getColumnNameByProperty(column);
return super.min(name);
}
/**
* Sum all numbers of a given column.
*/
async sum(column) {
this.setInternalQueries();
const name = this.schema.getColumnNameByProperty(column);
return super.sum(name);
}
/**
* Sum all numbers of a given column.
*/
async sumDistinct(column) {
this.setInternalQueries();
const name = this.schema.getColumnNameByProperty(column);
return super.sumDistinct(name);
}
/**
* Increment a value of a given column.
*/
async increment(column) {
this.setInternalQueries();
const name = this.schema.getColumnNameByProperty(column);
return super.increment(name);
}
/**
* Decrement a value of a given column.
*/
async decrement(column) {
this.setInternalQueries();
const name = this.schema.getColumnNameByProperty(column);
await super.decrement(name);
}
/**
* Calculate the average of a given column using distinct.
*/
async count(column) {
this.setInternalQueries();
if (!column) {
return super.count();
}
const name = this.schema.getColumnNameByProperty(column);
return super.count(name);
}
/**
* Calculate the average of a given column using distinct.
*/
async countDistinct(column) {
this.setInternalQueries();
const name = this.schema.getColumnNameByProperty(column);
return super.countDistinct(name);
}
/**
* Find value in database but returns only the value of
* selected column directly.
*/
async pluck(column) {
this.setInternalQueries();
const columnName = this.schema.getColumnNameByProperty(column);
return super.pluck(columnName);
}
/**
* Find many values in database but returns only the
* values of selected column directly.
*/
async pluckMany(column) {
this.setInternalQueries();
const columnName = this.schema.getColumnNameByProperty(column);
return super.pluckMany(columnName);
}
/**
* Find a value in database.
*/
async find() {
this.setInternalQueries();
const data = await super.find();
return this.generator.generateOne(data);
}
/**
* Find a value in database or throw exception if undefined.
*/
async findOrFail() {
const data = await this.find();
if (!data) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
throw new NotFoundDataException(this.Model.connection());
}
return data;
}
/**
* Return a single data or, if no results are found,
* execute the given closure.
*/
async findOr(closure) {
const data = (await this.find());
if (!data) {
return closure();
}
return data;
}
/**
* Find many values in database.
*/
async findMany() {
this.setInternalQueries();
const data = await super.findMany();
return this.generator.generateMany(data);
}
/**
* Find many values in database and return paginated.
*/
async paginate(page = { page: 0, limit: 10, resourceUrl: '/' }, limit = 10, resourceUrl = '/') {
this.setInternalQueries();
const data = await super.paginate(page, limit, resourceUrl);
data.data = await this.generator.generateMany(data.data);
return data;
}
/**
* Find many values in database and return
* as a collection instance.
*/
async collection() {
const models = await this.findMany();
return new Collection(models);
}
/**
* Create a value in database.
*/
async create(data = {}, cleanPersist = true) {
const created = await this.createMany([data], cleanPersist);
return created[0];
}
/**
* Create many values in database.
*/
async createMany(data, cleanPersist = true) {
data = await Promise.all(data.map(async (d) => {
const date = new Date();
const createdAt = this.schema.getCreatedAtColumn();
const updatedAt = this.schema.getUpdatedAtColumn();
const deletedAt = this.schema.getDeletedAtColumn();
const attributes = this.isToSetAttributes ? this.Model.attributes() : {};
const parsed = this.schema.propertiesToColumnNames(d, {
attributes,
cleanPersist
});
if (createdAt && parsed[createdAt.name] === undefined) {
parsed[createdAt.name] = date;
}
if (updatedAt && parsed[updatedAt.name] === undefined) {
parsed[updatedAt.name] = date;
}
if (deletedAt && parsed[deletedAt.name] === undefined) {
parsed[deletedAt.name] = null;
}
this.validateNullable(parsed);
await this.validateUnique(parsed);
return parsed;
}));
const created = await super.createMany(data);
return this.generator.generateMany(created);
}
/**
* Create or update a value in database.
*/
async createOrUpdate(data, cleanPersist = true) {
const hasValue = await this.find();
if (hasValue) {
const pk = this.primaryKeyProperty;
return this.where(pk, hasValue[pk]).update(data, cleanPersist);
}
return this.create(data, cleanPersist);
}
/**
* Update a value in database.
*/
async update(data, cleanPersist = true) {
this.setInternalQueries();
const date = new Date();
const updatedAt = this.schema.getUpdatedAtColumn();
const attributes = this.isToSetAttributes ? this.Model.attributes() : {};
const parsed = this.schema.propertiesToColumnNames(data, {
attributes,
cleanPersist
});
if (updatedAt && parsed[updatedAt.name] === undefined) {
parsed[updatedAt.name] = date;
}
await this.validateUnique(parsed, true);
const updated = await super.update(parsed);
if (Is.Array(updated)) {
return this.generator.generateMany(updated);
}
return this.generator.generateOne(updated);
}
/**
* Delete or soft delete a value in database.
*/
async delete(force = false) {
this.setInternalQueries({ addSelect: false });
if (!this.DELETED_AT_NAME || force) {
await super.delete();
return;
}
await this.update({ [this.DELETED_AT_PROP]: new Date() });
}
/**
* Restore one or multiple soft deleted models.
*/
async restore() {
this.setInternalQueries({ addSoftDelete: false });
if (!this.DELETED_AT_PROP) {
return;
}
const updatedAt = this.schema.getUpdatedAtColumn();
const data = { [this.DELETED_AT_PROP]: null };
if (updatedAt) {
data[updatedAt.name] = new Date();
}
const updated = await super.update(data);
if (Is.Array(updated)) {
return this.generator.generateMany(updated);
}
return this.generator.generateOne(updated);
}
/**
* Retrieve only the values that are soft deleted in
* database.
*/
onlyTrashed() {
this.isSoftDelete = false;
if (!this.DELETED_AT_PROP) {
return this;
}
return this.whereNotNull(this.DELETED_AT_PROP);
}
/**
* Retrieve values that are soft deleted in database.
*/
withTrashed() {
this.isSoftDelete = false;
if (!this.DELETED_AT_PROP) {
return this;
}
if (this.schema.getModelDriverName() === 'mongo') {
this.orWhereNull(this.DELETED_AT_PROP);
}
return this.orWhereNotNull(this.DELETED_AT_PROP);
}
/**
* Enable/disable setting the default attributes properties
* when creating/updating models.
*/
setAttributes(value) {
this.isToSetAttributes = value;
return this;
}
/**
* Enable/disable the `isUnique` property validation of
* models columns.
*/
uniqueValidation(value) {
this.isToValidateUnique = value;
return this;
}
/**
* Enable/disable the `isNullable` property validation of
* models columns.
*/
nullableValidation(value) {
this.isToValidateNullable = value;
return this;
}
/**
* Eager load a relation in your query.
*/
with(relation, closure) {
this.schema.includeRelation(relation, closure);
return this;
}
/**
* Only returns the data if the closure returns a result.
*/
whereHas(relation, closure) {
const options = this.schema.includeRelation(relation, closure);
super.whereExists(query => {
switch (options.type) {
case 'hasOne':
return HasOneRelation.whereHas(this.Model, query, options);
case 'hasMany':
return HasManyRelation.whereHas(this.Model, query, options);
case 'belongsTo':
return BelongsToRelation.whereHas(this.Model, query, options);
case 'belongsToMany':
return BelongsToManyRelation.whereHas(this.Model, query, options);
}
});
return this;
}
/**
* Executes the given closure when the first argument is true.
*/
when(criteria, closure) {
if (criteria) {
closure(this, criteria);
return this;
}
return this;
}
/**
* Set the columns that should be selected on query.
*/
select(...columns) {
if (!this.hasCustomSelect) {
this.hasCustomSelect = true;
this.selectColumns = columns.map(c => this.schema.getColumnNameByProperty(c));
return this;
}
columns.forEach(column => {
const index = this.selectColumns.indexOf(column);
if (index) {
return;
}
this.selectColumns.push(this.schema.getColumnNameByProperty(column));
});
return this;
}
/**
* Set a group by statement in your query.
*/
groupBy(...columns) {
super.groupBy(...this.schema.getColumnNamesByProperties(columns));
return this;
}
/**
* Set a having statement in your query.
*/
having(column, operation, value) {
const name = this.schema.getColumnNameByProperty(column);
super.having(name, operation, value);
return this;
}
/**
* Set a having in statement in your query.
*/
havingIn(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.havingIn(name, values);
return this;
}
/**
* Set a having not in statement in your query.
*/
havingNotIn(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.havingNotIn(name, values);
return this;
}
/**
* Set a having between statement in your query.
*/
havingBetween(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.havingBetween(name, values);
return this;
}
/**
* Set a having not between statement in your query.
*/
havingNotBetween(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.havingNotBetween(name, values);
return this;
}
/**
* Set a having null statement in your query.
*/
havingNull(column) {
const name = this.schema.getColumnNameByProperty(column);
super.havingNull(name);
return this;
}
/**
* Set a having not null statement in your query.
*/
havingNotNull(column) {
const name = this.schema.getColumnNameByProperty(column);
super.havingNotNull(name);
return this;
}
/**
* Set a orHaving statement in your query.
*/
orHaving(column, operation, value) {
const name = this.schema.getColumnNameByProperty(column);
super.orHaving(name, operation, value);
return this;
}
/**
* Set a orHaving in statement in your query.
*/
orHavingIn(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.orHavingIn(name, values);
return this;
}
/**
* Set a orHaving not in statement in your query.
*/
orHavingNotIn(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.orHavingNotIn(name, values);
return this;
}
/**
* Set a orHaving between statement in your query.
*/
orHavingBetween(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.orHavingBetween(name, values);
return this;
}
/**
* Set a orHaving not between statement in your query.
*/
orHavingNotBetween(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.orHavingNotBetween(name, values);
return this;
}
/**
* Set a orHaving null statement in your query.
*/
orHavingNull(column) {
const name = this.schema.getColumnNameByProperty(column);
super.orHavingNull(name);
return this;
}
/**
* Set a orHaving not null statement in your query.
*/
orHavingNotNull(column) {
const name = this.schema.getColumnNameByProperty(column);
super.orHavingNotNull(name);
return this;
}
/**
* Set a where statement in your query.
*/
where(statement, operation, value) {
if (Is.Function(statement)) {
const driver = this.driver.clone();
super.where(query => {
const modelQb = new ModelQueryBuilder(this.Model, driver.setQueryBuilder(query, { useSetQB: true }));
statement(modelQb);
});
return this;
}
if (!operation) {
const parsed = this.schema.propertiesToColumnNames(statement);
super.where(parsed);
return this;
}
const name = this.schema.getColumnNameByProperty(statement);
super.where(name, operation, value);
return this;
}
/**
* Set a where not statement in your query.
*/
whereNot(statement, value) {
if (Is.Function(statement)) {
const driver = this.driver.clone();
super.whereNot(query => {
const modelQb = new ModelQueryBuilder(this.Model, driver.setQueryBuilder(query, { useSetQB: true }));
statement(modelQb);
});
return this;
}
if (!value) {
const parsed = this.schema.propertiesToColumnNames(statement);
super.whereNot(parsed);
return this;
}
const name = this.schema.getColumnNameByProperty(statement);
super.whereNot(name, value);
return this;
}
/**
* Set a where like statement in your query.
*/
whereLike(column, value) {
const name = this.schema.getColumnNameByProperty(column);
super.whereLike(name, value);
return this;
}
/**
* Set a where ILike statement in your query.
*/
whereILike(column, value) {
const name = this.schema.getColumnNameByProperty(column);
super.whereILike(name, value);
return this;
}
/**
* Set a where in statement in your query.
*/
whereIn(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.whereIn(name, values);
return this;
}
/**
* Set a where not in statement in your query.
*/
whereNotIn(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.whereNotIn(name, values);
return this;
}
/**
* Set a where between statement in your query.
*/
whereBetween(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.whereBetween(name, values);
return this;
}
/**
* Set a where not between statement in your query.
*/
whereNotBetween(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.whereNotBetween(name, values);
return this;
}
/**
* Set a where null statement in your query.
*/
whereNull(column) {
const name = this.schema.getColumnNameByProperty(column);
super.whereNull(name);
return this;
}
/**
* Set a where not null statement in your query.
*/
whereNotNull(column) {
const name = this.schema.getColumnNameByProperty(column);
super.whereNotNull(name);
return this;
}
/**
* Set a orWhere statement in your query.
*/
orWhere(statement, operation, value) {
if (Is.Function(statement)) {
const driver = this.driver.clone();
super.orWhere(query => {
const modelQb = new ModelQueryBuilder(this.Model, driver.setQueryBuilder(query, { useSetQB: true }));
statement(modelQb);
});
return this;
}
if (!operation) {
const parsed = this.schema.propertiesToColumnNames(statement);
super.orWhere(parsed);
return this;
}
const name = this.schema.getColumnNameByProperty(statement);
super.orWhere(name, operation, value);
return this;
}
/**
* Set a orWhere not statement in your query.
*/
orWhereNot(statement, value) {
if (Is.Function(statement)) {
const driver = this.driver.clone();
super.orWhereNot(query => {
const modelQb = new ModelQueryBuilder(this.Model, driver.setQueryBuilder(query, { useSetQB: true }));
statement(modelQb);
});
return this;
}
if (!value) {
const parsed = this.schema.propertiesToColumnNames(statement);
super.orWhereNot(parsed);
return this;
}
const name = this.schema.getColumnNameByProperty(statement);
super.orWhereNot(name, value);
return this;
}
/**
* Set a orWhere like statement in your query.
*/
orWhereLike(statement, value) {
if (!value) {
const parsed = this.schema.propertiesToColumnNames(statement);
super.orWhereLike(parsed);
return this;
}
const name = this.schema.getColumnNameByProperty(statement);
super.orWhereLike(name, value);
return this;
}
/**
* Set a orWhere ILike statement in your query.
*/
orWhereILike(statement, value) {
if (!value) {
const parsed = this.schema.propertiesToColumnNames(statement);
super.orWhereILike(parsed);
return this;
}
const name = this.schema.getColumnNameByProperty(statement);
super.orWhereILike(name, value);
return this;
}
/**
* Set a orWhere in statement in your query.
*/
orWhereIn(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.orWhereIn(name, values);
return this;
}
/**
* Set a orWhere not in statement in your query.
*/
orWhereNotIn(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.orWhereNotIn(name, values);
return this;
}
/**
* Set a orWhere between statement in your query.
*/
orWhereBetween(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.orWhereBetween(name, values);
return this;
}
/**
* Set a orWhere not between statement in your query.
*/
orWhereNotBetween(column, values) {
const name = this.schema.getColumnNameByProperty(column);
super.orWhereNotBetween(name, values);
return this;
}
/**
* Set a orWhere null statement in your query.
*/
orWhereNull(column) {
const name = this.schema.getColumnNameByProperty(column);
super.orWhereNull(name);
return this;
}
/**
* Set a orWhere not null statement in your query.
*/
orWhereNotNull(column) {
const name = this.schema.getColumnNameByProperty(column);
super.orWhereNotNull(name);
return this;
}
/**
* Set an order by statement in your query.
*/
orderBy(column, direction = 'ASC') {
const name = this.schema.getColumnNameByProperty(column);
super.orderBy(name, direction);
return this;
}
/**
* Order the results easily by the latest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
latest(column) {
if (!column) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
column = 'createdAt';
}
const name = this.schema.getColumnNameByProperty(column);
super.latest(name);
return this;
}
/**
* Order the results easily by the oldest date. By default, the result will
* be ordered by the table's "createdAt" column.
*/
oldest(column) {
if (!column) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
column = 'createdAt';
}
const name = this.schema.getColumnNameByProperty(column);
super.oldest(name);
return this;
}
/**
* Set the internal selected properties and soft delete
* queries.
*/
setInternalQueries(options) {
options = Options.create(options, {
addSelect: true,
addSoftDelete: true
});
if (options.addSelect) {
super.select(...this.selectColumns);
}
if (options.addSoftDelete) {
super.when(this.isSoftDelete, query => query.whereNull(this.DELETED_AT_NAME));
}
}
/**
* Verify that columns with `isNullable` property
* can be created in database.
*/
validateNullable(data) {
if (!this.isToValidateNullable) {
return;
}
const records = [];
for (const column of this.schema.getAllNotNullableColumns()) {
const value = data[column.name];
if (value === undefined || value === null) {
records.push(column.property);
}
}
if (!Is.Empty(records)) {
throw new NullableValueException(records);
}
}
/**
* Verify that columns with isUnique property
* can be created in database.
*/
async validateUnique(data, isUpdate = false) {
if (!this.isToValidateUnique) {
return;
}
const records = {};
for (const column of this.schema.getAllUniqueColumns()) {
const value = data[column.name];
if (value === undefined) {
continue;
}
if (isUpdate) {
const data = await this.Model.query()
.where(column.name, value)
.findMany();
if (data.length > 1) {
records[column.property] = value;
continue;
}
}
const isDuplicated = await this.Model.query()
.where(column.name, value)
.exists();
if (isDuplicated) {
records[column.property] = value;
}
}
if (!Is.Empty(records)) {
throw new UniqueValueException(records);
}
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Macroable } from '@athenna/common';
import { BaseModel } from '#src/models/BaseModel';
export declare class ModelFactory<M extends BaseModel = any, R = M> extends Macroable {
/**
* The model that will be used to fabricate
* instances from.
*/
private Model;
/**
* The number of models to be created.
*/
private _count;
/**
* Set if the soft delete state is active or not.
*/
private _trashed;
/**
* Set the returning key that this factory will return.
*/
private _returning;
constructor(model: typeof BaseModel);
returning(key: '*'): ModelFactory<M>;
returning<T extends keyof M>(key: T): ModelFactory<M, M[T]>;
/**
* Same as returning method, but return the type of
* the key of the model to avoid using "as any" or
* other kind of stuffs to fix the type in definition
* method.
*
* Only the type is modified for convenience, the real
* return type of this method is still the ModelFactory
* instance.
*/
returningAs<T extends keyof M>(key: T): M[T];
/**
* Set the soft delete state in your model to
* fabricate deleted data.
*/
trashed(): this;
/**
* Remove the soft delete state in your model to
* not fabricate deleted data.
*/
untrashed(): this;
count(number: 1): ModelFactory<M, R>;
count(number: number): ModelFactory<M, R[]>;
/**
* Make models without creating it on database.
*/
make(override?: Partial<M>): Promise<R>;
/**
* Create models creating it on database.
*/
create(override?: Partial<M>): Promise<R>;
/**
* Execute the definition method and return data.
*/
private getDefinition;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { debug } from '#src/debug';
import { Macroable } from '@athenna/common';
import { BaseModel } from '#src/models/BaseModel';
export class ModelFactory extends Macroable {
constructor(model) {
super();
/**
* The number of models to be created.
*/
this._count = 1;
/**
* Set if the soft delete state is active or not.
*/
this._trashed = false;
/**
* Set the returning key that this factory will return.
*/
this._returning = '*';
this.Model = model;
}
/**
* Set the returning key that this factory will
* return after making or creating an instance.
*/
returning(key) {
this._returning = key;
return this;
}
/**
* Same as returning method, but return the type of
* the key of the model to avoid using "as any" or
* other kind of stuffs to fix the type in definition
* method.
*
* Only the type is modified for convenience, the real
* return type of this method is still the ModelFactory
* instance.
*/
returningAs(key) {
this._returning = key;
return this;
}
/**
* Set the soft delete state in your model to
* fabricate deleted data.
*/
trashed() {
this._trashed = true;
return this;
}
/**
* Remove the soft delete state in your model to
* not fabricate deleted data.
*/
untrashed() {
this._trashed = false;
return this;
}
/**
* Set the number of models to be created.
*/
count(number) {
this._count = number;
return this;
}
/**
* Make models without creating it on database.
*/
async make(override = {}) {
const promises = [];
for (let i = 1; i <= this._count; i++) {
promises.push(this.getDefinition(override, 'make'));
}
let data = await Promise.all(promises);
data = data.map(d => {
if (this._returning !== '*') {
return d[this._returning];
}
const model = new this.Model();
Object.keys(d).forEach(key => (model[key] = d[key]));
return model;
});
if (this._count === 1) {
return data[0];
}
return data;
}
/**
* Create models creating it on database.
*/
async create(override = {}) {
const promises = [];
for (let i = 1; i <= this._count; i++) {
promises.push(this.getDefinition(override, 'create'));
}
let data = await this.Model.createMany(await Promise.all(promises), false);
if (this._returning !== '*') {
data = data.map(d => d[this._returning]);
}
if (this._count === 1) {
return data[0];
}
return data;
}
/**
* Execute the definition method and return data.
*/
async getDefinition(override, method) {
const data = await this.Model.definition();
const promises = Object.keys(data).reduce((promises, key) => {
if ((override && override[key]) || !(data[key] instanceof ModelFactory)) {
return promises;
}
const SubFactory = data[key];
const result = SubFactory[method]().then(r => (data[key] = r));
promises.push(result);
return promises;
}, []);
await Promise.all(promises);
if (this._trashed) {
const column = this.Model.schema().getDeletedAtColumn();
if (!column) {
debug('there is any column with isDeleteDate option as true in model %s. trashed option will be ignored.', this.Model.name);
}
else {
data[column.property] = new Date();
}
}
return {
...data,
...override
};
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Macroable } from '@athenna/common';
import type { RelationOptions } from '#src/types';
import type { BaseModel } from '#src/models/BaseModel';
import { ModelSchema } from '#src/models/schemas/ModelSchema';
export declare class ModelGenerator<M extends BaseModel = any> extends Macroable {
/**
* The model that will be generated instances
* from.
*/
private Model;
/**
* The model schema that will be used to search
* for columns and relations.
*/
private schema;
constructor(model: new () => M, schema: ModelSchema<M>);
/**
* Generate one model instance with relations loaded.
*/
generateOne(data: any): Promise<M>;
/**
* Generate models instances with relations loaded.
*/
generateMany(data: any[]): Promise<M[]>;
/**
* Instantiate one model using vanilla database data.
*/
private instantiateOne;
/**
* Populate one object data in the model instance
* using the column dictionary to map keys.
*/
private populate;
/**
* Include one relation to one model.
*/
includeRelation(model: M, relation: RelationOptions): Promise<M>;
/**
* Include all relations to one model.
*/
private includeRelations;
/**
* Include one relation for all models.
*/
private includeRelationOfAll;
/**
* Include all relations for all models.
*/
private includeRelationsOfAll;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Macroable } from '@athenna/common';
import { ObjectId } from '#src/helpers/ObjectId';
import { ModelSchema } from '#src/models/schemas/ModelSchema';
import { HasOneRelation } from '#src/models/relations/HasOne/HasOneRelation';
import { HasManyRelation } from '#src/models/relations/HasMany/HasManyRelation';
import { BelongsToRelation } from '#src/models/relations/BelongsTo/BelongsToRelation';
import { BelongsToManyRelation } from '#src/models/relations/BelongsToMany/BelongsToManyRelation';
export class ModelGenerator extends Macroable {
constructor(model, schema) {
super();
this.Model = model;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
this.schema = schema;
}
/**
* Generate one model instance with relations loaded.
*/
async generateOne(data) {
if (!data) {
return undefined;
}
const model = this.instantiateOne(data);
return this.includeRelations(model);
}
/**
* Generate models instances with relations loaded.
*/
async generateMany(data) {
if (!data || !data.length) {
return [];
}
const models = await Promise.all(data.map(d => this.instantiateOne(d)));
return this.includeRelationsOfAll(models);
}
/**
* Instantiate one model using vanilla database data.
*/
instantiateOne(data) {
return this.populate(data, new this.Model());
}
/**
* Populate one object data in the model instance
* using the column dictionary to map keys.
*/
populate(object, model) {
Object.keys(object).forEach(key => {
const column = this.schema.getColumnByName(key);
if (!column) {
return;
}
if (ObjectId.isValidObject(object[key])) {
object[key] = object[key].toString();
}
model[column.property] = object[key];
});
return model;
}
/**
* Include one relation to one model.
*/
async includeRelation(model, relation) {
switch (relation.type) {
case 'hasOne':
return HasOneRelation.load(model, relation);
case 'hasMany':
return HasManyRelation.load(model, relation);
case 'belongsTo':
return BelongsToRelation.load(model, relation);
case 'belongsToMany':
return BelongsToManyRelation.load(model, relation);
default:
return model;
}
}
/**
* Include all relations to one model.
*/
async includeRelations(model) {
const relations = this.schema.getIncludedRelations();
if (!relations || !relations.length) {
return model.setOriginal();
}
for (const relation of relations) {
model = await this.includeRelation(model, relation);
}
if (!model) {
return undefined;
}
return model.setOriginal();
}
/**
* Include one relation for all models.
*/
async includeRelationOfAll(models, relation) {
switch (relation.type) {
case 'hasOne':
return HasOneRelation.loadAll(models, relation);
case 'hasMany':
return HasManyRelation.loadAll(models, relation);
case 'belongsTo':
return BelongsToRelation.loadAll(models, relation);
case 'belongsToMany':
return BelongsToManyRelation.loadAll(models, relation);
default:
return models;
}
}
/**
* Include all relations for all models.
*/
async includeRelationsOfAll(models) {
const relations = this.schema.getIncludedRelations();
if (!relations || !relations.length) {
return models.map(model => model.setOriginal());
}
for (const relation of relations) {
models = await this.includeRelationOfAll(models, relation);
}
return models.map(model => model.setOriginal());
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { BelongsToOptions } from '#src/types';
import type { BaseModel } from '#src/models/BaseModel';
import type { Driver } from '#src/database/drivers/Driver';
export declare class BelongsToRelation {
/**
* Get the options with defined default values.
*/
private static options;
/**
* Load a belongs to relation.
*/
static load(model: BaseModel, relation: BelongsToOptions): Promise<any>;
/**
* Load all models that belongs to relation.
*/
static loadAll(models: BaseModel[], relation: BelongsToOptions): Promise<any[]>;
/**
* Apply a where has relation to the query when the relation
* is a belongs to the given model.
*/
static whereHas(Model: typeof BaseModel, query: Driver, relation: BelongsToOptions): void;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { String } from '@athenna/common';
export class BelongsToRelation {
/**
* Get the options with defined default values.
*/
static options(relation) {
const RelationModel = relation.model();
relation.primaryKey =
relation.primaryKey || RelationModel.schema().getMainPrimaryKeyProperty();
relation.foreignKey =
relation.foreignKey || `${String.toCamelCase(RelationModel.name)}Id`;
return relation;
}
/**
* Load a belongs to relation.
*/
static async load(model, relation) {
this.options(relation);
model[relation.property] = await relation
.model()
.query()
.where(relation.primaryKey, model[relation.foreignKey])
.when(relation.closure, relation.closure)
.find();
return model;
}
/**
* Load all models that belongs to relation.
*/
static async loadAll(models, relation) {
this.options(relation);
const foreignValues = models.map(model => model[relation.foreignKey]);
const results = await relation
.model()
.query()
.whereIn(relation.primaryKey, foreignValues)
.when(relation.closure, relation.closure)
.findMany();
const map = new Map();
results.forEach(result => map.set(result[relation.primaryKey], result));
return models.map(model => {
model[relation.property] = map.get(model[relation.foreignKey]);
return model;
});
}
/**
* Apply a where has relation to the query when the relation
* is a belongs to the given model.
*/
static whereHas(Model, query, relation) {
const schema = Model.schema();
const RelationModel = relation.model();
const primaryKey = schema.getMainPrimaryKeyName();
const foreignKey = schema.getColumnNameByProperty(relation.foreignKey) ||
schema.getColumnNameByProperty(`${String.toCamelCase(RelationModel.name)}Id`);
let whereRaw = `${Model.table()}.${foreignKey} = ${RelationModel.table()}.${primaryKey}`;
switch (RelationModel.schema().getModelDriverName()) {
case 'sqlite':
case 'postgres':
whereRaw = `"${Model.table()}"."${foreignKey}" = "${RelationModel.table()}"."${primaryKey}"`;
}
RelationModel.query()
.setDriver(query, RelationModel.table())
.whereRaw(whereRaw)
.when(relation.closure, relation.closure);
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { BaseModel } from '#src/models/BaseModel';
import type { BelongsToManyOptions } from '#src/types';
import type { Driver } from '#src/database/drivers/Driver';
export declare class BelongsToManyRelation {
/**
* Get the options with defined default values.
*/
private static options;
/**
* Load a belongs to many relation.
*/
static load(model: BaseModel, relation: BelongsToManyOptions): Promise<any>;
/**
* Load all models that belongs to relation.
*/
static loadAll(models: BaseModel[], relation: BelongsToManyOptions): Promise<any[]>;
/**
* Apply a where has relation to the query when the given model
* belongs to many relations.
*/
static whereHas(Model: typeof BaseModel, query: Driver, relation: BelongsToManyOptions): void;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { String } from '@athenna/common';
export class BelongsToManyRelation {
/**
* Get the options with defined default values.
*/
static options(relation) {
const RelationModel = relation.model();
const PivotModel = relation.pivotModel();
relation.pivotTable = relation.pivotTable || PivotModel.table();
relation.relationPrimaryKey =
RelationModel.schema().getMainPrimaryKeyProperty();
relation.relationForeignKey = `${String.toCamelCase(RelationModel.name)}Id`;
return relation;
}
/**
* Load a belongs to many relation.
*/
static async load(model, relation) {
this.options(relation);
const pivotData = await relation
.pivotModel()
.query()
.where(relation.foreignKey, model[relation.primaryKey])
.findMany();
const relationIds = pivotData.map(d => d[relation.relationForeignKey]);
model[relation.property] = await relation
.model()
.query()
.whereIn(relation.relationPrimaryKey, relationIds)
.when(relation.closure, relation.closure)
.findMany();
return model;
}
/**
* Load all models that belongs to relation.
*/
static async loadAll(models, relation) {
this.options(relation);
const primaryKeys = models.map(m => m[relation.primaryKey]);
const pivotData = await relation
.pivotModel()
.query()
.whereIn(relation.foreignKey, primaryKeys)
.findMany();
const pivotDataMap = new Map();
const relationForeignKey = [];
pivotData.forEach(data => {
relationForeignKey.push(data[relation.relationForeignKey]);
const array = pivotDataMap.get(data[relation.foreignKey]) || [];
array.push(data[relation.relationForeignKey]);
pivotDataMap.set(data[relation.foreignKey], array);
});
const results = await relation
.model()
.query()
.whereIn(relation.relationPrimaryKey, relationForeignKey)
.when(relation.closure, relation.closure)
.findMany();
const map = new Map();
results.forEach(result => map.set(result[relation.relationPrimaryKey], result));
return models.map(model => {
const ids = pivotDataMap.get(model[relation.primaryKey]) || [];
model[relation.property] = ids
.map(id => map.get(id))
.filter(data => data !== undefined);
return model;
});
}
/**
* Apply a where has relation to the query when the given model
* belongs to many relations.
*/
static whereHas(Model, query, relation) {
const PivotModel = relation.pivotModel();
const RelationModel = relation.model();
const modelTable = Model.table();
const pivotTable = PivotModel.table();
const relatedTable = RelationModel.table();
const modelPK = Model.schema().getMainPrimaryKeyName();
const relatedPK = RelationModel.schema().getMainPrimaryKeyName();
const pivotFK = PivotModel.schema().getColumnNameByProperty(relation.foreignKey) ||
PivotModel.schema().getColumnNameByProperty(`${String.toCamelCase(Model.name)}Id`);
const pivotRK = PivotModel.schema().getColumnNameByProperty(relation.relationForeignKey) ||
PivotModel.schema().getColumnNameByProperty(`${String.toCamelCase(RelationModel.name)}Id`);
let whereRaw = `${pivotTable}.${pivotFK} = ${modelTable}.${modelPK}`;
switch (PivotModel.schema().getModelDriverName()) {
case 'sqlite':
case 'postgres':
whereRaw = `"${pivotTable}"."${pivotFK}" = "${modelTable}"."${modelPK}"`;
}
PivotModel.query()
.setDriver(query, pivotTable)
.whereRaw(whereRaw)
.whereExists(innerQuery => {
let whereRaw = `${relatedTable}.${relatedPK} = ${pivotTable}.${pivotRK}`;
switch (RelationModel.schema().getModelDriverName()) {
case 'sqlite':
case 'postgres':
whereRaw = `"${relatedTable}"."${relatedPK}" = "${pivotTable}"."${pivotRK}"`;
}
RelationModel.query()
.setDriver(innerQuery, relatedTable)
.whereRaw(whereRaw)
.when(relation.closure, relation.closure);
});
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { HasManyOptions } from '#src/types';
import type { BaseModel } from '#src/models/BaseModel';
import type { Driver } from '#src/database/drivers/Driver';
export declare class HasManyRelation {
/**
* Load a has many relation.
*/
static load(model: BaseModel, relation: HasManyOptions): Promise<any>;
/**
* Load all models that has one relation.
*/
static loadAll(models: BaseModel[], relation: HasManyOptions): Promise<any[]>;
/**
* Apply a where has relation to the query when the given model
* has many of the relation.
*/
static whereHas(Model: typeof BaseModel, query: Driver, relation: HasManyOptions): void;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { String } from '@athenna/common';
export class HasManyRelation {
/**
* Load a has many relation.
*/
static async load(model, relation) {
model[relation.property] = await relation
.model()
.query()
.where(relation.foreignKey, model[relation.primaryKey])
.when(relation.closure, relation.closure)
.findMany();
return model;
}
/**
* Load all models that has one relation.
*/
static async loadAll(models, relation) {
const primaryValues = models.map(model => model[relation.primaryKey]);
const results = await relation
.model()
.query()
.whereIn(relation.foreignKey, primaryValues)
.when(relation.closure, relation.closure)
.findMany();
const map = new Map();
results.forEach(result => {
const array = map.get(result[relation.foreignKey]) || [];
array.push(result);
map.set(result[relation.foreignKey], array);
});
return models.map(model => {
model[relation.property] = map.get(model[relation.primaryKey]) || [];
return model;
});
}
/**
* Apply a where has relation to the query when the given model
* has many of the relation.
*/
static whereHas(Model, query, relation) {
const schema = Model.schema();
const RelationModel = relation.model();
const primaryKey = schema.getMainPrimaryKeyName();
const foreignKey = schema.getColumnNameByProperty(relation.foreignKey) ||
schema.getColumnNameByProperty(`${String.toCamelCase(RelationModel.name)}Id`);
let whereRaw = `${RelationModel.table()}.${foreignKey} = ${Model.table()}.${primaryKey}`;
switch (RelationModel.schema().getModelDriverName()) {
case 'sqlite':
case 'postgres':
whereRaw = `"${RelationModel.table()}"."${foreignKey}" = "${Model.table()}"."${primaryKey}"`;
}
RelationModel.query()
.setDriver(query, RelationModel.table())
.whereRaw(whereRaw)
.when(relation.closure, relation.closure);
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { HasOneOptions } from '#src/types';
import type { BaseModel } from '#src/models/BaseModel';
import type { Driver } from '#src/database/drivers/Driver';
export declare class HasOneRelation {
/**
* Load a has one relation.
*/
static load(model: BaseModel, relation: HasOneOptions): Promise<any>;
/**
* Load all models that has one relation.
*/
static loadAll(models: BaseModel[], relation: HasOneOptions): Promise<any[]>;
/**
* Apply a where has relation to the query when the given model
* has one of the relation.
*/
static whereHas(Model: typeof BaseModel, query: Driver, relation: HasOneOptions): void;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { String } from '@athenna/common';
export class HasOneRelation {
/**
* Load a has one relation.
*/
static async load(model, relation) {
model[relation.property] = await relation
.model()
.query()
.where(relation.foreignKey, model[relation.primaryKey])
.when(relation.closure, relation.closure)
.find();
return model;
}
/**
* Load all models that has one relation.
*/
static async loadAll(models, relation) {
const primaryValues = models.map(model => model[relation.primaryKey]);
const results = await relation
.model()
.query()
.whereIn(relation.foreignKey, primaryValues)
.when(relation.closure, relation.closure)
.findMany();
const map = new Map();
results.forEach(result => map.set(result[relation.foreignKey], result));
return models.map(model => {
model[relation.property] = map.get(model[relation.primaryKey]);
return model;
});
}
/**
* Apply a where has relation to the query when the given model
* has one of the relation.
*/
static whereHas(Model, query, relation) {
const schema = Model.schema();
const RelationModel = relation.model();
const primaryKey = schema.getMainPrimaryKeyName();
const foreignKey = schema.getColumnNameByProperty(relation.foreignKey) ||
schema.getColumnNameByProperty(`${String.toCamelCase(RelationModel.name)}Id`);
let whereRaw = `${RelationModel.table()}.${foreignKey} = ${Model.table()}.${primaryKey}`;
switch (RelationModel.schema().getModelDriverName()) {
case 'sqlite':
case 'postgres':
whereRaw = `"${RelationModel.table()}"."${foreignKey}" = "${Model.table()}"."${primaryKey}"`;
}
RelationModel.query()
.setDriver(query, RelationModel.table())
.whereRaw(whereRaw)
.when(relation.closure, relation.closure);
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { ColumnOptions, ModelColumns, ModelRelations, RelationOptions } from '#src/types';
import type { BaseModel } from '#src/models/BaseModel';
import { Macroable } from '@athenna/common';
import type { ModelQueryBuilder } from '#src/models/builders/ModelQueryBuilder';
export declare class ModelSchema<M extends BaseModel = any> extends Macroable {
/**
* Save the columns defined by @Column annotation.
*/
columns: ColumnOptions[];
/**
* Save the relations defined by \@HasOne, \@BelongsTo,
* \@HasMany and \@ManyToMany annotations.
*/
relations: RelationOptions[];
/**
* The model class that is going to be used
* to craft the schema.
*/
private Model;
constructor(model: any);
/**
* Sync the database creating migrations
* or schemas in the database connection.
*/
sync(): Promise<void>;
/**
* Get the model name set for the schema.
*/
getModelName(): string;
/**
* Get the model table set for the schema.
*/
getModelTable(): string;
/**
* Get the model driver name.
*/
getModelDriverName(): any;
/**
* Get the model driver.
*/
getModelDriver(): import("../../database/drivers/MongoDriver.js").MongoDriver | import("../../database/drivers/MySqlDriver.js").MySqlDriver | import("../../database/drivers/SqliteDriver.js").SqliteDriver | import("../../database/drivers/PostgresDriver.js").PostgresDriver | typeof import("../../database/drivers/FakeDriver.js").FakeDriver;
/**
* Get the model connection name.
*/
getModelConnection(): string;
/**
* Get the column options of the main primary key.
*/
getMainPrimaryKey(): ColumnOptions;
/**
* Get the main primary key column name.
*/
getMainPrimaryKeyName(): string;
/**
* Get the main primary key property.
*/
getMainPrimaryKeyProperty(): string;
/**
* Convert an object using properties to database use
* column names.
*/
propertiesToColumnNames(data: Partial<M> | ModelColumns<M>, options?: {
attributes?: Record<string, any>;
cleanPersist?: boolean;
}): {};
/**
* Get the column options where column has isCreateDate
* as true.
*/
getCreatedAtColumn(): ColumnOptions;
/**
* Get the column options where column has isUpdateDate
* as true.
*/
getUpdatedAtColumn(): ColumnOptions;
/**
* Get the column options where column has isDeleteDate
* as true.
*/
getDeletedAtColumn(): ColumnOptions;
/**
* Get all column properties as an array of string.
*/
getAllColumnProperties(): string[];
/**
* Get all column names as an array of string.
*/
getAllColumnNames(): string[];
/**
* Get all columns where unique option is true.
*/
getAllUniqueColumns(): ColumnOptions[];
/**
* Get all columns where hidden option is true.
*/
getAllHiddenColumns(): ColumnOptions[];
/**
* Get all columns where nullable option is false.
*/
getAllNotNullableColumns(): ColumnOptions[];
/**
* Validate that model has createdAt and updatedAt
* column defined.
*/
hasTimestamps(): boolean;
/**
* Get the column options by the column database name.
*/
getColumnByName(column: string | ModelColumns<M>): ColumnOptions;
/**
* Get the column options by the column database name.
*
* If property cannot be found, the column name will be used.
*/
getPropertyByColumnName(column: string | ModelColumns<M>): string;
/**
* Get all the properties names by an array of column database names.
*
* If property cannot be found, the column name will be used.
*/
getPropertiesByColumnNames(columns: string[] | ModelColumns<M>[]): string[];
/**
* Get the column options by the model class property.
*/
getColumnByProperty(property: string | ModelColumns<M>): ColumnOptions;
/**
* Get the column name by the model class property.
*
* If the column name cannot be found, the property will be used.
*/
getColumnNameByProperty(property: string | ModelColumns<M>): string;
/**
* Get all the columns names by an array of model class properties.
*
* If the column name cannot be found, the property will be used.
*/
getColumnNamesByProperties(properties: string[] | ModelColumns<M>[]): string[];
/**
* Get the relation by the class property name.
*/
getRelationByProperty(property: string | ModelColumns<M>): RelationOptions;
/**
* Return the relation options only from relations
* that are included.
*/
getIncludedRelations(): RelationOptions[];
/**
* Return the relation properties.
*/
getRelationProperties(): string[];
/**
* Include a relation by setting the isIncluded
* option to true.
*/
includeRelation(property: string | ModelRelations<M>, closure?: (query: ModelQueryBuilder) => any): RelationOptions;
/**
* Include a relation by setting the isWhereHasIncluded
* option to true.
*/
includeWhereHasRelation(property: string | ModelRelations<M>, closure?: (query: ModelQueryBuilder) => any): RelationOptions;
/**
* Created nested relationships closure to
* load relationship's relationships
*/
private createdNestedRelationClosure;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Database } from '#src/facades/Database';
import { Annotation } from '#src/helpers/Annotation';
import { Json, Options, Macroable } from '@athenna/common';
import { NotImplementedRelationException } from '#src/exceptions/NotImplementedRelationException';
export class ModelSchema extends Macroable {
constructor(model) {
super();
this.Model = model;
this.columns = Json.copy(Annotation.getColumnsMeta(model));
this.relations = Json.copy(Annotation.getRelationsMeta(model));
}
/**
* Sync the database creating migrations
* or schemas in the database connection.
*/
async sync() {
await this.getModelDriver().sync(this);
}
/**
* Get the model name set for the schema.
*/
getModelName() {
return this.Model.name;
}
/**
* Get the model table set for the schema.
*/
getModelTable() {
return this.Model.table();
}
/**
* Get the model driver name.
*/
getModelDriverName() {
const connection = this.getModelConnection();
return Config.get(`database.connections.${connection}.driver`);
}
/**
* Get the model driver.
*/
getModelDriver() {
const connection = this.getModelConnection();
return Database.connection(connection).driver;
}
/**
* Get the model connection name.
*/
getModelConnection() {
const connection = this.Model.connection();
if (connection === 'default') {
return Config.get('database.default');
}
return connection;
}
/**
* Get the column options of the main primary key.
*/
getMainPrimaryKey() {
let options = this.columns.find(c => c.isMainPrimary);
if (!options) {
options = this.columns.find(c => c.name === 'id');
if (options) {
if (!options.hasSetName && this.getModelDriverName() === 'mongo') {
options.name = '_id';
}
}
}
if (!options) {
options = this.columns.find(c => c.name === '_id');
}
return options;
}
/**
* Get the main primary key column name.
*/
getMainPrimaryKeyName() {
const options = this.getMainPrimaryKey();
return options?.name || 'id';
}
/**
* Get the main primary key property.
*/
getMainPrimaryKeyProperty() {
const options = this.getMainPrimaryKey();
return options?.property || 'id';
}
/**
* Convert an object using properties to database use
* column names.
*/
propertiesToColumnNames(data, options = {}) {
options = Options.create(options, {
attributes: {},
cleanPersist: false
});
const parsed = {};
Object.keys(data).forEach(key => {
const column = this.getColumnByProperty(key) || {
name: key,
persist: false
};
if (!column.persist && options.cleanPersist) {
return;
}
if (data[key] === undefined) {
return;
}
parsed[column.name] = data[key];
});
Object.keys(options.attributes).forEach(key => {
const column = this.getColumnByProperty(key) || {
name: key,
persist: false
};
if (parsed[column.name] !== undefined) {
return;
}
parsed[column.name] = options.attributes[key];
});
return parsed;
}
/**
* Get the column options where column has isCreateDate
* as true.
*/
getCreatedAtColumn() {
return this.columns.find(c => c.isCreateDate);
}
/**
* Get the column options where column has isUpdateDate
* as true.
*/
getUpdatedAtColumn() {
return this.columns.find(c => c.isUpdateDate);
}
/**
* Get the column options where column has isDeleteDate
* as true.
*/
getDeletedAtColumn() {
return this.columns.find(c => c.isDeleteDate);
}
/**
* Get all column properties as an array of string.
*/
getAllColumnProperties() {
return this.columns.map(column => column.property);
}
/**
* Get all column names as an array of string.
*/
getAllColumnNames() {
return this.columns.map(column => column.name);
}
/**
* Get all columns where unique option is true.
*/
getAllUniqueColumns() {
return this.columns.filter(column => column.isUnique);
}
/**
* Get all columns where hidden option is true.
*/
getAllHiddenColumns() {
return this.columns.filter(column => column.isHidden);
}
/**
* Get all columns where nullable option is false.
*/
getAllNotNullableColumns() {
return this.columns.filter(column => !column.isNullable);
}
/**
* Validate that model has createdAt and updatedAt
* column defined.
*/
hasTimestamps() {
return !!this.getCreatedAtColumn() && !!this.getUpdatedAtColumn();
}
/**
* Get the column options by the column database name.
*/
getColumnByName(column) {
return this.columns.find(c => c.name === column);
}
/**
* Get the column options by the column database name.
*
* If property cannot be found, the column name will be used.
*/
getPropertyByColumnName(column) {
return this.getColumnByName(column)?.property || column;
}
/**
* Get all the properties names by an array of column database names.
*
* If property cannot be found, the column name will be used.
*/
getPropertiesByColumnNames(columns) {
return columns.map(column => this.getPropertyByColumnName(column));
}
/**
* Get the column options by the model class property.
*/
getColumnByProperty(property) {
return this.columns.find(c => c.property === property);
}
/**
* Get the column name by the model class property.
*
* If the column name cannot be found, the property will be used.
*/
getColumnNameByProperty(property) {
return this.getColumnByProperty(property)?.name || property;
}
/**
* Get all the columns names by an array of model class properties.
*
* If the column name cannot be found, the property will be used.
*/
getColumnNamesByProperties(properties) {
return properties.map(property => this.getColumnNameByProperty(property));
}
/**
* Get the relation by the class property name.
*/
getRelationByProperty(property) {
return this.relations.find(c => c.property === property);
}
/**
* Return the relation options only from relations
* that are included.
*/
getIncludedRelations() {
return this.relations.filter(r => r.isIncluded || r.isWhereHasIncluded);
}
/**
* Return the relation properties.
*/
getRelationProperties() {
return this.relations.map(r => r.property);
}
/**
* Include a relation by setting the isIncluded
* option to true.
*/
includeRelation(property, closure) {
const model = this.Model.name;
if (property.includes('.')) {
const [first, ...rest] = property.split('.');
property = first;
closure = this.createdNestedRelationClosure(rest);
}
const options = this.getRelationByProperty(property);
if (!options) {
throw new NotImplementedRelationException(property, model, this.relations.map(r => r.property).join(', '));
}
const i = this.relations.indexOf(options);
options.isIncluded = true;
options.closure = closure;
this.relations[i] = options;
return options;
}
/**
* Include a relation by setting the isWhereHasIncluded
* option to true.
*/
includeWhereHasRelation(property, closure) {
const model = this.Model.name;
if (property.includes('.')) {
const [first, ...rest] = property.split('.');
property = first;
closure = this.createdNestedRelationClosure(rest);
}
const options = this.getRelationByProperty(property);
if (!options) {
throw new NotImplementedRelationException(property, model, this.relations.map(r => r.property).join(', '));
}
const i = this.relations.indexOf(options);
options.isWhereHasIncluded = true;
options.closure = closure;
this.relations[i] = options;
return options;
}
/**
* Created nested relationships closure to
* load relationship's relationships
*/
createdNestedRelationClosure(relationships) {
if (relationships.length === 1) {
return (query) => query.with(relationships[0]);
}
const [first, ...rest] = relationships;
const closure = this.createdNestedRelationClosure(rest);
return (query) => query.with(first, closure);
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { ServiceProvider } from '@athenna/ioc';
export declare class DatabaseProvider extends ServiceProvider {
register(): Promise<void>;
shutdown(): Promise<void>;
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { Module } from '@athenna/common';
import { ServiceProvider } from '@athenna/ioc';
import { DatabaseImpl } from '#src/database/DatabaseImpl';
export class DatabaseProvider extends ServiceProvider {
async register() {
this.container.instance('athennaDbOpts', undefined);
this.container.transient('Athenna/Core/Database', DatabaseImpl);
const paths = Config.get('rc.models', []);
const promises = paths.map(path => {
return Module.resolve(path, Config.get('rc.parentURL')).then(Model => {
this.container.transient(`App/Models/${Model.name}`, Model);
if (!Model.sync()) {
return;
}
return Model.schema().sync();
});
});
await Promise.all(promises);
}
async shutdown() {
const database = this.container.use('Athenna/Core/Database');
if (!database) {
return;
}
await database.closeAll();
}
}
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { ColumnType } from '#src/types/columns/ColumnType';
export type ColumnOptions = {
/**
* The property name in class of the column.
*
* @readonly
* @default key
*/
property?: string;
/**
* The column name in database. The default
* value of this property will be the name
* of the class property as camelCase.
*
* @default String.toCamelCase(key)
*/
name?: string;
/**
* Holds if the user has set the name property
* in @Column or not.
*/
hasSetName?: boolean;
/**
* The column type in database. This value
* only matters when you are using MongoDB
* or when you want to synchronize you model
* with database FOR DEVELOPMENT purposes only.
*
* @default undefined
*/
type?: ColumnType;
/**
* The column length in database. This value only
* matters when you a want to synchronize your
* model with database FOR DEVELOPMENT purposes only.
*
* @default undefined
*/
length?: number;
/**
* Set the default value for the column before
* running your model `create()`, `createMany()`,
* `update()` and `createOrUpdate()` methods.
*
* This property doesn't change the behavior in
* your database, but only while running the above
* methods.
*
* @default null
*/
defaultTo?: any;
/**
* Set if the column is a primary key.
*
* @default false
*/
isPrimary?: boolean;
/**
* Set if the field should be hidden when
* executing the `toJSON()` method of the model.
*
* @default false
*/
isHidden?: boolean;
/**
* Set if the column is unique in database.
*
* @default false
*/
isUnique?: boolean;
/**
* Set if the column is nullable in database.
*
* @default true
*/
isNullable?: boolean;
/**
* Set if the column is an index in database.
*
* @default false
*/
isIndex?: boolean;
/**
* Set if the column is sparse in database.
*
* @default false
*/
isSparse?: boolean;
/**
* Set if the column should be persisted in database
* when executing operation like create or update.
*
* If set as `false`, Athenna will remove this column
* from this kind of operation, but it will still be
* available in operations like `find` and `findMany`.
*
* To also remove from listing operations, set isHidden
* as `true`.
*
* @default true
*/
persist?: boolean;
/**
* Set if this column is the main primary key that
* should be used when performing create queries to
* database.
*
* Your class can only have one column option with
* isMainPrimary set as true. If you set isMainPrimary
* as true, Athenna will set isPrimary as true by default.
*
* @default false
*/
isMainPrimary?: boolean;
/**
* Set if the column is createdAt field.
* Columns with `isCreateDate` as `true`
* will automatically be set with `new Date()`
* value when creating your model.
*
* The `new Date()` value will only be set
* when your column doesn't have `defaultTo`
* value set and also when the field is not
* present when calling any method that creates
* the model in database like `create()`.
*
* @default false
*/
isCreateDate?: boolean;
/**
* Set if the column is updatedAt field.
* Columns with `isUpdateDate` as `true`
* will automatically be set with `new Date()`
* value when updating your model.
*
* The `new Date()` value will only be set
* when your column doesn't have `defaultTo`
* value set and also when the field is not
* present when calling any method that updates
* the model in database like `update()`.
*
* @default false
*/
isUpdateDate?: boolean;
/**
* Set if the column is deletedAt field.
* By default, if you define add field with
* `isDeleteDate` as `true`, it will automatically
* turn on soft delete for your model, this means
* that if this column is not null, it will be
* considered a deleted value, not being retrieved
* when calling methods like `find()`.
*
* It will also automatically set `new Date()`
* value to the column when deleting your model.
*
* The `new Date()` value will only be set
* when your column doesn't have `defaultTo`
* value set and also when the field is not
* present when calling `delete()` method.
*
* @default false
*/
isDeleteDate?: boolean;
};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export {};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { Schema } from 'mongoose';
export type ColumnType = 'string' | 'uuid' | 'UUID' | 'enum' | 'integer' | 'float' | 'double' | 'numeric' | 'decimal' | 'json' | 'jsonb' | 'date' | 'datetime' | 'timestamp' | typeof String | typeof Number | typeof Date | typeof Buffer | typeof Boolean | typeof Schema.Types.UUID | typeof Schema.Types.BigInt | typeof Schema.Types.Mixed | typeof Schema.Types.ObjectId | typeof Array<any> | typeof Schema.Types.Decimal128 | typeof Map<any, any> | typeof Schema | typeof BigInt;
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export {};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { BaseModel } from '#src/models/BaseModel';
export type ColumnKeys<T> = {
[K in keyof T]: T[K] extends BaseModel | BaseModel[] ? never : K;
}[keyof Omit<T, 'save' | 'fresh' | 'refresh' | 'dirty' | 'delete' | 'restore' | 'isDirty' | 'isTrashed' | 'isPersisted' | 'setOriginal' | 'load' | 'toJSON'>];
export type ModelColumns<T> = Extract<ColumnKeys<T>, string>;
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export {};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export type ConnectionOptions = {
/**
* Force the connection to be created even if the
* connection is already opened. This option is
* useful to create a connection from scratch, meaning
* that your driver will not use the default one. This
* also means that is your responsibility to close this
* connection.
*
* @default false
*/
force?: boolean;
/**
* Save your connection in the ConnectionFactory class.
* If this is true, all the drivers will have a shared
* connection to use.
*
* @default true
*/
saveOnFactory?: boolean;
/**
* Since we are using the constructor method to create
* the connection, it could create the connection when
* we don't really want to. To avoid creating the
* connection is certain scenarios where you want to
* manipulate the driver client, set this option to `false`.
*
* @default true
*/
connect?: boolean;
};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export {};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export type Connections = 'fake' | 'mongo' | 'mysql' | 'sqlite' | 'postgres' | string;
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export {};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export type Direction = 'asc' | 'desc' | 'ASC' | 'DESC';
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export {};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { Driver } from '#src/database/drivers/Driver';
export type DriverKey = {
Driver: new (...args: any[]) => Driver;
client?: any;
};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export {};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export * from '#src/types/Direction';
export * from '#src/types/Operations';
export * from '#src/types/Connections';
export * from '#src/types/ConnectionOptions';
export * from '#src/types/columns/ColumnType';
export * from '#src/types/relations/Relation';
export * from '#src/types/columns/ModelColumns';
export * from '#src/types/columns/ColumnOptions';
export * from '#src/types/relations/HasOneOptions';
export * from '#src/types/relations/HasManyOptions';
export * from '#src/types/relations/BelongsToOptions';
export * from '#src/types/relations/BelongsToManyOptions';
export * from '#src/types/relations/RelationOptions';
export * from '#src/types/relations/ModelRelations';
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export * from '#src/types/Direction';
export * from '#src/types/Operations';
export * from '#src/types/Connections';
export * from '#src/types/ConnectionOptions';
export * from '#src/types/columns/ColumnType';
export * from '#src/types/relations/Relation';
export * from '#src/types/columns/ModelColumns';
export * from '#src/types/columns/ColumnOptions';
export * from '#src/types/relations/HasOneOptions';
export * from '#src/types/relations/HasManyOptions';
export * from '#src/types/relations/BelongsToOptions';
export * from '#src/types/relations/BelongsToManyOptions';
export * from '#src/types/relations/RelationOptions';
export * from '#src/types/relations/ModelRelations';
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export type Operations = '=' | '>' | '>=' | '<' | '<=' | '<>' | 'like' | 'ilike';
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export {};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { ModelColumns } from '#src/types';
import type { BaseModel } from '#src/models/BaseModel';
import type { ModelQueryBuilder } from '#src/models/builders/ModelQueryBuilder';
export type BelongsToManyOptions<T extends BaseModel = any, R extends BaseModel = any, P extends BaseModel = any> = {
/**
* The relation option type.
*
* @readonly
* @default 'belongsToMany'
*/
type?: 'belongsToMany';
/**
* The closure that should be executed while
* querying the relation data from database.
*
* @default undefined
*/
closure?: (query: ModelQueryBuilder<R>) => any;
/**
* The property name in class of the relation.
*
* @readonly
* @default key
*/
property?: ModelColumns<T>;
/**
* The relation model that is being referenced as
* a closure to protect models definition from import
* issues.
*
* @readonly
*/
model?: () => typeof BaseModel;
/**
* The pivot model that will be used to save the
* relations between main model and relation model.
*
* @readonly
*/
pivotModel?: () => typeof BaseModel;
/**
* Set if the model will be included when fetching
* data.
* If this option is true, you don't need to call
* methods like `with()` to eager load your relation.
*
* @default false
*/
isIncluded?: boolean;
/**
* Set if the model will be included when fetching
* data.
* If this option is true, you don't need to call
* methods like `whereHas()` to eager load your relation.
*
* @default false
*/
isWhereHasIncluded?: boolean;
/**
* The primary key is always the primary key
* of the main model.
*
* @default Model.schema().getMainPrimaryKey()
*/
primaryKey?: ModelColumns<T>;
/**
* The foreign key is the camelCase in singular
* representation of the main model table name with
* 'Id' in the end. The foreign key will always
* be defined inside the pivot model.
*
* @default `${String.toCamelCase(Model.name)}Id`
*/
foreignKey?: ModelColumns<P>;
/**
* The pivot table is always the merge of main model
* table name with relation model table name.
*
* @default PivotModel.table()
*/
pivotTable?: string;
/**
* The relation primary key is always the primary key
* of the relation model.
*
* @default Relation.schema().getMainPrimaryKeyName()
*/
relationPrimaryKey?: ModelColumns<R>;
/**
* The relation foreign key is the camelCase in singular
* representation of the relation model name with
* an 'Id' at the end. The relation foreign key will always
* be defined inside the pivot model.
*
* @default `${String.toCamelCase(Relation.name)}Id`
*/
relationForeignKey?: ModelColumns<P>;
};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export {};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { ModelColumns } from '#src/types';
import type { BaseModel } from '#src/models/BaseModel';
import type { ModelQueryBuilder } from 'src/models/builders/ModelQueryBuilder.js';
export type BelongsToOptions<T extends BaseModel = any, R extends BaseModel = any> = {
/**
* The relation option type.
*
* @readonly
* @default 'belongsTo'
*/
type?: 'belongsTo';
/**
* The closure that should be executed while
* querying the relation data from database.
*
* @default undefined
*/
closure?: (query: ModelQueryBuilder<R>) => any;
/**
* The property name in class of the relation.
*
* @readonly
* @default key
*/
property?: ModelColumns<T>;
/**
* The relation model that is being referenced as
* a closure to protect models definition from import
* issues.
*
* @readonly
*/
model?: () => typeof BaseModel;
/**
* Set if the model will be included when fetching
* data.
* If this option is true, you don't need to call
* methods like `with()` to eager load your relation.
*
* @default false
*/
isIncluded?: boolean;
/**
* Set if the model will be included when fetching
* data.
* If this option is true, you don't need to call
* methods like `whereHas()` to eager load your relation.
*
* @default false
*/
isWhereHasIncluded?: boolean;
/**
* The primary key is always the primary key
* of the relation model.
*
* @default RelationModel.schema().getMainPrimaryKey()
*/
primaryKey?: ModelColumns<R>;
/**
* The foreign key is the camelCase representation
* of the relation model name with an 'Id' at the end.
* The foreign key needs to be defined in the main model.
*
* @default `${String.toCamelCase(RelationModel.name)}Id`
*/
foreignKey?: ModelColumns<T>;
};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export {};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { ModelColumns } from '#src/types';
import type { BaseModel } from '#src/models/BaseModel';
import type { ModelQueryBuilder } from 'src/models/builders/ModelQueryBuilder.js';
export type HasManyOptions<T extends BaseModel = any, R extends BaseModel = any> = {
/**
* The relation option type.
*
* @readonly
* @default 'hasMany'
*/
type?: 'hasMany';
/**
* The closure that should be executed while
* querying the relation data from database.
*
* @default undefined
*/
closure?: (query: ModelQueryBuilder<R>) => any;
/**
* The property name in class of the relation.
*
* @readonly
* @default key
*/
property?: string;
/**
* The relation model that is being referenced as
* a closure to protect models definition from import
* issues.
*
* @readonly
*/
model?: () => typeof BaseModel;
/**
* Set if the model will be included when fetching
* data.
* If this option is true, you don't need to call
* methods like `with()` to eager load your relation.
*
* @default false
*/
isIncluded?: boolean;
/**
* Set if the model will be included when fetching
* data.
* If this option is true, you don't need to call
* methods like `whereHas()` to eager load your relation.
*
* @default false
*/
isWhereHasIncluded?: boolean;
/**
* The primary key is always the primary key
* of the main model.
*
* @default Model.schema().getMainPrimaryKey()
*/
primaryKey?: ModelColumns<T>;
/**
* The foreign key is the camelCase representation
* of the main model name with an 'Id' at the end.
*
* @default `${String.toCamelCase(Model.name)}Id`
*/
foreignKey?: ModelColumns<R>;
};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export {};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { ModelColumns } from '#src/types';
import type { BaseModel } from '#src/models/BaseModel';
import type { ModelQueryBuilder } from '#src/models/builders/ModelQueryBuilder';
export type HasOneOptions<T extends BaseModel = any, R extends BaseModel = any> = {
/**
* The relation option type.
*
* @readonly
* @default 'hasOne'
*/
type?: 'hasOne';
/**
* The closure that should be executed while
* querying the relation data from database.
*
* @default undefined
*/
closure?: (query: ModelQueryBuilder<R>) => any;
/**
* The property name in class of the relation.
*
* @readonly
* @default key
*/
property?: ModelColumns<T>;
/**
* The relation model that is being referenced as
* a closure to protect models definition from import
* issues.
*
* @readonly
*/
model?: () => typeof BaseModel;
/**
* Set if the model will be included when fetching
* data.
* If this option is true, you don't need to call
* methods like `with()` to eager load your relation.
*
* @default false
*/
isIncluded?: boolean;
/**
* Set if the model will be included when fetching
* data.
* If this option is true, you don't need to call
* methods like `whereHas()` to eager load your relation.
*
* @default false
*/
isWhereHasIncluded?: boolean;
/**
* The primary key is always the primary key
* of the main model.
*
* @default Model.schema().getMainPrimaryKey()
*/
primaryKey?: ModelColumns<T>;
/**
* The foreign key is the camelCase representation
* of the main model name with an 'Id' at the end.
*
* @default `${String.toCamelCase(Model.name)}Id`
*/
foreignKey?: ModelColumns<R>;
};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export {};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { BaseModel } from '#src/models/BaseModel';
export type RelationKeys<T> = {
[K in keyof T]: T[K] extends BaseModel | BaseModel[] ? K : never;
}[keyof T];
export type ModelRelations<T> = Extract<RelationKeys<T>, string>;
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export {};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { BaseModel } from '#src/models/BaseModel';
export type Relation<M extends BaseModel | BaseModel[]> = M;
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export {};
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import type { HasOneOptions } from '#src/types/relations/HasOneOptions';
import type { HasManyOptions } from '#src/types/relations/HasManyOptions';
import type { BelongsToOptions } from '#src/types/relations/BelongsToOptions';
import type { BelongsToManyOptions } from '#src/types/relations/BelongsToManyOptions';
export type RelationOptions = HasOneOptions | HasManyOptions | BelongsToOptions | BelongsToManyOptions;
/**
* @athenna/database
*
* (c) João Lenon <lenon@athenna.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
export {};
+1
-1
{
"name": "@athenna/database",
"version": "5.36.0",
"version": "5.37.0",
"description": "The Athenna database handler for SQL/NoSQL.",

@@ -5,0 +5,0 @@ "license": "MIT",