This commit is contained in:
Tutur33
2023-11-24 22:35:41 +01:00
parent 3c0b507a93
commit 7644b2a0f7
45165 changed files with 4803356 additions and 3 deletions
+16
View File
@@ -0,0 +1,16 @@
declare module '@ioc:Adonis/Core/Application' {
import * as Orm from '@ioc:Adonis/Lucid/Orm';
import Migrator from '@ioc:Adonis/Lucid/Migrator';
import { DatabaseContract } from '@ioc:Adonis/Lucid/Database';
import { FactoryManagerContract } from '@ioc:Adonis/Lucid/Factory';
import { SchemaConstructorContract } from '@ioc:Adonis/Lucid/Schema';
import { SeederConstructorContract } from '@ioc:Adonis/Lucid/Seeder';
interface ContainerBindings {
'Adonis/Lucid/Database': DatabaseContract;
'Adonis/Lucid/Factory': FactoryManagerContract;
'Adonis/Lucid/Orm': typeof Orm;
'Adonis/Lucid/Migrator': typeof Migrator;
'Adonis/Lucid/Schema': SchemaConstructorContract;
'Adonis/Lucid/Seeder': SeederConstructorContract;
}
}
+8
View File
@@ -0,0 +1,8 @@
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
+727
View File
@@ -0,0 +1,727 @@
/// <reference path="querybuilder.d.ts" />
/// <reference types="@adonisjs/events/build/adonis-typings" />
/// <reference types="@adonisjs/profiler/build/adonis-typings/profiler" />
/// <reference types="node" />
/// <reference types="node" />
/// <reference types="@adonisjs/logger/build/adonis-typings/logger" />
declare module '@ioc:Adonis/Lucid/Database' {
import { Knex } from 'knex';
import { Pool } from 'tarn';
import { EventEmitter } from 'events';
import { ConnectionOptions } from 'tls';
import { EmitterContract } from '@ioc:Adonis/Core/Event';
import { MacroableConstructorContract } from 'macroable';
import { LoggerContract } from '@ioc:Adonis/Core/Logger';
import { HealthReportEntry } from '@ioc:Adonis/Core/HealthCheck';
import { LucidModel, ModelQueryBuilderContract } from '@ioc:Adonis/Lucid/Orm';
import { ProfilerRowContract, ProfilerContract } from '@ioc:Adonis/Core/Profiler';
/**
* Same as knex. Need to redefine, as knex doesn't export this
* type
*/
export type IsolationLevels = 'read uncommitted' | 'read committed' | 'snapshot' | 'repeatable read' | 'serializable';
/**
* Migration node returned by the migration source
* implementation
*/
export type FileNode<T> = {
absPath: string;
name: string;
getSource: () => T | Promise<T>;
};
/**
* Dialect specific methods
*/
export interface DialectContract {
readonly name: 'mssql' | 'mysql' | 'oracledb' | 'postgres' | 'redshift' | 'sqlite3' | 'better-sqlite3';
readonly dateTimeFormat: string;
readonly version?: string;
readonly supportsAdvisoryLocks: boolean;
readonly supportsViews: boolean;
readonly supportsTypes: boolean;
readonly supportsReturningStatement: boolean;
getAllTables(schemas?: string[]): Promise<string[]>;
dropAllTables(schemas?: string[]): Promise<void>;
getAllViews(schemas?: string[]): Promise<string[]>;
dropAllViews(schemas?: string[]): Promise<void>;
getAllTypes(schemas?: string[]): Promise<string[]>;
dropAllTypes(schemas?: string[]): Promise<void>;
truncate(table: string, cascade?: boolean): Promise<void>;
getAdvisoryLock(key: string | number, timeout?: number): Promise<boolean>;
releaseAdvisoryLock(key: string | number): Promise<boolean>;
}
/**
* Shape of the transaction function to create a new transaction
*/
export interface TransactionFn {
<T>(callback: (trx: TransactionClientContract) => Promise<T>, options?: {
isolationLevel?: IsolationLevels;
}): Promise<T>;
(options?: {
isolationLevel?: IsolationLevels;
}): Promise<TransactionClientContract>;
}
/**
* Shape of the query client, that is used to retrive instances
* of query builder
*/
export interface QueryClientContract {
emitter: EmitterContract;
/**
* Custom profiler to time queries
*/
profiler?: ProfilerRowContract | ProfilerContract;
/**
* Tells if client is a transaction client or not
*/
readonly isTransaction: boolean;
/**
* The database dialect in use
*/
readonly dialect: DialectContract;
/**
* The client mode in which it is execute queries
*/
readonly mode: 'dual' | 'write' | 'read';
/**
* The name of the connnection from which the client
* was originated
*/
readonly connectionName: string;
/**
* Is debug enabled on the connnection or not. Also opens up the API to
* disable debug for a given client
*/
debug: boolean;
/**
* Returns schema instance for the write client
*/
schema: Knex.SchemaBuilder;
/**
* Returns the read and write clients
*/
getReadClient(): Knex<any, any>;
getWriteClient(): Knex<any, any>;
/**
* Returns the query builder for a given model
*/
modelQuery<T extends LucidModel, Result = T>(model: T): ModelQueryBuilderContract<T, Result>;
/**
* Returns the knex query builder instance
*/
knexQuery(): Knex.QueryBuilder;
/**
* Returns the knex raw query builder instance
*/
knexRawQuery(sql: string, bindings?: RawQueryBindings): Knex.Raw;
/**
* Get new query builder instance for select, update and
* delete calls
*/
query<Result = any>(): DatabaseQueryBuilderContract<Result>;
/**
* Get new query builder instance inserts
*/
insertQuery<ReturnColumns = any>(): InsertQueryBuilderContract<ReturnColumns[]>;
/**
* Get raw query builder instance
*/
rawQuery<Result = any>(sql: string, bindings?: RawQueryBindings): RawQueryBuilderContract<Result>;
/**
* Returns instance of reference builder
*/
ref(reference: string): ReferenceBuilderContract;
/**
* Returns instance of raw builder
*/
raw(sql: string, bindings?: RawQueryBindings): RawBuilderContract;
/**
* Truncate a given table
*/
truncate(table: string, cascade?: boolean): Promise<void>;
/**
* Returns columns info for a given table
*/
columnsInfo(table: string): Promise<{
[column: string]: Knex.ColumnInfo;
}>;
columnsInfo(table: string, column: string): Promise<Knex.ColumnInfo>;
/**
* Get all tables of the database
*/
getAllTables(schemas?: string[]): Promise<string[]>;
/**
* Returns an array of all views names for one or many schemas
*/
getAllViews(schemas?: string[]): Promise<string[]>;
/**
* Returns an array of all types names
*/
getAllTypes(schemas?: string[]): Promise<string[]>;
/**
* Drop all tables inside database
*/
dropAllTables(schemas?: string[]): Promise<void>;
/**
* Drop all views inside the database
*/
dropAllViews(schemas?: string[]): Promise<void>;
/**
* Drop all types inside the database
*/
dropAllTypes(schemas?: string[]): Promise<void>;
/**
* Same as `query()`, but also selects the table for the query. The `from` method
* doesn't allow defining the return type and one must use `query` to define
* that.
*/
from: FromTable<DatabaseQueryBuilderContract<any>>;
/**
* Same as `insertQuery()`, but also selects the table for the query.
* The `table` method doesn't allow defining the return type and
* one must use `insertQuery` to define that.
*/
table: <ReturnColumns = any>(table: string) => InsertQueryBuilderContract<ReturnColumns[]>;
/**
* Get instance of transaction client
*/
transaction: TransactionFn;
/**
* Work with advisory locks
*/
getAdvisoryLock(key: string | number, timeout?: number): Promise<boolean>;
releaseAdvisoryLock(key: string | number): Promise<boolean>;
}
/**
* The shape of transaction client to run queries under a given
* transaction on a single connection
*/
export interface TransactionClientContract extends QueryClientContract, EventEmitter {
knexClient: Knex.Transaction;
/**
* Custom profiler to time queries
*/
profiler?: ProfilerRowContract;
/**
* Is transaction completed or not
*/
isCompleted: boolean;
/**
* Commit transaction
*/
commit(): Promise<void>;
/**
* Rollback transaction
*/
rollback(): Promise<void>;
/**
* Returns the read and write transaction clients
*/
getReadClient(): Knex.Transaction<any, any>;
getWriteClient(): Knex.Transaction<any, any>;
/**
* Transaction named events
*/
on(event: 'commit', handler: (client: this) => void): this;
on(event: 'rollback', handler: (client: this) => void): this;
once(event: 'commit', handler: (client: this) => void): this;
once(event: 'rollback', handler: (client: this) => void): this;
after(event: 'rollback' | 'commit', handler: () => void | Promise<void>): this;
}
/**
* Connection node used by majority of database
* clients
*/
type SharedConnectionNode = {
host?: string;
user?: string;
password?: string;
database?: string;
port?: number;
};
/**
* Shape of the report node for the database connection report
*/
export type ReportNode = {
connection: string;
message: string;
error: any;
};
/**
* Migrations config
*/
export type MigratorConfig = {
disableTransactions?: boolean;
paths?: string[];
tableName?: string;
disableRollbacksInProduction?: boolean;
naturalSort?: boolean;
};
/**
* Seeders config
*/
export type SeedersConfig = {
paths: string[];
};
/**
* Shared config options for all clients
*/
export type SharedConfigNode = {
useNullAsDefault?: boolean;
debug?: boolean;
asyncStackTraces?: boolean;
revision?: number;
healthCheck?: boolean;
migrations?: MigratorConfig;
seeders?: SeedersConfig;
wipe?: {
ignoreTables?: string[];
};
pool?: {
afterCreate?: (conn: any, done: any) => void;
min?: number;
max?: number;
acquireTimeoutMillis?: number;
createTimeoutMillis?: number;
idleTimeoutMillis?: number;
createRetryIntervalMillis?: number;
reapIntervalMillis?: number;
log?: (msg: string) => any;
validate?: (resource: any) => boolean;
propagateCreateError?: boolean;
};
};
/**
* The Sqlite specific config options are taken directly from the
* driver. https://github.com/mapbox/node-sqlite3/wiki/API#new-sqlite3databasefilename-mode-callback
*
* Knex forwards all config options to the driver directly. So feel
* free to define them (let us know, in case any options are missing)
*/
export type SqliteConfig = SharedConfigNode & {
client: 'sqlite' | 'sqlite3' | 'better-sqlite3';
connection: {
filename: string;
flags?: string[];
debug?: boolean;
mode?: any;
};
replicas?: never;
};
/**
* The MYSQL specific config options are taken directly from the
* driver. https://www.npmjs.com/package/mysql#connection-options
*
* Knex forwards all config options to the driver directly. So feel
* free to define them (let us know, in case any options are missing)
*/
type MysqlConnectionNode = {
socketPath?: string;
localAddress?: string;
charset?: string;
timezone?: string;
stringifyObjects?: boolean;
insecureAuth?: boolean;
typeCast?: boolean;
supportBigNumbers?: boolean;
bigNumberStrings?: boolean;
dateStrings?: boolean | string[];
flags?: string;
ssl?: any;
};
export type MysqlConfig = SharedConfigNode & {
client: 'mysql' | 'mysql2';
version?: string;
connection?: SharedConnectionNode & MysqlConnectionNode;
replicas?: {
write: {
connection: MysqlConfig['connection'];
pool?: MysqlConfig['pool'];
};
read: {
connection: MysqlConfig['connection'][];
pool?: MysqlConfig['pool'];
};
};
};
/**
* Config is picked from PostgreSQL driver, just refer their docs
* https://node-postgres.com/features/connecting#programmatic.
*
* - `returning` is added by knex and not driver.
* - `searchPath` is also added by Knex.
*
* Knex forwards all config options to the driver directly. So feel
* free to define them (let us know, in case any options are missing)
*/
type PostgresConnectionNode = {
ssl?: boolean | ConnectionOptions;
};
export type PostgreConfig = SharedConfigNode & {
client: 'pg' | 'postgres' | 'postgresql';
version?: string;
returning?: string;
connection?: string | (SharedConnectionNode & PostgresConnectionNode);
replicas?: {
write: {
connection: PostgreConfig['connection'];
pool?: PostgreConfig['pool'];
};
read: {
connection: PostgreConfig['connection'][];
pool?: PostgreConfig['pool'];
};
};
searchPath?: string[];
wrapIdentifier?: (value: string) => string;
};
/**
* Redshift uses `pg` driver. So config options are same as Postgres.
* https://node-postgres.com/features/connecting#programmatic.
*
* Knex forwards all config options to the driver directly. So feel
* free to define them (let us know, in case any options are missing)
*/
export type RedshiftConfig = PostgreConfig & {
client: 'redshift';
};
/**
* Please install `oracledb` driver and not the `oracle`. The later is
* deprecated. Config is only allowed for `oracledb`.
*
* Please refer to the driver configuration docs to learn more about the
* config values.
* https://oracle.github.io/node-oracledb/doc/api.html#oracledbproperties
*/
type OracleConnectionNode = {
autoCommit?: boolean;
connectionClass?: string;
edition?: string;
externalAuth?: boolean;
fetchArraySize?: number;
fetchAsBuffer?: any[];
lobPrefetchSize?: number;
maxRows?: number;
oracleClientVersion?: number;
connectString?: string;
};
export type OracleConfig = SharedConfigNode & {
client: 'oracledb';
connection?: SharedConnectionNode & OracleConnectionNode;
replicas?: {
write: {
connection: OracleConfig['connection'];
pool?: OracleConfig['pool'];
};
read: {
connection: OracleConfig['connection'][];
pool?: OracleConfig['pool'];
};
};
fetchAsString?: any[];
};
/**
* Config values are taken directly from the driver config.
* https://www.npmjs.com/package/mssql#config.
*
* Knex forwards all config options to the driver directly. So feel
* free to define them (let us know, in case any options are missing)
*/
type MssqlConnectionNode = {
server: string;
domain?: string;
connectionTimeout?: number;
requestTimeout?: number;
parseJSON?: boolean;
options?: {
encrypt?: boolean;
useUTC?: boolean;
tdsVersion?: string;
appName?: string;
abortTransactionOnError?: boolean;
trustedConnection?: boolean;
enableArithAbort?: boolean;
isolationLevel?: 'READ_UNCOMMITTED' | 'READ_COMMITTED' | 'REPEATABLE_READ' | 'SERIALIZABLE' | 'SNAPSHOT';
maxRetriesOnTransientErrors?: number;
multiSubnetFailover?: boolean;
packetSize?: number;
trustServerCertificate?: boolean;
};
};
export type MssqlConfig = SharedConfigNode & {
client: 'mssql';
version?: string;
connection?: SharedConnectionNode & MssqlConnectionNode;
replicas?: {
write: {
connection: MssqlConfig['connection'];
pool?: MssqlConfig['pool'];
};
read: {
connection: MssqlConfig['connection'][];
pool?: MssqlConfig['pool'];
};
};
};
/**
* Connection config must be the config from one of the
* available dialects
*/
export type ConnectionConfig = SqliteConfig | MysqlConfig | PostgreConfig | OracleConfig | RedshiftConfig | MssqlConfig;
/**
* Shape of config inside the database config file
*/
export type DatabaseConfig = {
connection: string;
connections: {
[key: string]: ConnectionConfig;
};
};
/**
* The shape of a connection within the connection manager
*/
export type ConnectionNode = {
name: string;
config: ConnectionConfig;
connection?: ConnectionContract;
state: 'registered' | 'migrating' | 'open' | 'closing' | 'closed';
};
/**
* Connection manager to manage one or more database
* connections.
*/
export interface ConnectionManagerContract {
/**
* List of registered connection. You must check the connection state
* to understand, if it is connected or not
*/
connections: Map<string, ConnectionNode>;
/**
* Add a new connection to the list of managed connection. You must call
* connect separately to instantiate a connection instance
*/
add(connectionName: string, config: ConnectionConfig): void;
/**
* Instantiate a connection. It is a noop, when connection for the given
* name is already instantiated
*/
connect(connectionName: string): void;
/**
* Get connection node
*/
get(connectionName: string): ConnectionNode | undefined;
/**
* Find if a connection name is managed by the manager or not
*/
has(connectionName: string): boolean;
/**
* Patch the existing connection config. This triggers the disconnect on the
* old connection
*/
patch(connectionName: string, config: ConnectionConfig): void;
/**
* Find if a managed connection is instantiated or not
*/
isConnected(connectionName: string): boolean;
/**
* Close a given connection. This is also kill the underlying knex connection
* pool
*/
close(connectionName: string, release?: boolean): Promise<void>;
/**
* Close all managed connections
*/
closeAll(release?: boolean): Promise<void>;
/**
* Release a given connection. Releasing a connection means, you will have to
* re-add it using the `add` method
*/
release(connectionName: string): Promise<void>;
/**
* Returns the health check report for registered connections
*/
report(): Promise<HealthReportEntry & {
meta: ReportNode[];
}>;
}
/**
* Connection represents a single knex instance with inbuilt
* pooling capabilities.
*/
export interface ConnectionContract extends EventEmitter {
client?: Knex;
readClient?: Knex;
readonly dialectName: 'mssql' | 'mysql' | 'mysql2' | 'oracledb' | 'postgres' | 'redshift' | 'sqlite3';
/**
* Property to find if explicit read/write is enabled
*/
readonly hasReadWriteReplicas: boolean;
/**
* Read/write connection pools
*/
pool: null | Pool<any>;
readPool: null | Pool<any>;
/**
* Name of the connection
*/
readonly name: string;
/**
* Find if connection is ready or not
*/
readonly ready: boolean;
/**
* Untouched config
*/
config: ConnectionConfig;
/**
* List of emitted events
*/
on(event: 'connect', callback: (connection: ConnectionContract) => void): this;
on(event: 'error', callback: (error: Error, connection: ConnectionContract) => void): this;
on(event: 'disconnect', callback: (connection: ConnectionContract) => void): this;
on(event: 'disconnect:error', callback: (error: Error, connection: ConnectionContract) => void): this;
/**
* Make knex connection
*/
connect(): void;
/**
* Disconnect knex
*/
disconnect(): Promise<void>;
/**
* Returns the connection report
*/
getReport(): Promise<ReportNode>;
}
/**
* Options when retrieving new query client from the database
* query builder
*/
export type DatabaseClientOptions = Partial<{
mode: 'read' | 'write';
profiler: ProfilerRowContract | ProfilerContract;
}>;
/**
* Shape of the data emitted by the `db:query event`
*/
export type DbQueryEventNode = {
connection: string;
model?: string;
ddl?: boolean;
duration?: [number, number];
method: string;
sql: string;
bindings?: any[];
inTransaction?: boolean;
};
/**
* Database contract serves as the main API to interact with multiple
* database connections
*/
export interface DatabaseContract {
Database: MacroableConstructorContract<DatabaseContract> & {
new (config: DatabaseConfig, logger: LoggerContract, profiler: ProfilerContract, emitter: EmitterContract): DatabaseContract;
};
DatabaseQueryBuilder: MacroableConstructorContract<DatabaseQueryBuilderContract>;
InsertQueryBuilder: MacroableConstructorContract<InsertQueryBuilderContract>;
ModelQueryBuilder: MacroableConstructorContract<ModelQueryBuilderContract<any, any>>;
SimplePaginator: {
namingStrategy: {
paginationMetaKeys(): SimplePaginatorMetaKeys;
};
new <Row>(total: number, perPage: number, currentPage: number, ...rows: Row[]): SimplePaginatorContract<Row>;
};
hasHealthChecksEnabled: boolean;
/**
* Pretty print query logs
*/
prettyPrint: (queryLog: DbQueryEventNode) => void;
/**
* Name of the primary connection defined inside `config/database.ts`
* file
*/
primaryConnectionName: string;
/**
* Reference to the connection manager
*/
manager: ConnectionManagerContract;
/**
* Returns the raw connection instance
*/
getRawConnection: ConnectionManagerContract['get'];
/**
* Get query client for a given connection. Optionally one can also define
* the mode of the connection and profiler row
*/
connection(connectionName?: string, options?: DatabaseClientOptions): QueryClientContract;
/**
* Returns the knex query builder instance
*/
knexQuery(): Knex.QueryBuilder;
/**
* Returns the knex raw query builder instance
*/
knexRawQuery(sql: string, bindings?: RawQueryBindings): Knex.Raw;
/**
* Returns the query builder for a given model
*/
modelQuery<T extends LucidModel, Result = T>(model: T, options?: DatabaseClientOptions): ModelQueryBuilderContract<T, Result>;
/**
* Get query builder instance for a given connection.
*/
query<Result = any>(options?: DatabaseClientOptions): DatabaseQueryBuilderContract<Result>;
/**
* Get insert query builder instance for a given connection.
*/
insertQuery<ReturnColumns = any>(options?: DatabaseClientOptions): InsertQueryBuilderContract<ReturnColumns[]>;
/**
* Get raw query builder instance
*/
rawQuery<Result = any>(sql: string, bindings?: RawQueryBindings, options?: DatabaseClientOptions): RawQueryBuilderContract<Result>;
/**
* Returns instance of reference builder
*/
ref(reference: string): ReferenceBuilderContract;
/**
* Returns instance of raw builder
*/
raw(sql: string, bindings?: RawQueryBindings): RawBuilderContract;
/**
* Selects a table on the default connection by instantiating a new query
* builder instance. This method provides no control over the client
* mode and one must use `query` for that
*/
from: QueryClientContract['from'];
/**
* Selects a table on the default connection by instantiating a new query
* builder instance. This method provides no control over the client
* mode and one must use `insertQuery` for that
*/
table: QueryClientContract['table'];
/**
* Start a new transaction
*/
transaction: TransactionFn;
/**
* Returns the health check report for registered connections
*/
report(): Promise<HealthReportEntry & {
meta: ReportNode[];
}>;
/**
* Begin a new global transaction. Multiple calls to this
* method is a noop
*/
beginGlobalTransaction(connectionName?: string, options?: Exclude<DatabaseClientOptions, 'mode'>): Promise<TransactionClientContract>;
/**
* Commit an existing global transaction
*/
commitGlobalTransaction(connectionName?: string): Promise<void>;
/**
* Rollback an existing global transaction
*/
rollbackGlobalTransaction(connectionName?: string): Promise<void>;
}
const Database: DatabaseContract;
export default Database;
}
+9
View File
@@ -0,0 +1,9 @@
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/// <reference path="./querybuilder.ts" />
+6
View File
@@ -0,0 +1,6 @@
declare module '@ioc:Adonis/Core/Event' {
import { DbQueryEventNode } from '@ioc:Adonis/Lucid/Database';
interface EventsList {
'db:query': DbQueryEventNode;
}
}
+8
View File
@@ -0,0 +1,8 @@
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
+299
View File
@@ -0,0 +1,299 @@
declare module '@ioc:Adonis/Lucid/Factory' {
import { faker } from '@faker-js/faker';
import { OneOrMany, QueryClientContract, TransactionClientContract } from '@ioc:Adonis/Lucid/Database';
import { LucidRow, LucidModel, ModelAttributes, ModelAdapterOptions, RelationshipsContract, ExtractModelRelations, ModelObject } from '@ioc:Adonis/Lucid/Orm';
/**
* ------------------------------------------------------
* Helpers
* ------------------------------------------------------
*/
/**
* Extracts the attributes accepted by the lucid model set on a
* factory
*/
export type ExtractFactoryAttributes<T extends FactoryModelContract<LucidModel>> = Partial<ModelAttributes<InstanceType<T['model']>>>;
/**
* ------------------------------------------------------
* Callbacks
* ------------------------------------------------------
*/
/**
* Function to return the model attributes.
*/
export type DefineCallback<Model extends LucidModel> = (ctx: FactoryContextContract) => Promise<Partial<ModelAttributes<InstanceType<Model>>>> | Partial<ModelAttributes<InstanceType<Model>>>;
/**
* Function to generate custom stub ids
*/
export type StubIdCallback = (counter: number, model: LucidRow) => any;
/**
* Function to initiate a model instance. It will receive the
* attributes returned by the `define` method
*/
export type NewUpCallback<T extends FactoryModelContract<LucidModel>> = (attributes: ExtractFactoryAttributes<T>, ctx: FactoryContextContract, model: T['model'], builder: FactoryBuilderContract<T>) => InstanceType<T['model']>;
/**
* Function to merge attributes defined during runtime
*/
export type MergeCallback<T extends FactoryModelContract<LucidModel>> = (row: InstanceType<T['model']>, attributes: ExtractFactoryAttributes<T>, ctx: FactoryContextContract, builder: FactoryBuilderContract<T>) => void;
/**
* Callback to define a new model state
*/
export type StateCallback<Model extends LucidModel> = (row: InstanceType<Model>, ctx: FactoryContextContract, builder: FactoryBuilderContract<FactoryModelContract<Model>>) => any | Promise<any>;
/**
* ------------------------------------------------------
* Hooks
* ------------------------------------------------------
*/
/**
* List of events for which a factory will trigger hooks
*/
export type EventsList = 'makeStubbed' | 'create' | 'make';
/**
* Shape of hooks handler
*/
export type HooksHandler<Model extends FactoryModelContract<LucidModel>> = (builder: FactoryBuilderContract<Model>, row: InstanceType<Model['model']>, ctx: FactoryContextContract) => void | Promise<void>;
/**
* ------------------------------------------------------
* Runtime context
* ------------------------------------------------------
*/
/**
* The runtime context of the factory builder. A new state is constructed
* for each `create/make` operation and passed down to relationships
* as well.
*/
export interface FactoryContextContract {
faker: typeof faker;
isStubbed: boolean;
$trx: TransactionClientContract | undefined;
}
/**
* ------------------------------------------------------
* Relationships
* ------------------------------------------------------
*/
/**
* Callback accepted by the `with` method and relationships
* `create` and `make` methods
*/
export type RelationCallback = (builder: FactoryBuilderContract<FactoryModelContract<LucidModel>>) => void;
/**
* Shape of the factory relationships. To keep relationships slim, we will have
* a common interface for relationships vs fine tuning API for each type of
* relationship
*/
export interface FactoryRelationContract {
parent: LucidRow;
/**
* Reference to the Lucid model relationship
*/
relation: RelationshipsContract;
/**
* Merge attributes with the relationship and its children
*/
merge(attributes: any): this;
/**
* Define custom pivot attributes for many to many
* relationship
*/
pivotAttributes?(attributes: ModelObject | ModelObject[]): this;
/**
* Pass context to the relationship. Must be done everytime, so that
* relationships uses the same transaction as the parent model
*/
useCtx(ctx: FactoryContextContract): this;
/**
* Create and persist
*/
create(parent: LucidRow, callback?: RelationCallback, count?: number): Promise<void>;
/**
* Create and stub
*/
make(parent: LucidRow, callback?: RelationCallback, count?: number): Promise<void>;
}
/**
* ------------------------------------------------------
* Runtime builder
* ------------------------------------------------------
*/
/**
* Factory builder uses the factory model to create/make
* instances of lucid models
*/
export interface FactoryBuilderContract<FactoryModel extends FactoryModelContract<LucidModel>> {
/**
* Reference to the factory
*/
factory: FactoryModel;
/**
* Define custom database connection
*/
connection(connection: string): this;
/**
* Define custom query client
*/
client(client: QueryClientContract): this;
/**
* Apply pre-defined state
*/
apply<K extends keyof FactoryModel['states']>(...states: K[]): this;
/**
* Create/make relationships for explicitly defined related factories
*/
with<K extends keyof FactoryModel['relations']>(relation: K, count?: number, callback?: (
/**
* Receives the explicitly defined factory
*/
builder: FactoryModel['relations'][K] extends () => FactoryBuilderContract<any> ? ReturnType<FactoryModel['relations'][K]> & {
parent: InstanceType<FactoryModel['model']>;
} : never) => void): this;
/**
* Define pivot attributes when persisting a many to many
* relationship. Results in a noop, when not called
* for a many to many relationship
*/
pivotAttributes(attributes: ModelObject | ModelObject[]): this;
/**
* Merge custom set of attributes. They are passed to the merge method of
* the model factory
*
* For `createMany` and `makeMany`, you can pass an array of attributes mapped
* according to the array index.
*/
merge(attributes: OneOrMany<ExtractFactoryAttributes<FactoryModel>>): this;
/**
* Merge custom set of attributes with the correct factory builder
* model and all of its relationships as well
*/
mergeRecursive(attributes: any): this;
/**
* Define custom runtime context. This method is usually called by
* the relationships to ensure a single context is used by the
* parent and relationship factories.
*
* Do not define a custom context, unless you know what you are really
* doing.
*/
useCtx(ctx: FactoryContextContract): this;
/**
* Tap into the persistence layer of factory builder. Allows one
* to modify the model instance just before it is persisted
* to the database
*/
tap(callback: (row: InstanceType<FactoryModel['model']>, ctx: FactoryContextContract, builder: this) => void): this;
/**
* Make model instance without persitance. The make method
* doesn't process relationships
*/
make(): Promise<InstanceType<FactoryModel['model']>>;
/**
* Create model instance and stub out the persistance
* mechanism
*/
makeStubbed(): Promise<InstanceType<FactoryModel['model']>>;
/**
* Create and persist model instance
*/
create(): Promise<InstanceType<FactoryModel['model']>>;
/**
* Make model instance without persitance. The makeMany method
* doesn't process relationships
*/
makeMany(count: number): Promise<InstanceType<FactoryModel['model']>[]>;
/**
* Create one or more model instances and stub
* out the persistance mechanism.
*/
makeStubbedMany(count: number): Promise<InstanceType<FactoryModel['model']>[]>;
/**
* Create and persist more than one model instance
*/
createMany(count: number): Promise<InstanceType<FactoryModel['model']>[]>;
}
/**
* Query contract that initiates the factory builder. Since the factory builder
* API surface is small, we also proxy all of it's methods for a nicer DX
*/
export interface FactoryBuilderQueryContract<FactoryModel extends FactoryModelContract<LucidModel>> extends FactoryBuilderContract<FactoryModel> {
query(options?: ModelAdapterOptions, viaRelation?: FactoryRelationContract): FactoryBuilderContract<FactoryModel>;
}
/**
* ------------------------------------------------------
* Factory model
* ------------------------------------------------------
*/
/**
* Factory model exposes the API to defined a model factory with states
* and relationships
*/
export interface FactoryModelContract<Model extends LucidModel> {
/**
* Reference to the underlying lucid model used by the factory
* model
*/
model: Model;
/**
* Mainly for types support. Not used at runtime to derive any
* logic. Sorry, at times have to hack into typescript to
* get the desired output. :)
*/
states: unknown;
relations: unknown;
/**
* Optionally define a custom method to instantiate the model
* instance and manage merging attributes
*/
newUp(callback: NewUpCallback<this>): this;
merge(callback: MergeCallback<this>): this;
/**
* Define custom state for the factory. When executing the factory,
* you can apply the pre-defined states
*/
state<K extends string>(state: K, callback: StateCallback<Model>): this & {
states: {
[P in K]: StateCallback<Model>;
};
};
/**
* Define a relationship on another factory
*/
relation<K extends ExtractModelRelations<InstanceType<Model>>, Relation>(relation: K, callback: Relation): this & {
relations: {
[P in K]: Relation;
};
};
/**
* Define before hooks. Only `create` event is invoked
* during the before lifecycle
*/
before(event: Exclude<EventsList, 'make'>, handler: HooksHandler<this>): this;
/**
* Define after hooks.
*/
after(event: EventsList, handler: HooksHandler<this>): this;
/**
* Build model factory. This method returns the factory builder, which can be used to
* execute model queries
*/
build(): FactoryBuilderQueryContract<this>;
}
/**
* ------------------------------------------------------
* Manager to register new factories
* ------------------------------------------------------
*/
/**
* Factory manager to define new factories
*/
export interface FactoryManagerContract {
/**
* Define a custom factory
*/
define<Model extends LucidModel>(model: Model, callback: DefineCallback<Model>): FactoryModelContract<Model>;
/**
* Define a custom callback to generate stub ids
*/
stubId(callback: StubIdCallback): void;
}
const Factory: FactoryManagerContract;
export default Factory;
}
+8
View File
@@ -0,0 +1,8 @@
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
+13
View File
@@ -0,0 +1,13 @@
/// <reference path="database.d.ts" />
/// <reference path="events.d.ts" />
/// <reference path="querybuilder.d.ts" />
/// <reference path="model.d.ts" />
/// <reference path="orm.d.ts" />
/// <reference path="schema.d.ts" />
/// <reference path="migrator.d.ts" />
/// <reference path="relations.d.ts" />
/// <reference path="factory.d.ts" />
/// <reference path="validator.d.ts" />
/// <reference path="seeder.d.ts" />
/// <reference path="container.d.ts" />
/// <reference path="test-utils.d.ts" />
+21
View File
@@ -0,0 +1,21 @@
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/// <reference path="./database.ts" />
/// <reference path="./events.ts" />
/// <reference path="./querybuilder.ts" />
/// <reference path="./model.ts" />
/// <reference path="./orm.ts" />
/// <reference path="./schema.ts" />
/// <reference path="./migrator.ts" />
/// <reference path="./relations.ts" />
/// <reference path="./factory.ts" />
/// <reference path="./validator.ts" />
/// <reference path="./seeder.ts" />
/// <reference path="./container.ts" />
/// <reference path="./test-utils.ts" />
+77
View File
@@ -0,0 +1,77 @@
/// <reference types="node" />
/// <reference types="@adonisjs/application/build/adonis-typings" />
declare module '@ioc:Adonis/Lucid/Migrator' {
import { EventEmitter } from 'events';
import { ApplicationContract } from '@ioc:Adonis/Core/Application';
import { FileNode, DatabaseContract } from '@ioc:Adonis/Lucid/Database';
/**
* Options accepted by migrator constructor
*/
export type MigratorOptions = {
direction: 'up';
connectionName?: string;
dryRun?: boolean;
disableLocks?: boolean;
} | {
direction: 'down';
batch?: number;
connectionName?: string;
dryRun?: boolean;
disableLocks?: boolean;
};
/**
* Shape of migrated file within migrator
*/
export type MigratedFileNode = {
status: 'completed' | 'error' | 'pending';
queries: string[];
file: FileNode<unknown>;
batch: number;
};
/**
* Shape of migrated file within migrator
*/
export type MigrationListNode = {
name: string;
status: 'pending' | 'migrated' | 'corrupt';
batch?: number;
migrationTime?: Date;
};
/**
* Shape of the migrator
*/
export interface MigratorContract extends EventEmitter {
dryRun: boolean;
disableLocks: boolean;
version: number;
direction: 'up' | 'down';
status: 'completed' | 'skipped' | 'pending' | 'error';
error: null | Error;
migratedFiles: {
[file: string]: MigratedFileNode;
};
run(): Promise<void>;
getList(): Promise<MigrationListNode[]>;
close(): Promise<void>;
on(event: 'start', callback: () => void): this;
on(event: 'end', callback: () => void): this;
on(event: 'acquire:lock', callback: () => void): this;
on(event: 'release:lock', callback: () => void): this;
on(event: 'create:schema:table', callback: () => void): this;
on(event: 'create:schema_versions:table', callback: () => void): this;
on(event: 'upgrade:version', callback: (payload: {
from: number;
to: number;
}) => void): this;
on(event: 'migration:start', callback: (file: MigratedFileNode) => void): this;
on(event: 'migration:completed', callback: (file: MigratedFileNode) => void): this;
on(event: 'migration:error', callback: (file: MigratedFileNode) => void): this;
}
/**
* Migrator class constructor
*/
const Migrator: {
new (db: DatabaseContract, app: ApplicationContract, options: MigratorOptions): MigratorContract;
};
export default Migrator;
}
+8
View File
@@ -0,0 +1,8 @@
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
+801
View File
@@ -0,0 +1,801 @@
/// <reference types="@adonisjs/profiler/build/adonis-typings/profiler" />
declare module '@ioc:Adonis/Lucid/Orm' {
import { DateTime } from 'luxon';
import { Hooks } from '@poppinss/hooks';
import { ProfilerContract, ProfilerRowContract } from '@ioc:Adonis/Core/Profiler';
import { Update, Counter, OneOrMany, Aggregate, Returning, DialectContract, ChainableContract, QueryClientContract, SimplePaginatorMetaKeys, SimplePaginatorContract, TransactionClientContract, ExcutableQueryBuilderContract } from '@ioc:Adonis/Lucid/Database';
/**
* ------------------------------------------------------
* Helpers
* ------------------------------------------------------
*/
/**
* Same as [[Parameters]] but omits the first parameter
*/
type OmitFirst<T extends (...args: any) => any> = T extends (x: any, ...args: infer P) => any ? P : never;
/**
* Same as [[Pick]] but picks by value and not the key
*/
type PickProperties<T, P> = Pick<T, {
[K in keyof T]: T[K] extends P ? K : never;
}[keyof T]>;
/**
* Decorator function
*/
type DecoratorFn = (target: any, property: any) => void;
/**
* Typed decorator
*/
type TypedDecorator<PropType> = <TKey extends string, TTarget extends {
[K in TKey]: PropType;
}>(target: TTarget, property: TKey) => void;
/**
* Typed decorator that also represents an optional property
*/
type OptionalTypedDecorator<PropType> = <TKey extends string, TTarget extends {
[K in TKey]?: PropType;
}>(target: TTarget, property: TKey) => void;
/**
* A complex type that filters out functions and relationships from the
* model attributes and consider all other properties as database
* columns. Alternatively, the user can self define a `$columns`
* property.
*/
type ModelAttributes<Model extends LucidRow> = Model['$columns'] extends undefined ? {
[Filtered in {
[P in keyof Model]: P extends keyof LucidRow | 'serializeExtras' ? never : Model[P] extends Function | ModelRelationTypes ? never : P;
}[keyof Model]]: Model[Filtered];
} : Model['$columns'];
/**
* Extract the query scopes of a model
*/
type ExtractScopes<Model> = {
[Scope in keyof PickProperties<Model, QueryScope<QueryScopeCallback>>]: (...args: Model[Scope] extends QueryScopeCallback ? OmitFirst<Model[Scope]> : never) => ExtractScopes<Model>;
};
/**
* Reusable interface to define an object.
*/
interface ModelObject {
[key: string]: any;
}
/**
* Shape of cache node to keep getters optimized
*/
type CacheNode = {
original: any;
resolved: any;
getter: (value: any) => any;
};
/**
* Shape for cherry picking fields
*/
type CherryPickFields = string[] | {
pick?: string[];
omit?: string[];
};
/**
* Shape for cherry picking fields on nested relationships
*/
type CherryPick = {
fields?: CherryPickFields;
relations?: {
[relation: string]: CherryPick;
};
};
/**
* List of events for which a model will trigger hooks
*/
type EventsList = 'save' | 'create' | 'update' | 'delete' | 'fetch' | 'find' | 'paginate';
type HooksHandler<Data, Event extends EventsList> = ((data: Data, event: Event) => Promise<void> | void) | string;
/**
* ------------------------------------------------------
* Query Scope
* ------------------------------------------------------
*/
/**
* Generic query scope callback
*/
type QueryScopeCallback<Model extends LucidModel = LucidModel> = (query: ModelQueryBuilderContract<Model>, ...args: any[]) => void;
/**
* Query scope
*/
type QueryScope<Scope extends QueryScopeCallback> = Scope & {
readonly isQueryScope: true;
};
/**
* A function to mark a method as query scope
*/
type ScopeFn = <Model extends LucidModel, Scope extends QueryScopeCallback = QueryScopeCallback<Model>>(callback: Scope) => QueryScope<Scope>;
/**
* ------------------------------------------------------
* Decorators and Options
* ------------------------------------------------------
*/
/**
* Options for defining a column
*/
type ColumnOptions = {
columnName: string;
serializeAs: string | null;
isPrimary: boolean;
meta?: any;
/**
* Invoked before serializing process happens
*/
serialize?: (value: any, attribute: string, model: LucidRow) => any;
/**
* Invoked before create or update happens
*/
prepare?: (value: any, attribute: string, model: LucidRow) => any;
/**
* Invoked when row is fetched from the database
*/
consume?: (value: any, attribute: string, model: LucidRow) => any;
};
/**
* Shape of column options after they have set on the model
*/
type ModelColumnOptions = ColumnOptions & {
hasGetter: boolean;
hasSetter: boolean;
};
/**
* Represents a computed property on the model
*/
type ComputedOptions = {
serializeAs: string | null;
meta?: any;
};
/**
* Options accepted by the Model.$addRelation method
*/
type ModelRelationOptions = RelationOptions<ModelRelations> | ManyToManyRelationOptions<ModelRelations> | ThroughRelationOptions<ModelRelations>;
/**
* Signature for column decorator function
*/
type ColumnDecorator = (options?: Partial<ColumnOptions>) => DecoratorFn;
/**
* Signature for computed decorator function
*/
type ComputedDecorator = (options?: Partial<ComputedOptions>) => DecoratorFn;
/**
* Decorator for defining date columns
*/
type DateColumnDecorator = (options?: Partial<ColumnOptions & {
autoCreate: boolean;
autoUpdate: boolean;
}>) => OptionalTypedDecorator<DateTime | null>;
/**
* Decorator for defining date time columns. It is same as
* date column as of now
*/
type DateTimeColumnDecorator = DateColumnDecorator;
/**
* Decorator for defining hooks. The generics enforces that
* decorator is used on static properties only
*/
type HooksDecorator = () => <Model extends LucidModel>(target: Model, property: string) => void;
/**
* ------------------------------------------------------
* Model Options
* ------------------------------------------------------
*/
/**
* Model options to be used when making queries
*/
type ModelOptions = {
connection?: string;
profiler?: ProfilerContract | ProfilerRowContract;
};
/**
* Adapter also accepts a client directly
*/
type ModelAdapterOptions = ModelOptions & {
client?: QueryClientContract;
};
/**
* Options used by the method that internally invokes
* the merge method.
] */
type ModelAssignOptions = ModelAdapterOptions & {
allowExtraProperties?: boolean;
};
/**
* Preload function on a model instance
*/
interface LucidRowPreload<Model extends LucidRow> extends Preload<Model, Promise<void>> {
(callback: (preloader: PreloaderContract<Model>) => void): Promise<void>;
}
interface LucidRowAggregate<Model extends LucidRow> extends Preload<Model, Promise<void>> {
(callback: (preloader: PreloaderContract<Model>) => void): Promise<void>;
}
/**
* An extension of the simple paginator with support for serializing models
*/
interface ModelPaginatorContract<Result extends LucidRow> extends Omit<SimplePaginatorContract<Result>, 'toJSON'> {
serialize(cherryPick?: CherryPick): {
meta: any;
data: ModelObject[];
};
toJSON(): {
meta: any;
data: ModelObject[];
};
}
/**
* Lazy load aggregates for a given model instance
*/
interface LazyLoadAggregatesContract<Model extends LucidRow> extends Promise<void> {
loadAggregate: WithAggregate<Model, this>;
loadCount: WithCount<Model, this>;
exec(): Promise<void>;
}
/**
* ------------------------------------------------------
* Model Query Builder
* ------------------------------------------------------
*/
/**
* Model query builder will have extras methods on top of the Database query builder
*/
interface ModelQueryBuilderContract<Model extends LucidModel, Result = InstanceType<Model>> extends ChainableContract, ExcutableQueryBuilderContract<Result[]> {
model: Model;
returning: Returning<this>;
/**
* Define a callback to transform a row
*/
rowTransformer(callback: (row: LucidRow) => void): this;
/**
* Define a custom preloader for the current query
*/
usePreloader(preloader: PreloaderContract<LucidRow>): this;
/**
* Whether or not the query is a child query generated for `.where`
* callbacks
*/
isChildQuery: boolean;
/**
* Alias for the @withScopes method
*/
apply<Scopes extends ExtractScopes<Model>>(callback: (scopes: Scopes) => void): this;
/**
* Apply model query scopes on the query bulder
*/
withScopes<Scopes extends ExtractScopes<Model>>(callback: (scopes: Scopes) => void): this;
/**
* A copy of client options.
*/
readonly clientOptions: ModelAdapterOptions;
/**
* Reference to query client used for making queries
*/
client: QueryClientContract;
/**
* Clone query builder instance
*/
clone<ClonedResult = Result>(): ModelQueryBuilderContract<Model, ClonedResult>;
/**
* A custom set of sideloaded properties defined on the query
* builder, this will be passed to the model instance created
* by the query builder
*/
sideload(value: ModelObject): this;
/**
* Execute and get first result
*/
first(): Promise<Result | null>;
/**
* Return the first matching row or fail
*/
firstOrFail(): Promise<Result>;
/**
* Perform delete operation
*/
del(returning?: OneOrMany<string>): ModelQueryBuilderContract<Model, any>;
delete(returning?: OneOrMany<string>): ModelQueryBuilderContract<Model, any>;
/**
* A shorthand to define limit and offset based upon the
* current page
*/
forPage(page: number, perPage?: number): this;
/**
* Execute query with pagination
*/
paginate(page: number, perPage?: number): Promise<Result extends LucidRow ? ModelPaginatorContract<Result> : SimplePaginatorContract<Result>>;
/**
* Mutations (update and increment can be one query aswell)
*/
update: Update<ModelQueryBuilderContract<Model, any>>;
increment: Counter<ModelQueryBuilderContract<Model, any>>;
decrement: Counter<ModelQueryBuilderContract<Model, any>>;
/**
* Fetch relationship count
*/
withCount: WithCount<InstanceType<Model>, this>;
/**
* Fetch aggregate value for a given relationship
*/
withAggregate: WithAggregate<InstanceType<Model>, this>;
/**
* Add where constraint using the relationship
*/
has: Has<InstanceType<Model>, this>;
orHas: Has<InstanceType<Model>, this>;
andHas: Has<InstanceType<Model>, this>;
doesntHave: Has<InstanceType<Model>, this>;
orDoesntHave: Has<InstanceType<Model>, this>;
andDoesntHave: Has<InstanceType<Model>, this>;
/**
* Add where constraint using the relationship with a custom callback
*/
whereHas: WhereHas<InstanceType<Model>, this>;
orWhereHas: WhereHas<InstanceType<Model>, this>;
andWhereHas: WhereHas<InstanceType<Model>, this>;
whereDoesntHave: WhereHas<InstanceType<Model>, this>;
orWhereDoesntHave: WhereHas<InstanceType<Model>, this>;
andWhereDoesntHave: WhereHas<InstanceType<Model>, this>;
/**
* Define relationships to be preloaded
*/
preload: Preload<InstanceType<Model>, this>;
/**
* Aggregates
*/
count: Aggregate<this>;
countDistinct: Aggregate<this>;
min: Aggregate<this>;
max: Aggregate<this>;
sum: Aggregate<this>;
sumDistinct: Aggregate<this>;
avg: Aggregate<this>;
avgDistinct: Aggregate<this>;
/**
* Executes the callback when dialect matches one of the mentioned
* dialects
*/
ifDialect(dialect: DialectContract['name'] | DialectContract['name'][], matchCallback: (query: this) => any, noMatchCallback?: (query: this) => any): this;
/**
* Executes the callback when dialect matches doesn't all the mentioned
* dialects
*/
unlessDialect(dialect: DialectContract['name'] | DialectContract['name'][], matchCallback: (query: this) => any, noMatchCallback?: (query: this) => any): this;
/**
* Get rows back as a plain javascript object and not an array
* of model instances
*/
pojo<T>(): ModelQueryBuilderContract<Model, T>;
}
/**
* Shape of model keys
*/
interface ModelKeysContract {
add(key: string, value: string): void;
get(key: string, defaultValue: string): string;
get(key: string, defaultValue?: string): string | undefined;
resolve(key: string): string;
all(): ModelObject;
}
/**
* ------------------------------------------------------
* Shape of Model instance
* ------------------------------------------------------
*/
/**
* Shape of the model instance. We prefix the properties with a `$` to
* differentiate between special properties provided by the base
* model but with exception to `save`, `delete`, `fill`, `merge`
* and `toJSON`.
*
* @note: Since the interface name appears next to the inherited model
* methods, we have to choose a succinct name
*/
interface LucidRow {
$attributes: ModelObject;
$extras: ModelObject;
$original: ModelObject;
$preloaded: {
[relation: string]: LucidRow | LucidRow[];
};
/**
* Columns is a property to get type information for model
* attributes. This must be declared by the end user
*/
$columns: undefined;
$sideloaded: ModelObject;
$primaryKeyValue?: number | string;
$isPersisted: boolean;
$isNew: boolean;
$isLocal: boolean;
$dirty: ModelObject;
$isDirty: boolean;
$isDeleted: boolean;
$options?: ModelOptions;
$trx?: TransactionClientContract;
$setOptionsAndTrx(options?: ModelAdapterOptions): void;
useTransaction(trx: TransactionClientContract): this;
useConnection(connection: string): this;
/**
* Gives an option to the end user to define constraints for update, insert
* and delete queries. Since the query builder for these queries aren't
* exposed to the end user, this method opens up the API to build
* custom queries.
*/
$getQueryFor(action: 'insert', client: QueryClientContract): ReturnType<QueryClientContract['insertQuery']>;
$getQueryFor(action: 'update' | 'delete' | 'refresh', client: QueryClientContract): ModelQueryBuilderContract<LucidModel>;
/**
* Read/write attributes. Following methods are intentionally loosely typed,
* so that one can bypass the public facing API and type checking for
* advanced use cases
*/
$setAttribute(key: string, value: any): void;
$getAttribute(key: string): any;
$getAttributeFromCache(key: string, callback: CacheNode['getter']): any;
/**
* Read/write realtionships. Following methods are intentionally loosely typed,
* so that one can bypass the public facing API and type checking for
* advanced use cases
*/
$hasRelated(key: string): boolean;
$setRelated(key: string, result: OneOrMany<LucidRow> | null): void;
$pushRelated(key: string, result: OneOrMany<LucidRow> | null): void;
$getRelated(key: string, defaultValue?: any): OneOrMany<LucidRow> | undefined | null;
/**
* Consume the adapter result and hydrate the model
*/
$consumeAdapterResult(adapterResult: ModelObject, sideloadAttributes?: ModelObject): void;
$hydrateOriginals(): void;
fill(value: Partial<ModelAttributes<this>>, allowExtraProperties?: boolean): this;
merge(value: Partial<ModelAttributes<this>>, allowExtraProperties?: boolean): this;
/**
* Enable force update even when no attributes
* are dirty
*/
enableForceUpdate(): this;
/**
* Actions to perform on the instance
*/
save(): Promise<this>;
delete(): Promise<void>;
refresh(): Promise<this>;
/**
* Load relationships onto the instance
*/
load: LucidRowPreload<this>;
/**
* Alias for "load"
* @deprecated
*/
preload: LucidRowPreload<this>;
/**
* Load aggregates
*/
loadAggregate: <Self extends this, Name extends ExtractModelRelations<Self>, RelatedBuilder = Self[Name] extends ModelRelations ? Self[Name]['subQuery'] : never>(name: Name, callback: (builder: RelatedBuilder) => void) => LazyLoadAggregatesContract<Self>;
/**
* Load count
*/
loadCount: <Self extends this, Name extends ExtractModelRelations<Self>, RelatedBuilder = Self[Name] extends ModelRelations ? Self[Name]['subQuery'] : never>(name: Name, callback?: (builder: RelatedBuilder) => void) => LazyLoadAggregatesContract<Self>;
/**
* Serialize attributes to a plain object
*/
serializeAttributes(fields?: CherryPickFields, raw?: boolean): ModelObject;
/**
* Serialize computed properties to a plain object
*/
serializeComputed(fields?: CherryPickFields): ModelObject;
/**
* Serialize relationships to key-value pair of model instances and
* their serializeAs keys
*/
serializeRelations(fields: undefined, raw: true): {
[key: string]: LucidRow | LucidRow[];
};
/**
* Serialize relationships to key-value pair of plain nested objects
*/
serializeRelations(cherryPick: CherryPick['relations'] | undefined, raw: false | undefined): ModelObject;
serializeRelations(cherryPick?: CherryPick['relations'], raw?: boolean): ModelObject;
/**
* Serialize model to a plain object
*/
serialize(cherryPick?: CherryPick): ModelObject;
/**
* Converts model to an object. It just returns the properties
* of the model, along with preloaded relationships
*/
toObject(): ModelObject;
/**
* Serialize everything
*/
toJSON(): ModelObject;
/**
* Returns related model for a given relationship
*/
related<Name extends ExtractModelRelations<this>>(relation: Name): this[Name] extends ModelRelations ? this[Name]['client'] : never;
}
/**
* ------------------------------------------------------
* Shape of Model constructor
* ------------------------------------------------------
*/
/**
* Shape of the model static properties. The `$` prefix is to denote
* special properties from the base model.
*
* @note: Since the interface name appears next to the inherited model
* methods, we have to choose a succinct name
*/
interface LucidModel {
/**
* Whether or not model has been booted. After this model configurations
* are ignored
*/
readonly booted: boolean;
/**
* A map of defined columns
*/
$columnsDefinitions: Map<string, ModelColumnOptions>;
/**
* A map of defined relationships
*/
$relationsDefinitions: Map<string, RelationshipsContract>;
/**
* A map of computed properties
*/
$computedDefinitions: Map<string, ComputedOptions>;
/**
* The primary key for finding unique referencing to a
* model
*/
primaryKey: string;
/**
* Custom database connection to use
*/
connection?: string;
/**
* Naming strategy to use
*/
namingStrategy: NamingStrategyContract;
/**
* Database table to use
*/
table: string;
/**
* Self assign the primary instead of relying on the database to
* return it back
*/
selfAssignPrimaryKey: boolean;
/**
* Adapter to work as a bridge between query builder and the model
*/
$adapter: AdapterContract;
/**
* Reference to hooks
*/
$hooks: Hooks;
/**
* A copy of internal keys mapping. One should be able to resolve between
* all key versions
*/
$keys: {
attributesToColumns: ModelKeysContract;
attributesToSerialized: ModelKeysContract;
columnsToAttributes: ModelKeysContract;
columnsToSerialized: ModelKeysContract;
serializedToColumns: ModelKeysContract;
serializedToAttributes: ModelKeysContract;
};
/**
* Creating model from adapter results
*/
$createFromAdapterResult<T extends LucidModel>(this: T, result?: ModelObject, sideloadAttributes?: ModelObject, options?: ModelAdapterOptions): null | InstanceType<T>;
/**
* Creating multiple model instances from an array of adapter
* result
*/
$createMultipleFromAdapterResult<T extends LucidModel>(this: T, results: ModelObject[], sideloadAttributes?: ModelObject, options?: ModelAdapterOptions): InstanceType<T>[];
/**
* Managing columns
*/
$addColumn(name: string, options: Partial<ColumnOptions>): ColumnOptions;
$hasColumn(name: string): boolean;
$getColumn(name: string): ModelColumnOptions | undefined;
/**
* Managing computed columns
*/
$addComputed(name: string, options: Partial<ComputedOptions>): ComputedOptions;
$hasComputed(name: string): boolean;
$getComputed(name: string): ComputedOptions | undefined;
/**
* Managing relationships
*/
$addRelation(name: string, type: ModelRelationTypes['__opaque_type'], relatedModel: () => LucidModel, options: ModelRelationOptions): void;
/**
* Find if a relationship exists
*/
$hasRelation(name: string): boolean;
/**
* Get relationship declaration
*/
$getRelation<Model extends LucidModel, Name extends ExtractModelRelations<InstanceType<Model>>>(this: Model, name: Name): InstanceType<Model>[Name] extends ModelRelations ? InstanceType<Model>[Name]['client']['relation'] : RelationshipsContract;
$getRelation<Model extends LucidModel>(this: Model, name: string): RelationshipsContract;
/**
* Define a static property on the model using the inherit or
* define strategy.
*
* Inherit strategy will clone the property from the parent model
* and will set it on the current model
*/
$defineProperty<Model extends LucidModel, Prop extends keyof Model>(this: Model, propertyName: Prop, defaultValue: Model[Prop], strategy: 'inherit' | 'define' | ((value: Model[Prop]) => Model[Prop])): void;
/**
* Boot model
*/
boot(): void;
/**
* Register a before hook
*/
before<Model extends LucidModel, Event extends 'find' | 'fetch'>(this: Model, event: Event, handler: HooksHandler<ModelQueryBuilderContract<Model>, Event>): void;
before<Model extends LucidModel>(this: Model, event: 'paginate', handler: HooksHandler<[
ModelQueryBuilderContract<Model>,
ModelQueryBuilderContract<Model>
], 'paginate'>): void;
before<Model extends LucidModel, Event extends EventsList>(this: Model, event: Event, handler: HooksHandler<InstanceType<Model>, Event>): void;
/**
* Register an after hook
*/
after<Model extends LucidModel>(this: Model, event: 'fetch', handler: HooksHandler<InstanceType<Model>[], 'fetch'>): void;
after<Model extends LucidModel>(this: Model, event: 'paginate', handler: HooksHandler<ModelPaginatorContract<InstanceType<Model>>, 'paginate'>): void;
after<Model extends LucidModel, Event extends EventsList>(this: Model, event: Event, handler: HooksHandler<InstanceType<Model>, Event>): void;
/**
* Create model and return its instance back
*/
create<T extends LucidModel>(this: T, values: Partial<ModelAttributes<InstanceType<T>>>, options?: ModelAssignOptions): Promise<InstanceType<T>>;
/**
* Create many of model instances
*/
createMany<T extends LucidModel>(this: T, values: Partial<ModelAttributes<InstanceType<T>>>[], options?: ModelAssignOptions): Promise<InstanceType<T>[]>;
/**
* Find one using the primary key
*/
find<T extends LucidModel>(this: T, value: any, options?: ModelAdapterOptions): Promise<null | InstanceType<T>>;
/**
* Find one using the primary key or fail
*/
findOrFail<T extends LucidModel>(this: T, value: any, options?: ModelAdapterOptions): Promise<InstanceType<T>>;
/**
* Find one using a key-value pair
*/
findBy<T extends LucidModel>(this: T, key: string, value: any, options?: ModelAdapterOptions): Promise<null | InstanceType<T>>;
/**
* Find one using a key-value pair or fail
*/
findByOrFail<T extends LucidModel>(this: T, key: string, value: any, options?: ModelAdapterOptions): Promise<InstanceType<T>>;
/**
* Same as `query().first()`
*/
first<T extends LucidModel>(this: T, options?: ModelAdapterOptions): Promise<null | InstanceType<T>>;
/**
* Same as `query().firstOrFail()`
*/
firstOrFail<T extends LucidModel>(this: T, options?: ModelAdapterOptions): Promise<InstanceType<T>>;
/**
* Find many using an array of primary keys
*/
findMany<T extends LucidModel>(this: T, value: any[], options?: ModelAdapterOptions): Promise<InstanceType<T>[]>;
/**
* Returns the first row or create a new instance of model without
* persisting it
*/
firstOrNew<T extends LucidModel>(this: T, searchPayload: Partial<ModelAttributes<InstanceType<T>>>, savePayload?: Partial<ModelAttributes<InstanceType<T>>>, options?: ModelAssignOptions): Promise<InstanceType<T>>;
/**
* Returns the first row or save it to the database
*/
firstOrCreate<T extends LucidModel>(this: T, searchPayload: Partial<ModelAttributes<InstanceType<T>>>, savePayload?: Partial<ModelAttributes<InstanceType<T>>>, options?: ModelAssignOptions): Promise<InstanceType<T>>;
/**
* Returns the first row or save it to the database
*/
updateOrCreate<T extends LucidModel>(this: T, searchPayload: Partial<ModelAttributes<InstanceType<T>>>, updatePayload: Partial<ModelAttributes<InstanceType<T>>>, options?: ModelAssignOptions): Promise<InstanceType<T>>;
/**
* Find rows or create in-memory instances of the missing
* one's.
*/
fetchOrNewUpMany<T extends LucidModel>(this: T, predicate: keyof ModelAttributes<InstanceType<T>> | (keyof ModelAttributes<InstanceType<T>>)[], payload: Partial<ModelAttributes<InstanceType<T>>>[], options?: ModelAssignOptions): Promise<InstanceType<T>[]>;
/**
* Find rows or create many when missing. One db call is invoked
* for each create
*/
fetchOrCreateMany<T extends LucidModel>(this: T, predicate: keyof ModelAttributes<InstanceType<T>> | (keyof ModelAttributes<InstanceType<T>>)[], payload: Partial<ModelAttributes<InstanceType<T>>>[], options?: ModelAssignOptions): Promise<InstanceType<T>[]>;
/**
* Update existing rows or create new one's.
*/
updateOrCreateMany<T extends LucidModel>(this: T, predicate: keyof ModelAttributes<InstanceType<T>> | (keyof ModelAttributes<InstanceType<T>>)[], payload: Partial<ModelAttributes<InstanceType<T>>>[], options?: ModelAssignOptions): Promise<InstanceType<T>[]>;
/**
* Fetch all rows
*/
all<T extends LucidModel>(this: T, options?: ModelAdapterOptions): Promise<InstanceType<T>[]>;
/**
* Returns the query for fetching a model instance
*/
query<Model extends LucidModel, Result = InstanceType<Model>>(this: Model, options?: ModelAdapterOptions): ModelQueryBuilderContract<Model, Result>;
/**
* Truncate model table
*/
truncate(cascade?: boolean): Promise<void>;
new (): LucidRow;
}
/**
* ------------------------------------------------------
* Database Adapter
* ------------------------------------------------------
*/
/**
* Every adapter must adhere to the Adapter contract
*/
interface AdapterContract {
/**
* Returns query client for a model instance by inspecting it's options
*/
modelClient(instance: LucidRow): QueryClientContract;
/**
* Returns query client for a model constructor
*/
modelConstructorClient(modelConstructor: LucidModel, options?: ModelAdapterOptions): QueryClientContract;
/**
* Delete model instance
*/
delete(instance: LucidRow): Promise<void>;
/**
* Refresh model instance to reflect new values
* from the database
*/
refresh(instance: LucidRow): Promise<void>;
/**
* Perform insert
*/
insert(instance: LucidRow, attributes: ModelObject): Promise<void>;
/**
* Perform update
*/
update(instance: LucidRow, attributes: ModelObject): Promise<void>;
/**
* Must return the query builder for the model
*/
query(modelConstructor: LucidModel, options?: ModelAdapterOptions): ModelQueryBuilderContract<LucidModel, LucidRow>;
}
/**
* Naming strategy for model
*/
interface NamingStrategyContract {
/**
* The default table name for the given model
*/
tableName(model: LucidModel): string;
/**
* The database column name for a given model attribute
*/
columnName(model: LucidModel, attributeName: string): string;
/**
* The post serialization name for a given model attribute
*/
serializedName(model: LucidModel, attributeName: string): string;
/**
* The local key for a given model relationship
*/
relationLocalKey(relation: ModelRelations['__opaque_type'], model: LucidModel, relatedModel: LucidModel, relationName: string): string;
/**
* The foreign key for a given model relationship
*/
relationForeignKey(relation: ModelRelations['__opaque_type'], model: LucidModel, relatedModel: LucidModel, relationName: string): string;
/**
* Pivot table name for many to many relationship
*/
relationPivotTable(relation: 'manyToMany', model: LucidModel, relatedModel: LucidModel, relationName: string): string;
/**
* Pivot foreign key for many to many relationship
*/
relationPivotForeignKey(relation: 'manyToMany', model: LucidModel, relatedModel: LucidModel, relationName: string): string;
/**
* Keys for the pagination meta
*/
paginationMetaKeys(): SimplePaginatorMetaKeys;
}
}
+8
View File
@@ -0,0 +1,8 @@
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
+47
View File
@@ -0,0 +1,47 @@
declare module '@ioc:Adonis/Lucid/Orm' {
import { SimplePaginatorMetaKeys } from '@ioc:Adonis/Lucid/Database';
const SnakeCaseNamingStrategy: {
new (): NamingStrategyContract;
};
const scope: ScopeFn;
const BaseModel: LucidModel;
/**
* Relationships
*/
const hasOne: HasOneDecorator;
const belongsTo: BelongsToDecorator;
const hasMany: HasManyDecorator;
const manyToMany: ManyToManyDecorator;
const hasManyThrough: HasManyThroughDecorator;
/**
* Hooks
*/
const beforeSave: HooksDecorator;
const afterSave: HooksDecorator;
const beforeCreate: HooksDecorator;
const afterCreate: HooksDecorator;
const beforeUpdate: HooksDecorator;
const afterUpdate: HooksDecorator;
const beforeDelete: HooksDecorator;
const afterDelete: HooksDecorator;
const beforeFind: HooksDecorator;
const afterFind: HooksDecorator;
const beforeFetch: HooksDecorator;
const afterFetch: HooksDecorator;
const beforePaginate: HooksDecorator;
const afterPaginate: HooksDecorator;
const ModelPaginator: {
namingStrategy: {
paginationMetaKeys(): SimplePaginatorMetaKeys;
};
new <Row extends LucidRow>(total: number, perPage: number, currentPage: number, ...rows: Row[]): ModelPaginatorContract<Row>;
};
/**
* Columns and computed
*/
const column: ColumnDecorator & {
date: DateColumnDecorator;
dateTime: DateTimeColumnDecorator;
};
const computed: ComputedDecorator;
}
+8
View File
@@ -0,0 +1,8 @@
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
+701
View File
@@ -0,0 +1,701 @@
/// <reference types="node" />
declare module '@ioc:Adonis/Lucid/Database' {
import { Knex } from 'knex';
import { DialectContract, QueryClientContract, TransactionClientContract } from '@ioc:Adonis/Lucid/Database';
/**
* Extracted from ts-essentials
*/
type Dictionary<T, K extends string | number = string> = {
[key in K]: T;
};
/**
* Get one or many of a generic
*/
type OneOrMany<T> = T | T[];
/**
* Allowing a generic value along with raw query instance or a subquery
* instance
*/
type ValueWithSubQueries<T> = T | ChainableContract | RawQuery;
/**
* Acceptable raw queries
*/
type RawQuery = RawBuilderContract | RawQueryBuilderContract | Knex.Raw | Knex.RawQueryBuilder;
/**
* A known set of values allowed when defining values for different
* clauses
*/
type StrictValues = string | number | boolean | Date | Array<string> | Array<number> | Array<Date> | Array<boolean> | Buffer | RawQuery | ReferenceBuilderContract;
/**
* Strict set of allowed values except the raw queries
*/
type StrictValuesWithoutRaw = Exclude<StrictValues, RawQuery>;
/**
* Shape of raw query bindings
*/
type RawQueryBindings = {
[key: string]: StrictValues;
} | StrictValues[];
/**
* A builder method to allow raw queries. However, the return type is the
* instance of current query builder. This is used for `.{verb}Raw` methods.
*/
interface RawQueryFn<Builder extends ChainableContract> {
(sql: string | RawQuery): Builder;
(sql: string, bindings: RawQueryBindings): Builder;
}
/**
* Query callback is used to write wrapped queries. We get rid of `this` from
* knex, since it makes everything confusing.
*/
type QueryCallback<Builder> = (builder: Builder) => void;
/**
* Shape of the function accepted by the chainable query builder to
* pass lucid query builder to wrapped callbacks like
* `.where(function () {})`.
*
* - This method will accept the wrapped callback
* - Return a new method, that is accepted by Knex.
* - When knex calls that method, this method will invoke the user wrapped
* callback, but instead of passing the knex query builder, it will
* pass the appropriate lucid query builder.
*/
type DBQueryCallback = (userFn: QueryCallback<ChainableContract>, keysResolver?: (columnName: string) => string) => (builder: Knex.QueryBuilder) => void;
/**
* Possible signatures for a select method on database query builder.
*/
interface DatabaseQueryBuilderSelect<Builder extends ChainableContract> {
/**
* Selecting columns as a dictionary with key as the alias and value is
* the original column.
*/
(columns: Dictionary<string, string>): Builder;
/**
* An array of values with subqueries
*/
(columns: ValueWithSubQueries<string | number>[]): Builder;
/**
* A spread of array arguments
*/
(...columns: ValueWithSubQueries<string | number>[]): Builder;
/**
* Wildcard selector.
*/
(column: '*'): Builder;
}
/**
* Possible signatures for adding a where clause
*/
interface Where<Builder extends ChainableContract> {
/**
* Callback for wrapped clauses
*/
(callback: QueryCallback<Builder>): Builder;
/**
* Passing an object of named key-value pair
*/
(clause: Dictionary<any, string>): Builder;
/**
* Key-value pair. The value can also be a subquery
*/
(key: string | RawQuery, value: StrictValues | ChainableContract): Builder;
(key: string | RawQuery, operator: string, value: StrictValues | ChainableContract): Builder;
}
/**
* Possible signatures for adding a whereLike clause
*/
interface WhereLike<Builder extends ChainableContract> {
/**
* Key-value pair. The value can also be a subquery
*/
(key: string, value: StrictValues | ChainableContract): Builder;
}
/**
* Possible signatures for adding a whereLike clause
*/
interface WhereJson<Builder extends ChainableContract> {
/**
* Key-value pair. The value can also be a subquery
*/
(column: string, value: Record<string, any> | ChainableContract | QueryCallback<Builder>): Builder;
}
interface WhereJsonPath<Builder extends ChainableContract> {
(column: string, jsonPath: string, value: string | number | boolean | string[] | number[] | boolean[] | Record<string, any> | ChainableContract | QueryCallback<Builder>): Builder;
(column: string, jsonPath: string, operator: string, value: string | number | boolean | string[] | number[] | boolean[] | Record<string, any> | ChainableContract | QueryCallback<Builder>): Builder;
}
/**
* Possible signatures for adding a where column clause
*/
interface WhereColumn<Builder extends ChainableContract> {
/**
* Key-value pair.
*/
(column: string | RawQuery, comparisonColumn: string): Builder;
(column: string | RawQuery, operator: string, comparisonColumn: string): Builder;
}
/**
* Possible signatures for adding where in clause.
*/
interface WhereIn<Builder extends ChainableContract> {
/**
* Column name and array of values
*/
(K: string | RawQuery, value: StrictValues[]): Builder;
/**
* Column names and array of values as an 2d array
*/
(K: string[], value: StrictValues[][]): Builder;
/**
* Column name with a subquery for a callback that yields an array of
* results
*/
(k: string | RawQuery, subquery: ChainableContract | QueryCallback<Builder> | RawBuilderContract | RawQuery): Builder;
/**
* Column names along with a subquery that yields an array
*/
(k: string[], subquery: ChainableContract | RawBuilderContract | RawQuery): Builder;
}
/**
* Possible signatures for adding whereNull clause.
*/
interface WhereNull<Builder extends ChainableContract> {
(key: string | RawQuery): Builder;
}
/**
* Possibles signatures for adding a where exists clause
*/
interface WhereExists<Builder extends ChainableContract> {
(callback: QueryCallback<Builder> | ChainableContract | RawBuilderContract | RawQuery): Builder;
}
/**
* Possibles signatures for adding a where between clause
*/
interface WhereBetween<Builder extends ChainableContract> {
/**
* Accept any string as a key for supporting prefix columns
*/
(key: string | RawQuery, value: [StrictValues | ChainableContract, StrictValues | ChainableContract]): Builder;
}
/**
* Possible signatures for join query
*/
interface Join<Builder extends ChainableContract> {
/**
* Defining the join table with primary and secondary columns
* to match
*/
(table: string, primaryColumn: string, secondaryColumn: string): Builder;
/**
* Defining the join table with primary and secondary columns
* to match, where secondary column is output of a raw query
*/
(table: string, primaryColumn: string, raw: RawQuery): Builder;
/**
* Defining the join table with primary and secondary columns
* to match with a custom operator
*/
(table: string, primaryColumn: string, operator: string, secondaryColumn: string): Builder;
/**
* Join with a callback. The callback receives an array of join class from
* knex directly.
*/
(table: string, callback: Knex.JoinCallback): Builder;
}
/**
* Possible signatures for a distinct clause
*/
interface Distinct<Builder extends ChainableContract> {
(columns: string[]): Builder;
(...columns: string[]): Builder;
(column: '*'): Builder;
}
/**
* The signatures are same as the `distinct` method. For subqueries and
* raw queries, one must use `groupByRaw`.
*/
interface GroupBy<Builder extends ChainableContract> extends Distinct<Builder> {
}
/**
* Possible signatures for aggregate functions. Aggregates will push to the
* result set. Unlike knex, we force defining aliases for each aggregate.
*/
interface Aggregate<Builder extends ChainableContract> {
/**
* Accepting column with the alias for the count.
*/
(column: OneOrMany<ValueWithSubQueries<string>>, alias?: string): Builder;
/**
* Accepting an object for multiple counts in a single query.
*/
(columns: Dictionary<OneOrMany<ValueWithSubQueries<string>>, string>): Builder;
}
/**
* Possible signatures for orderBy method.
*/
interface OrderBy<Builder extends ChainableContract> {
/**
* Order by a column and optional direction
*/
(column: string | ChainableContract | RawBuilderContract | RawQuery, direction?: 'asc' | 'desc'): Builder;
/**
* Order by multiple columns in default direction
*/
(columns: string[]): Builder;
/**
* Order by multiple columns and custom direction for each of them
*/
(columns: {
column: string | ChainableContract | RawBuilderContract | RawQuery;
order?: 'asc' | 'desc';
}[]): Builder;
}
/**
* Possible signatures for a union clause
*/
interface Union<Builder extends ChainableContract> {
(callback: OneOrMany<QueryCallback<Builder>>, wrap?: boolean): Builder;
(subquery: OneOrMany<ChainableContract | RawQuery>, wrap?: boolean): Builder;
}
/**
* Same signature as union
*/
interface UnionAll<Builder extends ChainableContract> extends Union<Builder> {
}
/**
* Same signature as union
*/
interface Intersect<Builder extends ChainableContract> extends Union<Builder> {
}
/**
* Possible signatures for having clause
*/
interface Having<Builder extends ChainableContract> {
/**
* A subquery callback
*/
(callback: QueryCallback<Builder> | RawBuilderContract | RawQuery): Builder;
/**
* Key operator and value. Value can be a subquery as well
*/
(key: string | RawQuery, operator: string, value: StrictValues | ChainableContract | RawBuilderContract | RawQuery): Builder;
}
/**
* Possible signatures for `having in` clause.
*/
interface HavingIn<Builder extends ChainableContract> {
/**
* Key and an array of literal values, raw queries or
* subqueries.
*/
(key: string | RawQuery, value: (StrictValues | ChainableContract | RawBuilderContract | RawQuery)[]): Builder;
/**
* Key, along with a query callback
*/
(key: string | RawQuery, callback: QueryCallback<Builder>): Builder;
}
/**
* Possible signatures for `having null` clause
*/
interface HavingNull<Builder extends ChainableContract> extends WhereNull<Builder> {
}
/**
* Possible signatures for `having exists` clause
*/
interface HavingExists<Builder extends ChainableContract> {
/**
* A query callback or a sub query
*/
(callback: QueryCallback<Builder> | ChainableContract): Builder;
}
/**
* Possible signatures for having between
*/
interface HavingBetween<Builder extends ChainableContract> extends WhereBetween<Builder> {
}
/**
* Possible signatures of `with` CTE
*/
interface With<Builder extends ChainableContract> {
(alias: string, query: RawQuery | ChainableContract | QueryCallback<Builder>, columns?: string[]): Builder;
}
/**
* Possible signatures for defining table for a select query.
*/
interface FromTable<Builder extends ChainableContract> {
(table: string | Dictionary<string, string> | QueryCallback<Builder> | ChainableContract): Builder;
}
/**
* Possible signatures for the `returning` method.
*/
interface Returning<Builder> {
(column: OneOrMany<string>): Builder;
}
/**
* Possible signatures for performing an update
*/
interface Update<Builder extends ChainableContract> {
/**
* Accepts an array of object of named key/value pair and returns an array
* of Generic return columns.
*/
(values: Dictionary<any, string>, returning?: OneOrMany<string>): Builder;
/**
* Accepts a key-value pair to update.
*/
(column: string, value: any, returning?: OneOrMany<string>): Builder;
}
/**
* Possible signatures for incrementing/decrementing
* values
*/
interface Counter<Builder extends ChainableContract> {
(column: string, counter?: number): Builder;
(values: Dictionary<number, string>): Builder;
}
/**
* Possible signatures for an insert query
*/
interface Insert<Builder extends InsertQueryBuilderContract> {
(values: Dictionary<any, string>): Builder;
}
/**
* Possible signatures for doing multiple inserts in a single query
*/
interface MultiInsert<Builder extends InsertQueryBuilderContract> {
(values: Dictionary<any, string>[]): Builder;
}
/**
* The chainable contract has all the methods that can be chained
* to build a query. This interface will never have any
* methods to execute a query.
*/
interface ChainableContract {
knexQuery: Knex.QueryBuilder;
columns: (string | Knex.QueryBuilder | Knex.RawQueryBuilder)[];
subQueryAlias?: string;
hasAggregates: boolean;
hasGroupBy: boolean;
hasUnion: boolean;
keysResolver?: (columnName: string) => string;
from: FromTable<this>;
select: DatabaseQueryBuilderSelect<this>;
wrapExisting(): this;
where: Where<this>;
orWhere: Where<this>;
andWhere: Where<this>;
whereNot: Where<this>;
orWhereNot: Where<this>;
andWhereNot: Where<this>;
whereColumn: WhereColumn<this>;
orWhereColumn: WhereColumn<this>;
andWhereColumn: WhereColumn<this>;
whereNotColumn: WhereColumn<this>;
orWhereNotColumn: WhereColumn<this>;
andWhereNotColumn: WhereColumn<this>;
whereIn: WhereIn<this>;
orWhereIn: WhereIn<this>;
andWhereIn: WhereIn<this>;
whereNotIn: WhereIn<this>;
orWhereNotIn: WhereIn<this>;
andWhereNotIn: WhereIn<this>;
whereNull: WhereNull<this>;
orWhereNull: WhereNull<this>;
andWhereNull: WhereNull<this>;
whereNotNull: WhereNull<this>;
orWhereNotNull: WhereNull<this>;
andWhereNotNull: WhereNull<this>;
whereExists: WhereExists<this>;
orWhereExists: WhereExists<this>;
andWhereExists: WhereExists<this>;
whereNotExists: WhereExists<this>;
orWhereNotExists: WhereExists<this>;
andWhereNotExists: WhereExists<this>;
whereBetween: WhereBetween<this>;
orWhereBetween: WhereBetween<this>;
andWhereBetween: WhereBetween<this>;
whereNotBetween: WhereBetween<this>;
orWhereNotBetween: WhereBetween<this>;
andWhereNotBetween: WhereBetween<this>;
whereRaw: RawQueryFn<this>;
orWhereRaw: RawQueryFn<this>;
andWhereRaw: RawQueryFn<this>;
whereLike: WhereLike<this>;
orWhereLike: WhereLike<this>;
andWhereLike: WhereLike<this>;
whereILike: WhereLike<this>;
orWhereILike: WhereLike<this>;
andWhereILike: WhereLike<this>;
whereJson: WhereJson<this>;
orWhereJson: WhereJson<this>;
andWhereJson: WhereJson<this>;
whereNotJson: WhereJson<this>;
orWhereNotJson: WhereJson<this>;
andWhereNotJson: WhereJson<this>;
whereJsonSuperset: WhereJson<this>;
orWhereJsonSuperset: WhereJson<this>;
andWhereJsonSuperset: WhereJson<this>;
whereNotJsonSuperset: WhereJson<this>;
orWhereNotJsonSuperset: WhereJson<this>;
andWhereNotJsonSuperset: WhereJson<this>;
whereJsonSubset: WhereJson<this>;
orWhereJsonSubset: WhereJson<this>;
andWhereJsonSubset: WhereJson<this>;
whereNotJsonSubset: WhereJson<this>;
orWhereNotJsonSubset: WhereJson<this>;
andWhereNotJsonSubset: WhereJson<this>;
whereJsonPath: WhereJsonPath<this>;
orWhereJsonPath: WhereJsonPath<this>;
andWhereJsonPath: WhereJsonPath<this>;
join: Join<this>;
innerJoin: Join<this>;
leftJoin: Join<this>;
leftOuterJoin: Join<this>;
rightJoin: Join<this>;
rightOuterJoin: Join<this>;
fullOuterJoin: Join<this>;
crossJoin: Join<this>;
joinRaw: RawQueryFn<this>;
having: Having<this>;
orHaving: Having<this>;
andHaving: Having<this>;
havingIn: HavingIn<this>;
orHavingIn: HavingIn<this>;
andHavingIn: HavingIn<this>;
havingNotIn: HavingIn<this>;
orHavingNotIn: HavingIn<this>;
andHavingNotIn: HavingIn<this>;
havingNull: HavingNull<this>;
orHavingNull: HavingNull<this>;
andHavingNull: HavingNull<this>;
havingNotNull: HavingNull<this>;
orHavingNotNull: HavingNull<this>;
andHavingNotNull: HavingNull<this>;
havingExists: HavingExists<this>;
orHavingExists: HavingExists<this>;
andHavingExists: HavingExists<this>;
havingNotExists: HavingExists<this>;
orHavingNotExists: HavingExists<this>;
andHavingNotExists: HavingExists<this>;
havingBetween: HavingBetween<this>;
orHavingBetween: HavingBetween<this>;
andHavingBetween: HavingBetween<this>;
havingNotBetween: HavingBetween<this>;
orHavingNotBetween: HavingBetween<this>;
andHavingNotBetween: HavingBetween<this>;
havingRaw: RawQueryFn<this>;
orHavingRaw: RawQueryFn<this>;
andHavingRaw: RawQueryFn<this>;
distinct: Distinct<this>;
distinctOn: Distinct<this>;
groupBy: GroupBy<this>;
groupByRaw: RawQueryFn<this>;
orderBy: OrderBy<this>;
orderByRaw: RawQueryFn<this>;
union: Union<this>;
unionAll: UnionAll<this>;
intersect: Intersect<this>;
with: With<this>;
withRecursive: With<this>;
withMaterialized: With<this>;
withNotMaterialized: With<this>;
withSchema(schema: string): this;
as(name: string): this;
offset(offset: number): this;
limit(limit: number): this;
clearSelect(): this;
clearWhere(): this;
clearOrder(): this;
clearHaving(): this;
clearLimit(): this;
clearOffset(): this;
forUpdate(...tableNames: string[]): this;
forShare(...tableNames: string[]): this;
skipLocked(): this;
noWait(): this;
/**
* Executes the callback when condition is truthy
*/
if(condition: any, matchCallback: (query: this) => any, noMatchCallback?: (query: this) => any): this;
/**
* Executes the callback when condition is falsy
*/
unless(condition: any, matchCallback: (query: this) => any, noMatchCallback?: (query: this) => any): this;
/**
* Write blocks to match from
*/
match(...blocks: ([condition: any, callback: (query: this) => any] | ((query: this) => any))[]): this;
}
/**
* Shape of the raw query that can also be passed as a value to
* other queries
*/
interface RawQueryBuilderContract<Result = any> extends ExcutableQueryBuilderContract<Result> {
knexQuery: Knex.Raw;
client: QueryClientContract;
wrap(before: string, after: string): this;
}
/**
* Reference builder
*/
interface ReferenceBuilderContract {
withSchema(name: string): this;
as(name: string): this;
toKnex(client: Knex.Client): Knex.Ref<string, any>;
}
/**
* Static raw builder
*/
interface RawBuilderContract {
wrap(before: string, after: string): this;
toKnex(client: Knex.Client): Knex.Raw;
}
/**
* The keys for the simple paginator meta
* data
*/
type SimplePaginatorMetaKeys = {
total: string;
perPage: string;
currentPage: string;
lastPage: string;
firstPage: string;
firstPageUrl: string;
lastPageUrl: string;
nextPageUrl: string;
previousPageUrl: string;
};
/**
* Shape of the simple paginator that works with offset and limit
*/
interface SimplePaginatorContract<Result> extends Array<Result> {
all(): Result[];
readonly firstPage: number;
readonly perPage: number;
readonly currentPage: number;
readonly lastPage: number;
readonly hasPages: boolean;
readonly hasMorePages: boolean;
readonly isEmpty: boolean;
readonly total: number;
readonly hasTotal: boolean;
namingStrategy: {
paginationMetaKeys(): SimplePaginatorMetaKeys;
};
baseUrl(url: string): this;
queryString(values: {
[key: string]: any;
}): this;
getUrl(page: number): string;
getMeta(): any;
getNextPageUrl(): string | null;
getPreviousPageUrl(): string | null;
getUrlsForRange(start: number, end: number): {
url: string;
page: number;
isActive: boolean;
}[];
toJSON(): {
meta: any;
data: Result[];
};
}
/**
* Database query builder exposes the API to construct SQL query using fluent
* chainable API
*/
interface DatabaseQueryBuilderContract<Result = Dictionary<any, string>> extends ChainableContract, ExcutableQueryBuilderContract<Result[]> {
client: QueryClientContract;
returning: Returning<this>;
/**
* Clone current query
*/
clone<ClonedResult = Result>(): DatabaseQueryBuilderContract<ClonedResult>;
/**
* Execute and get first result
*/
first(): Promise<Result | null>;
/**
* Execute and get first result or fail
*/
firstOrFail(): Promise<Result>;
/**
* Perform delete operation
*/
del(returning?: OneOrMany<string>): this;
delete(returning?: OneOrMany<string>): this;
/**
* A shorthand to define limit and offset based upon the
* current page
*/
forPage(page: number, perPage?: number): this;
/**
* Execute query with pagination
*/
paginate(page: number, perPage?: number): Promise<SimplePaginatorContract<Result>>;
/**
* Mutations (update and increment can be one query aswell)
*/
update: Update<this>;
increment: Counter<this>;
decrement: Counter<this>;
/**
* Aggregates
*/
count: Aggregate<this>;
countDistinct: Aggregate<this>;
min: Aggregate<this>;
max: Aggregate<this>;
sum: Aggregate<this>;
sumDistinct: Aggregate<this>;
avg: Aggregate<this>;
avgDistinct: Aggregate<this>;
/**
* Executes the callback when dialect matches one of the mentioned
* dialects
*/
ifDialect(dialect: DialectContract['name'] | DialectContract['name'][], matchCallback: (query: this) => any, noMatchCallback?: (query: this) => any): this;
/**
* Executes the callback when dialect matches doesn't all the mentioned
* dialects
*/
unlessDialect(dialect: DialectContract['name'] | DialectContract['name'][], matchCallback: (query: this) => any, noMatchCallback?: (query: this) => any): this;
}
/**
* Insert query builder to perform database inserts.
*/
interface InsertQueryBuilderContract<Result = any> extends ExcutableQueryBuilderContract<Result> {
knexQuery: Knex.QueryBuilder;
client: QueryClientContract;
/**
* Table for the insert query
*/
table(table: string): this;
withSchema(schema: string): this;
/**
* Define returning columns
*/
returning: Returning<this>;
/**
* Inserting a single record.
*/
insert: Insert<this>;
/**
* Inserting multiple columns at once
*/
multiInsert: MultiInsert<this>;
}
/**
* A executable query builder will always have these methods on it.
*/
interface ExcutableQueryBuilderContract<Result> extends Promise<Result> {
debug(debug: boolean): this;
timeout(time: number, options?: {
cancel: boolean;
}): this;
useTransaction(trx: TransactionClientContract): this;
reporterData(data: any): this;
toQuery(): string;
exec(): Promise<Result>;
toSQL(): Knex.Sql;
}
}
+8
View File
@@ -0,0 +1,8 @@
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
+665
View File
@@ -0,0 +1,665 @@
declare module '@ioc:Adonis/Lucid/Orm' {
import { RawQuery, OneOrMany, StrictValues, QueryCallback, ChainableContract, RawBuilderContract, QueryClientContract, TransactionClientContract } from '@ioc:Adonis/Lucid/Database';
/**
* ------------------------------------------------------
* Helpers
* ------------------------------------------------------
*/
/**
* Extracts relationship attributes from the model
*/
type ExtractModelRelations<Model extends LucidRow> = {
[Key in keyof Model]: Model[Key] extends ModelRelations ? Key : never;
}[keyof Model];
/**
* Returns relationship model instance or array of instances based
* upon the relationship type
*/
type GetRelationModelInstance<Relation extends ModelRelations> = Relation['__opaque_type'] extends 'hasOne' | 'belongsTo' ? Relation['instance'] : Relation['instance'][];
/**
* ------------------------------------------------------
* Options
* ------------------------------------------------------
*/
/**
* Options accepted when defining a new relationship. Certain
* relationships like `manyToMany` have their own options
*/
type RelationOptions<Related extends ModelRelations> = {
localKey?: string;
foreignKey?: string;
serializeAs?: string | null;
onQuery?(query: Related['builder'] | Related['subQuery']): void;
};
/**
* Options accepted by many to many relationship
*/
type ManyToManyRelationOptions<Related extends ModelRelations> = {
pivotTable?: string;
localKey?: string;
pivotForeignKey?: string;
relatedKey?: string;
pivotRelatedForeignKey?: string;
pivotColumns?: string[];
pivotTimestamps?: boolean | {
createdAt: string | boolean;
updatedAt: string | boolean;
};
serializeAs?: string | null;
onQuery?(query: Related['builder'] | Related['subQuery']): void;
};
/**
* Options accepted by through relationships
*/
type ThroughRelationOptions<Related extends ModelRelations> = RelationOptions<Related> & {
throughLocalKey?: string;
throughForeignKey?: string;
throughModel: () => LucidModel;
};
/**
* ------------------------------------------------------
* Decorators
* ------------------------------------------------------
*/
/**
* Decorator signature to define has one relationship
*/
type HasOneDecorator = <RelatedModel extends LucidModel>(model: () => RelatedModel, options?: RelationOptions<HasOne<RelatedModel>>) => TypedDecorator<HasOne<RelatedModel> | null>;
/**
* Decorator signature to define has many relationship
*/
type HasManyDecorator = <RelatedModel extends LucidModel>(model: () => RelatedModel, options?: RelationOptions<HasOne<RelatedModel>>) => TypedDecorator<HasMany<RelatedModel>>;
/**
* Decorator signature to define belongs to relationship
*/
type BelongsToDecorator = <RelatedModel extends LucidModel>(model: () => RelatedModel, options?: RelationOptions<HasOne<RelatedModel>>) => TypedDecorator<BelongsTo<RelatedModel> | null>;
/**
* Decorator signature to define many to many relationship
*/
type ManyToManyDecorator = <RelatedModel extends LucidModel>(model: () => RelatedModel, column?: ManyToManyRelationOptions<ManyToMany<RelatedModel>>) => TypedDecorator<ManyToMany<RelatedModel>>;
/**
* Decorator signature to define has many through relationship
*/
type HasManyThroughDecorator = <RelatedModel extends LucidModel>(model: [() => RelatedModel, () => LucidModel], column?: Omit<ThroughRelationOptions<HasManyThrough<RelatedModel>>, 'throughModel'>) => TypedDecorator<HasManyThrough<RelatedModel>>;
/**
* ------------------------------------------------------
* Opaque typed relationships
* ------------------------------------------------------
*
* They have no runtime relevance, just a way to distinguish
* between standard model properties and relationships
*
*/
type ModelRelationTypes = {
readonly __opaque_type: 'hasOne' | 'hasMany' | 'belongsTo' | 'manyToMany' | 'hasManyThrough';
};
/**
* Opaque type for has one relationship
*/
type HasOne<RelatedModel extends LucidModel, ParentModel extends LucidModel = LucidModel> = InstanceType<RelatedModel> & {
readonly __opaque_type: 'hasOne';
model: RelatedModel;
instance: InstanceType<RelatedModel>;
client: HasOneClientContract<HasOneRelationContract<ParentModel, RelatedModel>, RelatedModel>;
builder: RelationQueryBuilderContract<RelatedModel, any>;
subQuery: RelationSubQueryBuilderContract<RelatedModel>;
};
/**
* Opaque type for has many relationship
*/
type HasMany<RelatedModel extends LucidModel, ParentModel extends LucidModel = LucidModel> = InstanceType<RelatedModel>[] & {
readonly __opaque_type: 'hasMany';
model: RelatedModel;
instance: InstanceType<RelatedModel>;
client: HasManyClientContract<HasManyRelationContract<ParentModel, RelatedModel>, RelatedModel>;
builder: HasManyQueryBuilderContract<RelatedModel, any>;
subQuery: RelationSubQueryBuilderContract<RelatedModel>;
};
/**
* Opaque type for has belongs to relationship
*/
type BelongsTo<RelatedModel extends LucidModel, ParentModel extends LucidModel = LucidModel> = InstanceType<RelatedModel> & {
readonly __opaque_type: 'belongsTo';
model: RelatedModel;
instance: InstanceType<RelatedModel>;
client: BelongsToClientContract<BelongsToRelationContract<ParentModel, RelatedModel>, RelatedModel>;
builder: RelationQueryBuilderContract<RelatedModel, any>;
subQuery: RelationSubQueryBuilderContract<RelatedModel>;
};
/**
* Opaque type for many to many relationship
*/
type ManyToMany<RelatedModel extends LucidModel, ParentModel extends LucidModel = LucidModel> = InstanceType<RelatedModel>[] & {
readonly __opaque_type: 'manyToMany';
model: RelatedModel;
instance: InstanceType<RelatedModel>;
client: ManyToManyClientContract<ManyToManyRelationContract<ParentModel, RelatedModel>, RelatedModel>;
builder: ManyToManyQueryBuilderContract<RelatedModel, any>;
subQuery: ManyToManySubQueryBuilderContract<RelatedModel>;
};
/**
* Opaque type for many to many relationship
*/
type HasManyThrough<RelatedModel extends LucidModel, ParentModel extends LucidModel = LucidModel> = InstanceType<RelatedModel>[] & {
readonly __opaque_type: 'hasManyThrough';
model: RelatedModel;
instance: InstanceType<RelatedModel>;
client: HasManyThroughClientContract<HasManyThroughRelationContract<ParentModel, RelatedModel>, RelatedModel>;
builder: HasManyThroughQueryBuilderContract<RelatedModel, any>;
subQuery: RelationSubQueryBuilderContract<RelatedModel>;
};
/**
* These exists on the models directly as a relationship. The idea
* is to distinguish relationship properties from other model
* properties.
*/
type ModelRelations = HasOne<LucidModel, LucidModel> | HasMany<LucidModel, LucidModel> | BelongsTo<LucidModel, LucidModel> | ManyToMany<LucidModel, LucidModel> | HasManyThrough<LucidModel, LucidModel>;
/**
* ------------------------------------------------------
* Relationships
* ------------------------------------------------------
*/
/**
* Interface to be implemented by all relationship types
*/
interface BaseRelationContract<ParentModel extends LucidModel, RelatedModel extends LucidModel> {
readonly type: ModelRelationTypes['__opaque_type'];
readonly relationName: string;
readonly serializeAs: string | null;
readonly booted: boolean;
readonly model: ParentModel;
relatedModel(): RelatedModel;
boot(): void;
clone(parent: LucidModel): this;
/**
* Get client
*/
client(parent: InstanceType<ParentModel>, client: QueryClientContract): unknown;
/**
* Get eager query for the relationship
*/
eagerQuery(parent: OneOrMany<InstanceType<ParentModel>>, client: QueryClientContract): RelationQueryBuilderContract<RelatedModel, InstanceType<RelatedModel>>;
subQuery(client: QueryClientContract): RelationSubQueryBuilderContract<RelatedModel>;
}
/**
* Has one relationship interface
*/
interface HasOneRelationContract<ParentModel extends LucidModel, RelatedModel extends LucidModel> extends BaseRelationContract<ParentModel, RelatedModel> {
readonly type: 'hasOne';
readonly localKey: string;
readonly foreignKey: string;
/**
* Set related model as a relationship on the parent model.
*/
setRelated(parent: InstanceType<ParentModel>, related: InstanceType<RelatedModel> | null): void;
/**
* Push related model as a relationship on the parent model
*/
pushRelated(parent: InstanceType<ParentModel>, related: InstanceType<RelatedModel> | null): void;
/**
* Set multiple related instances on the multiple parent models.
* This method is generally invoked during eager load.
*
* Fetch 10 users and then all profiles for all 10 users and then
* call this method to set related instances
*/
setRelatedForMany(parent: InstanceType<ParentModel>[], related: InstanceType<RelatedModel>[]): void;
/**
* Returns the query client for one or many model instances. The query
* client then be used to fetch and persist relationships.
*/
client(parent: InstanceType<ParentModel>, client: QueryClientContract): HasOneClientContract<this, RelatedModel>;
/**
* Hydrates related model attributes for persistance
*/
hydrateForPersistance(parent: LucidRow, values: ModelObject | LucidRow): void;
}
/**
* Has many relationship interface
*/
interface HasManyRelationContract<ParentModel extends LucidModel, RelatedModel extends LucidModel> extends BaseRelationContract<ParentModel, RelatedModel> {
readonly type: 'hasMany';
readonly localKey: string;
readonly foreignKey: string;
/**
* Set related models as a relationship on the parent model
*/
setRelated(parent: InstanceType<ParentModel>, related: InstanceType<RelatedModel>[]): void;
/**
* Push related model(s) as a relationship on the parent model
*/
pushRelated(parent: InstanceType<ParentModel>, related: OneOrMany<InstanceType<RelatedModel>>): void;
/**
* Set multiple related instances on the multiple parent models.
* This method is generally invoked during eager load.
*
* Fetch 10 users and then all posts for all 10 users and then
* call this method to set related instances
*/
setRelatedForMany(parent: InstanceType<ParentModel>[], related: InstanceType<RelatedModel>[]): void;
/**
* Returns the query client for one or many model instances. The query
* client then be used to fetch and persist relationships.
*/
client(parent: InstanceType<ParentModel>, client: QueryClientContract): HasManyClientContract<this, RelatedModel>;
/**
* Hydrates related model attributes for persistance
*/
hydrateForPersistance(parent: LucidRow, values: ModelObject | LucidRow): void;
}
/**
* Belongs to relationship interface
*/
interface BelongsToRelationContract<ParentModel extends LucidModel, RelatedModel extends LucidModel> extends BaseRelationContract<ParentModel, RelatedModel> {
readonly type: 'belongsTo';
readonly localKey: string;
readonly foreignKey: string;
/**
* Set related model as a relationship on the parent model
*/
setRelated(parent: InstanceType<ParentModel>, related: InstanceType<RelatedModel> | null): void;
/**
* Push related model as a relationship on the parent model
*/
pushRelated(parent: InstanceType<ParentModel>, related: InstanceType<RelatedModel> | null): void;
/**
* Set multiple related instances on the multiple parent models.
* This method is generally invoked during eager load.
*
* Fetch 10 profiles and then users for all 10 profiles and then
* call this method to set related instances
*/
setRelatedForMany(parent: InstanceType<ParentModel>[], related: InstanceType<RelatedModel>[]): void;
/**
* Returns the query client for a model instance
*/
client(parent: InstanceType<ParentModel>, client: QueryClientContract): BelongsToClientContract<this, RelatedModel>;
/**
* Hydrates parent model attributes for persistance
*/
hydrateForPersistance(parent: LucidRow, values: ModelObject | LucidRow): void;
}
/**
* Many to many relationship interface
*/
interface ManyToManyRelationContract<ParentModel extends LucidModel, RelatedModel extends LucidModel> extends BaseRelationContract<ParentModel, RelatedModel> {
type: 'manyToMany';
readonly localKey: string;
readonly relatedKey: string;
readonly pivotForeignKey: string;
readonly pivotRelatedForeignKey: string;
readonly pivotTable: string;
pivotColumns: string[];
/**
* Set related models as a relationship on the parent model
*/
setRelated(parent: InstanceType<ParentModel>, related: InstanceType<RelatedModel>[]): void;
/**
* Push related model(s) as a relationship on the parent model
*/
pushRelated(parent: InstanceType<ParentModel>, related: OneOrMany<InstanceType<RelatedModel>>): void;
/**
* Set multiple related instances on the multiple parent models.
* This method is generally invoked during eager load.
*/
setRelatedForMany(parent: InstanceType<ParentModel>[], related: InstanceType<RelatedModel>[]): void;
/**
* Returns the query client for one model instance
*/
client(parent: InstanceType<ParentModel>, client: QueryClientContract): ManyToManyClientContract<this, RelatedModel>;
/**
* Get eager query for the relationship
*/
eagerQuery(parent: OneOrMany<InstanceType<ParentModel>>, client: QueryClientContract): ManyToManyQueryBuilderContract<RelatedModel, InstanceType<RelatedModel>>;
/**
* Get subquery for the relationships
*/
subQuery(client: QueryClientContract): ManyToManySubQueryBuilderContract<RelatedModel>;
/**
* Returns key-value pair for the pivot table in relation to the parent model
*/
getPivotPair(parent: LucidRow): [string, number | string];
/**
* Returns key-value pair for the pivot table in relation to the related model
*/
getPivotRelatedPair(related: LucidRow): [string, number | string];
}
/**
* Has many through relationship interface
*/
interface HasManyThroughRelationContract<ParentModel extends LucidModel, RelatedModel extends LucidModel> extends BaseRelationContract<ParentModel, RelatedModel> {
type: 'hasManyThrough';
readonly localKey: string;
readonly foreignKey: string;
readonly throughLocalKey: string;
readonly throughForeignKey: string;
/**
* Set related models as a relationship on the parent model
*/
setRelated(parent: InstanceType<ParentModel>, related: InstanceType<RelatedModel>[]): void;
/**
* Push related model(s) as a relationship on the parent model
*/
pushRelated(parent: InstanceType<ParentModel>, related: InstanceType<RelatedModel> | InstanceType<RelatedModel>[]): void;
/**
* Set multiple related instances on the multiple parent models.
* This method is generally invoked during eager load.
*/
setRelatedForMany(parent: InstanceType<ParentModel>[], related: InstanceType<RelatedModel>[]): void;
/**
* Returns the query client for a model instance
*/
client(model: InstanceType<ParentModel>, client: QueryClientContract): RelationQueryClientContract<this, RelatedModel>;
}
/**
* A union of relationships
*/
type RelationshipsContract = HasOneRelationContract<LucidModel, LucidModel> | HasManyRelationContract<LucidModel, LucidModel> | BelongsToRelationContract<LucidModel, LucidModel> | ManyToManyRelationContract<LucidModel, LucidModel> | HasManyThroughRelationContract<LucidModel, LucidModel>;
/**
* ------------------------------------------------------
* Relationships query client
* ------------------------------------------------------
*/
interface RelationQueryClientContract<Relation extends RelationshipsContract, RelatedModel extends LucidModel> {
relation: Relation;
/**
* Return a query builder instance of the relationship
*/
query<Result = InstanceType<RelatedModel>>(): RelationQueryBuilderContract<RelatedModel, Result>;
}
/**
* Query client for has one relationship
*/
interface HasOneClientContract<Relation extends RelationshipsContract, RelatedModel extends LucidModel> extends RelationQueryClientContract<Relation, RelatedModel> {
/**
* Save related instance. Sets up the FK automatically
*/
save(related: InstanceType<RelatedModel>): Promise<void>;
/**
* Create related instance. Sets up the FK automatically
*/
create(values: Partial<ModelAttributes<InstanceType<RelatedModel>>>, options?: ModelAssignOptions): Promise<InstanceType<RelatedModel>>;
/**
* Return first or create related instance
*/
firstOrCreate(search: Partial<ModelAttributes<InstanceType<RelatedModel>>>, savePayload?: Partial<ModelAttributes<InstanceType<RelatedModel>>>, options?: ModelAssignOptions): Promise<InstanceType<RelatedModel>>;
/**
* Update or create related instance
*/
updateOrCreate(search: Partial<ModelAttributes<InstanceType<RelatedModel>>>, updatePayload: Partial<ModelAttributes<InstanceType<RelatedModel>>>, options?: ModelAssignOptions): Promise<InstanceType<RelatedModel>>;
}
/**
* Query client for has many relationship. Extends hasOne and
* adds support for saving many relations
*/
interface HasManyClientContract<Relation extends RelationshipsContract, RelatedModel extends LucidModel> extends HasOneClientContract<Relation, RelatedModel> {
/**
* Save many of related instances. Sets up FK automatically
*/
saveMany(related: InstanceType<RelatedModel>[]): Promise<void>;
/**
* Create many of related instances. Sets up FK automatically
*/
createMany(values: Partial<ModelAttributes<InstanceType<RelatedModel>>>[], options?: ModelAssignOptions): Promise<InstanceType<RelatedModel>[]>;
/**
* Fetch or create rows. Providers a great API to sync rows
*/
fetchOrCreateMany(payload: Partial<ModelAttributes<InstanceType<RelatedModel>>>[], predicate?: keyof ModelAttributes<InstanceType<RelatedModel>> | (keyof ModelAttributes<InstanceType<RelatedModel>>)[], options?: ModelAssignOptions): Promise<InstanceType<RelatedModel>[]>;
/**
* Update or create rows. Providers a great API to sync rows
*/
updateOrCreateMany(payload: Partial<ModelAttributes<InstanceType<RelatedModel>>>[], predicate?: keyof ModelAttributes<InstanceType<RelatedModel>> | (keyof ModelAttributes<InstanceType<RelatedModel>>)[], options?: ModelAssignOptions): Promise<InstanceType<RelatedModel>[]>;
/**
* Return a query builder instance of the relationship
*/
query<Result = InstanceType<RelatedModel>>(): HasManyQueryBuilderContract<RelatedModel, Result>;
}
/**
* Query client for belongs to relationship. Uses `associate` and
* `dissociate` over save.
*/
interface BelongsToClientContract<Relation extends RelationshipsContract, RelatedModel extends LucidModel> extends RelationQueryClientContract<Relation, RelatedModel> {
/**
* Associate related instance
*/
associate(related: InstanceType<RelatedModel>): Promise<void>;
/**
* Dissociate related instance
*/
dissociate(): Promise<void>;
}
/**
* Query client for many to many relationship.
*/
interface ManyToManyClientContract<Relation extends RelationshipsContract, RelatedModel extends LucidModel> extends RelationQueryClientContract<Relation, RelatedModel> {
/**
* Returns related model query builder instance
*/
query<Result = InstanceType<RelatedModel>>(): ManyToManyQueryBuilderContract<RelatedModel, Result>;
/**
* Pivot query just targets the pivot table without any joins
*/
pivotQuery<Result = any>(): ManyToManyQueryBuilderContract<RelatedModel, Result>;
/**
* Save related model instance. Sets up FK automatically
*/
save(related: InstanceType<RelatedModel>, performSync?: boolean, // defaults to true
pivotAttributes?: ModelObject): Promise<void>;
/**
* Save many of related model instance. Sets up FK automatically
*/
saveMany(related: InstanceType<RelatedModel>[], performSync?: boolean, // defaults to true
pivotAttributes?: (ModelObject | undefined)[]): Promise<void>;
/**
* Create related model instance. Sets up FK automatically
*/
create(values: Partial<ModelAttributes<InstanceType<RelatedModel>>>, pivotAttributes?: ModelObject, options?: ModelAssignOptions): Promise<InstanceType<RelatedModel>>;
/**
* Create many of related model instances. Sets up FK automatically
*/
createMany(values: Partial<ModelAttributes<InstanceType<RelatedModel>>>[], pivotAttributes?: (ModelObject | undefined)[], options?: ModelAssignOptions): Promise<InstanceType<RelatedModel>[]>;
/**
* Attach new pivot rows
*/
attach(ids: (string | number)[] | Record<string, ModelObject>, trx?: TransactionClientContract): Promise<void>;
/**
* Detach existing pivot rows
*/
detach(ids?: (string | number)[], trx?: TransactionClientContract): Promise<void>;
/**
* Sync pivot rows.
*/
sync(ids: (string | number)[] | Record<string, ModelObject>, detach?: boolean, trx?: TransactionClientContract): Promise<void>;
}
/**
* HasMany through client contract. HasMany through doesn't
* allow persisting relationships. Use the direct relation
* for that.
*/
interface HasManyThroughClientContract<Relation extends RelationshipsContract, RelatedModel extends LucidModel> extends RelationQueryClientContract<Relation, RelatedModel> {
/**
* Return a query builder instance of the relationship
*/
query<Result = InstanceType<RelatedModel>>(): HasManyThroughQueryBuilderContract<RelatedModel, Result>;
}
/**
* ------------------------------------------------------
* Relationships query builders
* ------------------------------------------------------
*/
/**
* Interface with query builder options for the many to many pivot
* table
*/
interface PivotQueryBuilderContract {
pivotColumns(columns: string[]): this;
wherePivot: WherePivot<this>;
orWherePivot: WherePivot<this>;
andWherePivot: WherePivot<this>;
whereNotPivot: WherePivot<this>;
orWhereNotPivot: WherePivot<this>;
andWhereNotPivot: WherePivot<this>;
whereInPivot: WhereInPivot<this>;
orWhereInPivot: WhereInPivot<this>;
andWhereInPivot: WhereInPivot<this>;
whereNotInPivot: WhereInPivot<this>;
orWhereNotInPivot: WhereInPivot<this>;
andWhereNotInPivot: WhereInPivot<this>;
whereNullPivot: WhereNullPivot<this>;
orWhereNullPivot: WhereNullPivot<this>;
andWhereNullPivot: WhereNullPivot<this>;
whereNotNullPivot: WhereNullPivot<this>;
orWhereNotNullPivot: WhereNullPivot<this>;
andWhereNotNullPivot: WhereNullPivot<this>;
}
/**
* Base query builder for all relations
*/
interface RelationQueryBuilderContract<Related extends LucidModel, Result> extends ModelQueryBuilderContract<Related, Result> {
/**
* Is query a relationship query obtained using `related('relation').query()`
*/
isRelatedQuery: true;
/**
* Is query a relationship query obtained using `related('relation').subQuery()`
*/
isRelatedSubQuery: false;
/**
* Is query a relationship query obtained using one of the preload methods.
*/
isRelatedPreloadQuery: boolean;
selectRelationKeys(): this;
}
/**
* Has many query builder contract
*/
interface HasManyQueryBuilderContract<Related extends LucidModel, Result> extends RelationQueryBuilderContract<Related, Result> {
groupLimit(limit: number): this;
groupOrderBy(column: string, direction?: 'asc' | 'desc'): this;
}
/**
* Has many query through builder contract
*/
interface HasManyThroughQueryBuilderContract<Related extends LucidModel, Result> extends RelationQueryBuilderContract<Related, Result> {
groupLimit(limit: number): this;
groupOrderBy(column: string, direction?: 'asc' | 'desc'): this;
}
/**
* Possible signatures for adding a where clause
*/
interface WherePivot<Builder> {
(key: string, value: StrictValues | ChainableContract): Builder;
(key: string, operator: string, value: StrictValues | ChainableContract): Builder;
}
/**
* Possible signatures for adding whereNull clause.
*/
interface WhereNullPivot<Builder> {
(key: string): Builder;
}
/**
* Possible signatures for adding where in clause.
*/
interface WhereInPivot<Builder> {
(K: string, value: StrictValues[]): Builder;
(K: string[], value: StrictValues[][]): Builder;
(k: string, subquery: ChainableContract | QueryCallback<Builder> | RawBuilderContract | RawQuery): Builder;
(k: string[], subquery: ChainableContract | RawBuilderContract | RawQuery): Builder;
}
/**
* Shape of many to many query builder. It has few methods over the standard
* model query builder
*/
interface ManyToManyQueryBuilderContract<Related extends LucidModel, Result> extends RelationQueryBuilderContract<Related, Result>, PivotQueryBuilderContract {
isPivotOnlyQuery: boolean;
groupLimit(limit: number): this;
groupOrderBy(column: string, direction?: 'asc' | 'desc'): this;
}
/**
* ------------------------------------------------------
* Sub Queries
* ------------------------------------------------------
*/
/**
* Not in use right now. Since after omitting these types from the
* model query builder losses "this" scope. Need to re-think
*/
type UnSupportedSubQueryMethods = 'preload' | 'decrement' | 'increment' | 'update' | 'paginate' | 'delete' | 'del' | 'firstOrFail' | 'first' | 'exec' | 'withCount';
/**
* SubQuery builder allows creating sub queries targeting a relationship. Sub queries
* cannot be executed directly, but can be used as a reference in the parent query
* builder. Use cases are:
*
* - withCount
* - whereHas
*/
interface RelationSubQueryBuilderContract<Related extends LucidModel> extends ModelQueryBuilderContract<Related, any> {
/**
* Is query a relationship query obtained using `related('relation').query()`
*/
isRelatedQuery: false;
/**
* Is query a relationship query obtained using `related('relation').subQuery()`
*/
isRelatedSubQuery: true;
/**
* Is query a relationship query obtained using one of the preload methods.
*/
isRelatedPreloadQuery: false;
selfJoinCounter: number;
readonly selfJoinAlias: string;
selectRelationKeys(): this;
prepare(): this;
}
/**
* SubQuery builder for many to many relationship
*/
interface ManyToManySubQueryBuilderContract<Related extends LucidModel> extends RelationSubQueryBuilderContract<Related>, PivotQueryBuilderContract {
}
/**
* The withCount function
*/
interface WithCount<Model extends LucidRow, Builder> {
<Name extends ExtractModelRelations<Model>, RelatedBuilder = Model[Name] extends ModelRelations ? Model[Name]['subQuery'] : never>(relation: Name, callback?: (builder: RelatedBuilder) => void): Builder;
}
/**
* The with aggregate function
*/
interface WithAggregate<Model extends LucidRow, Builder> {
<Name extends ExtractModelRelations<Model>, RelatedBuilder = Model[Name] extends ModelRelations ? Model[Name]['subQuery'] : never>(relation: Name, callback: (builder: RelatedBuilder) => void): Builder;
}
/**
* The has function
*/
interface Has<Model extends LucidRow, Builder> {
<Name extends ExtractModelRelations<Model>>(relation: Name, operator?: string, value?: StrictValues | ChainableContract): Builder;
}
/**
* The whereHas function
*/
interface WhereHas<Model extends LucidRow, Builder> {
<Name extends ExtractModelRelations<Model>, RelatedBuilder = Model[Name] extends ModelRelations ? Model[Name]['subQuery'] : never>(relation: Name, callback: (builder: RelatedBuilder) => void, operator?: string, value?: StrictValues | ChainableContract): Builder;
}
/**
* ------------------------------------------------------
* Preloader
* ------------------------------------------------------
*/
/**
* The preload function
*/
interface Preload<Model extends LucidRow, Builder> {
<Name extends ExtractModelRelations<Model>, RelatedBuilder = Model[Name] extends ModelRelations ? Model[Name]['builder'] : never>(relation: Name, callback?: (builder: RelatedBuilder) => void): Builder;
}
/**
* Shape of the preloader to preload relationships
*/
interface PreloaderContract<Model extends LucidRow> {
processAllForOne(parent: Model, client: QueryClientContract): Promise<void>;
processAllForMany(parent: Model[], client: QueryClientContract): Promise<void>;
load: Preload<Model, this>;
preload: Preload<Model, this>;
debug(debug: boolean): this;
sideload(values: ModelObject): this;
}
}
+8
View File
@@ -0,0 +1,8 @@
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
+35
View File
@@ -0,0 +1,35 @@
declare module '@ioc:Adonis/Lucid/Schema' {
import { Knex } from 'knex';
import { QueryClientContract, RawQueryBindings } from '@ioc:Adonis/Lucid/Database';
/**
* Shape of callback to defer database calls
*/
export type DeferCallback = (client: QueryClientContract) => void | Promise<void>;
/**
* Shape of schema class constructor
*/
export interface SchemaConstructorContract {
disableTransactions: boolean;
new (db: QueryClientContract, file: string, dryRun: boolean): SchemaContract;
}
/**
* Shape of schema class
*/
export interface SchemaContract {
readonly file: string;
dryRun: boolean;
debug: boolean;
db: QueryClientContract;
schema: Knex.SchemaBuilder;
now(precision?: number): Knex.Raw;
knex(): Knex.QueryBuilder;
raw(sql: string, bindings?: RawQueryBindings): Knex.Raw;
defer: (cb: DeferCallback) => void;
up(): Promise<void> | void;
down(): Promise<void> | void;
execUp(): Promise<string[] | boolean>;
execDown(): Promise<string[] | boolean>;
}
const Schema: SchemaConstructorContract;
export default Schema;
}
+8
View File
@@ -0,0 +1,8 @@
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
+27
View File
@@ -0,0 +1,27 @@
declare module '@ioc:Adonis/Lucid/Seeder' {
import { QueryClientContract, FileNode } from '@ioc:Adonis/Lucid/Database';
/**
* Shape of seeder class
*/
export type SeederConstructorContract = {
/**
* @deprecated
*/
developmentOnly: boolean;
environment: string[];
new (client: QueryClientContract): {
client: QueryClientContract;
run(): Promise<void>;
};
};
/**
* Shape of file node returned by the run method
*/
export type SeederFileNode = {
status: 'pending' | 'completed' | 'failed' | 'ignored';
error?: any;
file: FileNode<unknown>;
};
const BaseSeeder: SeederConstructorContract;
export default BaseSeeder;
}
+8
View File
@@ -0,0 +1,8 @@
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
+11
View File
@@ -0,0 +1,11 @@
declare module '@ioc:Adonis/Core/TestUtils' {
type HookCleanupHandler = () => Promise<void>;
type HookCallback = () => Promise<HookCleanupHandler> | Promise<void>;
interface TestUtilsContract {
db(connectionName?: string): {
seed: HookCallback;
migrate: HookCallback;
truncate: HookCallback;
};
}
}
+8
View File
@@ -0,0 +1,8 @@
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
+24
View File
@@ -0,0 +1,24 @@
/// <reference types="@adonisjs/validator" />
declare module '@ioc:Adonis/Core/Validator' {
import { Rule } from '@ioc:Adonis/Core/Validator';
type DbRowCheckOptions = {
table: string;
column: string;
dateFormat?: string;
connection?: string;
caseInsensitive?: boolean;
constraints?: {
[key: string]: any;
};
where?: {
[key: string]: any;
};
whereNot?: {
[key: string]: any;
};
};
interface Rules {
exists(options: DbRowCheckOptions): Rule;
unique(options: DbRowCheckOptions): Rule;
}
}
+8
View File
@@ -0,0 +1,8 @@
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
+81
View File
@@ -0,0 +1,81 @@
import { BaseCommand } from '@adonisjs/core/build/standalone';
export default class DbSeed extends BaseCommand {
static commandName: string;
static description: string;
static settings: {
loadApp: boolean;
};
private seeder;
/**
* Track if one or more seeders have failed
*/
private hasError;
/**
* Choose a custom pre-defined connection. Otherwise, we use the
* default connection
*/
connection: string;
/**
* Interactive mode allows selecting seeder files
*/
interactive: boolean;
/**
* Define a custom set of seeder files. Interactive and files together ignores
* the interactive mode.
*/
files: string[];
/**
* Display migrations result in one compact single-line output
*/
compactOutput: boolean;
/**
* Print log message to the console
*/
private printLogMessage;
/**
* Not a valid connection
*/
private printNotAValidConnection;
/**
* Print log that the selected seeder file is invalid
*/
private printNotAValidFile;
/**
* Get files cherry picked using either "--interactive" or the
* "--files" flag
*/
private getCherryPickedFiles;
/**
* Instantiate seeders runner
*/
private instantiateSeeder;
/**
* Execute selected seeders
*/
private executedSeeders;
/**
* Print Single-line output when `compact-output` is enabled
*/
private logCompactFinalStatus;
/**
* Run as a subcommand. Never close database connection or exit
* process here
*/
private runAsSubCommand;
/**
* Branching out, so that if required we can implement
* "runAsMain" separately from "runAsSubCommand".
*
* For now, they both are the same
*/
private runAsMain;
/**
* Handle command
*/
run(): Promise<void>;
/**
* Lifecycle method invoked by ace after the "run"
* method.
*/
completed(): Promise<void>;
}
+328
View File
@@ -0,0 +1,328 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
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 __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const slash_1 = __importDefault(require("slash"));
const path_1 = require("path");
const standalone_1 = require("@adonisjs/core/build/standalone");
class DbSeed extends standalone_1.BaseCommand {
constructor() {
super(...arguments);
Object.defineProperty(this, "seeder", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Track if one or more seeders have failed
*/
Object.defineProperty(this, "hasError", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
/**
* Choose a custom pre-defined connection. Otherwise, we use the
* default connection
*/
Object.defineProperty(this, "connection", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Interactive mode allows selecting seeder files
*/
Object.defineProperty(this, "interactive", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Define a custom set of seeder files. Interactive and files together ignores
* the interactive mode.
*/
Object.defineProperty(this, "files", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
/**
* Display migrations result in one compact single-line output
*/
Object.defineProperty(this, "compactOutput", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
}
/**
* Print log message to the console
*/
printLogMessage(file) {
const colors = this['colors'];
let color = 'gray';
let message = '';
let prefix = '';
switch (file.status) {
case 'pending':
message = 'pending ';
color = 'gray';
break;
case 'failed':
message = 'error ';
prefix = file.error.message;
color = 'red';
break;
case 'ignored':
message = 'ignored ';
prefix = 'Enabled only in development environment';
color = 'dim';
break;
case 'completed':
message = 'completed';
color = 'green';
break;
}
console.log(`${colors[color]('')} ${colors[color](message)} ${file.file.name}`);
if (prefix) {
console.log(` ${colors[color](prefix)}`);
}
}
/**
* Not a valid connection
*/
printNotAValidConnection(connection) {
this.logger.error(`"${connection}" is not a valid connection name. Double check "config/database" file`);
}
/**
* Print log that the selected seeder file is invalid
*/
printNotAValidFile(fileName) {
this.printLogMessage({
file: {
name: fileName,
absPath: fileName,
getSource: () => { },
},
status: 'failed',
error: new Error('Invalid file path. Pass relative path from the application root'),
});
}
/**
* Get files cherry picked using either "--interactive" or the
* "--files" flag
*/
async getCherryPickedFiles(seedersFiles) {
if (this.files.length) {
return this.files.map((file) => {
const fileExt = (0, path_1.extname)(file);
return (fileExt ? file.replace(fileExt, '') : file).replace(/^\.\/|^\.\\\\/, '');
});
}
else if (this.interactive) {
return await this.prompt.multiple('Select files to run', seedersFiles.map((file) => {
return { name: file.name };
}));
}
return seedersFiles.map((file) => file.name);
}
/**
* Instantiate seeders runner
*/
async instantiateSeeder() {
const db = this.application.container.use('Adonis/Lucid/Database');
const { SeedsRunner } = await Promise.resolve().then(() => __importStar(require('../src/SeedsRunner')));
this.seeder = new SeedsRunner(db, this.application, this.connection);
}
/**
* Execute selected seeders
*/
async executedSeeders(selectedSeederFiles, files) {
const seedersResults = [];
for (let fileName of selectedSeederFiles) {
const sourceFile = files.find(({ name }) => (0, slash_1.default)(fileName) === (0, slash_1.default)(name));
if (!sourceFile) {
this.printNotAValidFile(fileName);
this.hasError = true;
return;
}
const response = await this.seeder.run(sourceFile);
if (response.status === 'failed') {
this.hasError = true;
}
if (!this.compactOutput) {
this.printLogMessage(response);
}
seedersResults.push(response);
}
return seedersResults;
}
/**
* Print Single-line output when `compact-output` is enabled
*/
logCompactFinalStatus(seedersResults) {
const countByStatus = seedersResults.reduce((acc, value) => {
acc[value.status] = acc[value.status] + 1;
return acc;
}, { completed: 0, failed: 0, ignored: 0, pending: 0 });
let message = ` Executed ${countByStatus.completed} seeders`;
if (countByStatus.failed) {
message += `, ${countByStatus.failed} failed`;
}
if (countByStatus.ignored) {
message += `, ${countByStatus.ignored} ignored`;
}
const color = countByStatus.failed ? 'red' : 'grey';
this.logger.log(this.colors[color](message));
if (countByStatus.failed > 0) {
const erroredSeeder = seedersResults.find((seeder) => seeder.status === 'failed');
const seederName = this.colors.grey(erroredSeeder.file.name + ':');
const error = this.colors.red(erroredSeeder.error.message);
this.logger.log(`${seederName} ${error}\n`);
}
}
/**
* Run as a subcommand. Never close database connection or exit
* process here
*/
async runAsSubCommand() {
const db = this.application.container.use('Adonis/Lucid/Database');
this.connection = this.connection || db.primaryConnectionName;
/**
* Invalid database connection
*/
if (!db.manager.has(this.connection)) {
this.printNotAValidConnection(this.connection);
this.exitCode = 1;
return;
}
/**
* Cannot use --files and --interactive together
*/
if (this.files && this.interactive) {
this.logger.warning('Cannot use "--interactive" and "--files" together. Ignoring "--interactive"');
}
await this.instantiateSeeder();
const files = await this.seeder.getList();
const cherryPickedFiles = await this.getCherryPickedFiles(files);
const result = await this.executedSeeders(cherryPickedFiles, files);
if (this.compactOutput && result) {
this.logCompactFinalStatus(result);
}
this.exitCode = this.hasError ? 1 : 0;
}
/**
* Branching out, so that if required we can implement
* "runAsMain" separately from "runAsSubCommand".
*
* For now, they both are the same
*/
async runAsMain() {
await this.runAsSubCommand();
}
/**
* Handle command
*/
async run() {
if (this.isMain) {
await this.runAsMain();
}
else {
await this.runAsSubCommand();
}
}
/**
* Lifecycle method invoked by ace after the "run"
* method.
*/
async completed() {
if (this.seeder && this.isMain) {
await this.seeder.close();
}
}
}
Object.defineProperty(DbSeed, "commandName", {
enumerable: true,
configurable: true,
writable: true,
value: 'db:seed'
});
Object.defineProperty(DbSeed, "description", {
enumerable: true,
configurable: true,
writable: true,
value: 'Execute database seeders'
});
Object.defineProperty(DbSeed, "settings", {
enumerable: true,
configurable: true,
writable: true,
value: {
loadApp: true,
}
});
__decorate([
standalone_1.flags.string({ description: 'Define a custom database connection for the seeders', alias: 'c' }),
__metadata("design:type", String)
], DbSeed.prototype, "connection", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Run seeders in interactive mode', alias: 'i' }),
__metadata("design:type", Boolean)
], DbSeed.prototype, "interactive", void 0);
__decorate([
standalone_1.flags.array({ description: 'Define a custom set of seeders files names to run', alias: 'f' }),
__metadata("design:type", Array)
], DbSeed.prototype, "files", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'A compact single-line output' }),
__metadata("design:type", Boolean)
], DbSeed.prototype, "compactOutput", void 0);
exports.default = DbSeed;
+50
View File
@@ -0,0 +1,50 @@
import { BaseCommand } from '@adonisjs/core/build/standalone';
export default class DbTruncate extends BaseCommand {
static commandName: string;
static description: string;
static settings: {
loadApp: boolean;
};
/**
* Choose a custom pre-defined connection. Otherwise, we use the
* default connection
*/
connection: string;
/**
* Force command execution in production
*/
force: boolean;
/**
* Not a valid connection
*/
private printNotAValidConnection;
/**
* Prompts to take consent when truncating the database in production
*/
private takeProductionConstent;
/**
* Truncate all tables except adonis migrations table
*/
private performTruncate;
/**
* Run as a subcommand. Never close database connections or exit
* process inside this method
*/
private runAsSubCommand;
/**
* Branching out, so that if required we can implement
* "runAsMain" separately from "runAsSubCommand".
*
* For now, they both are the same
*/
private runAsMain;
/**
* Handle command
*/
run(): Promise<void>;
/**
* Lifecycle method invoked by ace after the "run"
* method.
*/
completed(): Promise<void>;
}
+167
View File
@@ -0,0 +1,167 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* 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);
};
Object.defineProperty(exports, "__esModule", { value: true });
const standalone_1 = require("@adonisjs/core/build/standalone");
class DbTruncate extends standalone_1.BaseCommand {
constructor() {
super(...arguments);
/**
* Choose a custom pre-defined connection. Otherwise, we use the
* default connection
*/
Object.defineProperty(this, "connection", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Force command execution in production
*/
Object.defineProperty(this, "force", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
/**
* Not a valid connection
*/
printNotAValidConnection(connection) {
this.logger.error(`"${connection}" is not a valid connection name. Double check "config/database" file`);
}
/**
* Prompts to take consent when truncating the database in production
*/
async takeProductionConstent() {
/**
* Do not prompt when CLI is not interactive
*/
if (!this.isInteractive) {
return false;
}
const question = 'You are in production environment. Want to continue truncating the database?';
try {
return await this.prompt.confirm(question);
}
catch (error) {
return false;
}
}
/**
* Truncate all tables except adonis migrations table
*/
async performTruncate(client) {
let tables = await client.getAllTables(['public']);
tables = tables.filter((table) => !['adonis_schema', 'adonis_schema_versions'].includes(table));
await Promise.all(tables.map((table) => client.truncate(table, true)));
this.logger.success('Truncated tables successfully');
}
/**
* Run as a subcommand. Never close database connections or exit
* process inside this method
*/
async runAsSubCommand() {
const db = this.application.container.use('Adonis/Lucid/Database');
this.connection = this.connection || db.primaryConnectionName;
const connection = db.connection(this.connection || db.primaryConnectionName);
/**
* Continue with clearing the database when not in production
* or force flag is passed
*/
let continueTruncate = !this.application.inProduction || this.force;
if (!continueTruncate) {
continueTruncate = await this.takeProductionConstent();
}
/**
* Do not continue when in prod and the prompt was cancelled
*/
if (!continueTruncate) {
return;
}
/**
* Invalid database connection
*/
if (!db.manager.has(this.connection)) {
this.printNotAValidConnection(this.connection);
this.exitCode = 1;
return;
}
await this.performTruncate(connection);
}
/**
* Branching out, so that if required we can implement
* "runAsMain" separately from "runAsSubCommand".
*
* For now, they both are the same
*/
async runAsMain() {
await this.runAsSubCommand();
}
/**
* Handle command
*/
async run() {
if (this.isMain) {
await this.runAsMain();
}
else {
await this.runAsSubCommand();
}
}
/**
* Lifecycle method invoked by ace after the "run"
* method.
*/
async completed() {
if (this.isMain) {
await this.application.container.use('Adonis/Lucid/Database').manager.closeAll(true);
}
}
}
Object.defineProperty(DbTruncate, "commandName", {
enumerable: true,
configurable: true,
writable: true,
value: 'db:truncate'
});
Object.defineProperty(DbTruncate, "description", {
enumerable: true,
configurable: true,
writable: true,
value: 'Truncate all tables in database'
});
Object.defineProperty(DbTruncate, "settings", {
enumerable: true,
configurable: true,
writable: true,
value: {
loadApp: true,
}
});
__decorate([
standalone_1.flags.string({ description: 'Define a custom database connection', alias: 'c' }),
__metadata("design:type", String)
], DbTruncate.prototype, "connection", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Explicitly force command to run in production' }),
__metadata("design:type", Boolean)
], DbTruncate.prototype, "force", void 0);
exports.default = DbTruncate;
+66
View File
@@ -0,0 +1,66 @@
import { BaseCommand } from '@adonisjs/core/build/standalone';
export default class DbWipe extends BaseCommand {
static commandName: string;
static description: string;
static settings: {
loadApp: boolean;
};
/**
* Choose a custom pre-defined connection. Otherwise, we use the
* default connection
*/
connection: string;
/**
* Drop all views in database
*/
dropViews: boolean;
/**
* Drop all types in database
*/
dropTypes: boolean;
/**
* Force command execution in production
*/
force: boolean;
/**
* Not a valid connection
*/
private printNotAValidConnection;
/**
* Prompts to take consent when wiping the database in production
*/
private takeProductionConstent;
/**
* Drop all views (if asked for and supported)
*/
private performDropViews;
/**
* Drop all tables
*/
private performDropTables;
/**
* Drop all types (if asked for and supported)
*/
private performDropTypes;
/**
* Run as a subcommand. Never close database connections or exit
* process inside this method
*/
private runAsSubCommand;
/**
* Branching out, so that if required we can implement
* "runAsMain" separately from "runAsSubCommand".
*
* For now, they both are the same
*/
private runAsMain;
/**
* Handle command
*/
run(): Promise<void>;
/**
* Lifecycle method invoked by ace after the "run"
* method.
*/
completed(): Promise<void>;
}
+219
View File
@@ -0,0 +1,219 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* 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);
};
Object.defineProperty(exports, "__esModule", { value: true });
const standalone_1 = require("@adonisjs/core/build/standalone");
class DbWipe extends standalone_1.BaseCommand {
constructor() {
super(...arguments);
/**
* Choose a custom pre-defined connection. Otherwise, we use the
* default connection
*/
Object.defineProperty(this, "connection", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Drop all views in database
*/
Object.defineProperty(this, "dropViews", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Drop all types in database
*/
Object.defineProperty(this, "dropTypes", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Force command execution in production
*/
Object.defineProperty(this, "force", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
/**
* Not a valid connection
*/
printNotAValidConnection(connection) {
this.logger.error(`"${connection}" is not a valid connection name. Double check "config/database" file`);
}
/**
* Prompts to take consent when wiping the database in production
*/
async takeProductionConstent() {
/**
* Do not prompt when CLI is not interactive
*/
if (!this.isInteractive) {
return false;
}
const question = 'You are in production environment. Want to continue wiping the database?';
try {
return await this.prompt.confirm(question);
}
catch (error) {
return false;
}
}
/**
* Drop all views (if asked for and supported)
*/
async performDropViews(client) {
if (!this.dropViews) {
return;
}
if (!client.dialect.supportsViews) {
this.logger.warning(`Dropping views is not supported by "${client.dialect.name}"`);
}
await client.dropAllViews();
this.logger.success('Dropped views successfully');
}
/**
* Drop all tables
*/
async performDropTables(client) {
await client.dropAllTables();
this.logger.success('Dropped tables successfully');
}
/**
* Drop all types (if asked for and supported)
*/
async performDropTypes(client) {
if (!this.dropTypes) {
return;
}
if (!client.dialect.supportsTypes) {
this.logger.warning(`Dropping types is not supported by "${client.dialect.name}"`);
}
await client.dropAllTypes();
this.logger.success('Dropped types successfully');
}
/**
* Run as a subcommand. Never close database connections or exit
* process inside this method
*/
async runAsSubCommand() {
const db = this.application.container.use('Adonis/Lucid/Database');
this.connection = this.connection || db.primaryConnectionName;
const connection = db.connection(this.connection || db.primaryConnectionName);
/**
* Continue with clearing the database when not in production
* or force flag is passed
*/
let continueWipe = !this.application.inProduction || this.force;
if (!continueWipe) {
continueWipe = await this.takeProductionConstent();
}
/**
* Do not continue when in prod and the prompt was cancelled
*/
if (!continueWipe) {
return;
}
/**
* Invalid database connection
*/
if (!db.manager.has(this.connection)) {
this.printNotAValidConnection(this.connection);
this.exitCode = 1;
return;
}
await this.performDropViews(connection);
await this.performDropTables(connection);
await this.performDropTypes(connection);
}
/**
* Branching out, so that if required we can implement
* "runAsMain" separately from "runAsSubCommand".
*
* For now, they both are the same
*/
async runAsMain() {
await this.runAsSubCommand();
}
/**
* Handle command
*/
async run() {
if (this.isMain) {
await this.runAsMain();
}
else {
await this.runAsSubCommand();
}
}
/**
* Lifecycle method invoked by ace after the "run"
* method.
*/
async completed() {
if (this.isMain) {
await this.application.container.use('Adonis/Lucid/Database').manager.closeAll(true);
}
}
}
Object.defineProperty(DbWipe, "commandName", {
enumerable: true,
configurable: true,
writable: true,
value: 'db:wipe'
});
Object.defineProperty(DbWipe, "description", {
enumerable: true,
configurable: true,
writable: true,
value: 'Drop all tables, views and types in database'
});
Object.defineProperty(DbWipe, "settings", {
enumerable: true,
configurable: true,
writable: true,
value: {
loadApp: true,
}
});
__decorate([
standalone_1.flags.string({ description: 'Define a custom database connection', alias: 'c' }),
__metadata("design:type", String)
], DbWipe.prototype, "connection", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Drop all views' }),
__metadata("design:type", Boolean)
], DbWipe.prototype, "dropViews", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Drop all custom types (Postgres only)' }),
__metadata("design:type", Boolean)
], DbWipe.prototype, "dropTypes", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Explicitly force command to run in production' }),
__metadata("design:type", Boolean)
], DbWipe.prototype, "force", void 0);
exports.default = DbWipe;
+34
View File
@@ -0,0 +1,34 @@
import { BaseCommand } from '@adonisjs/core/build/standalone';
/**
* Command to make a new Factory
*/
export default class MakeFactory extends BaseCommand {
static commandName: string;
static description: string;
/**
* Name of the model to be used in the factory
*/
model: string;
/**
* Import path to the model used in the factory
*/
modelPath: string;
exact: boolean;
/**
* Generate model import path used in the factory
*/
private generateModelImportPath;
/**
* Path to the factories directory
*/
protected getDestinationPath(): string;
/**
* Passed down to the stub template
*/
protected templateData(): {
model: string;
modelImportPath: string;
toModelName: () => (model: string, render: any) => any;
};
run(): Promise<void>;
}
+133
View File
@@ -0,0 +1,133 @@
"use strict";
/*
* @adonisjs/assembler
*
* (c) AdonisJS
*
* 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);
};
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = require("path");
const standalone_1 = require("@adonisjs/core/build/standalone");
/**
* Command to make a new Factory
*/
class MakeFactory extends standalone_1.BaseCommand {
constructor() {
super(...arguments);
/**
* Name of the model to be used in the factory
*/
Object.defineProperty(this, "model", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Import path to the model used in the factory
*/
Object.defineProperty(this, "modelPath", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "exact", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
/**
* Generate model import path used in the factory
*/
generateModelImportPath() {
let base = this.application.rcFile.namespaces.models || 'App/Models';
if (!base.endsWith('/')) {
base += '/';
}
let importPath = this.model;
if (this.modelPath) {
importPath = this.modelPath;
}
else if (importPath.endsWith('Factory')) {
importPath = importPath.replace(/Factory$/, '');
}
if (importPath.startsWith(base)) {
return importPath;
}
return base + importPath;
}
/**
* Path to the factories directory
*/
getDestinationPath() {
const base = this.application.rcFile.directories.database || 'database';
return (0, path_1.join)(base, 'factories');
}
/**
* Passed down to the stub template
*/
templateData() {
return {
model: this.model,
modelImportPath: this.generateModelImportPath(),
toModelName: () => {
return function (model, render) {
return render(model).split('/').pop();
};
},
};
}
async run() {
const stub = (0, path_1.join)(__dirname, '..', 'templates', 'factory.txt');
this.generator
.addFile(this.model, { pattern: 'pascalcase', form: 'singular', suffix: 'Factory' })
.stub(stub)
.useMustache()
.destinationDir(this.getDestinationPath())
.appRoot(this.application.appRoot)
.apply(this.templateData());
await this.generator.run();
}
}
Object.defineProperty(MakeFactory, "commandName", {
enumerable: true,
configurable: true,
writable: true,
value: 'make:factory'
});
Object.defineProperty(MakeFactory, "description", {
enumerable: true,
configurable: true,
writable: true,
value: 'Make a new factory'
});
__decorate([
standalone_1.args.string({ description: 'The name of the model' }),
__metadata("design:type", String)
], MakeFactory.prototype, "model", void 0);
__decorate([
standalone_1.flags.string({ description: 'The path to the model' }),
__metadata("design:type", String)
], MakeFactory.prototype, "modelPath", void 0);
__decorate([
standalone_1.flags.boolean({
description: 'Create the factory with the exact name as provided',
alias: 'e',
}),
__metadata("design:type", Boolean)
], MakeFactory.prototype, "exact", void 0);
exports.default = MakeFactory;
+43
View File
@@ -0,0 +1,43 @@
import { BaseCommand } from '@adonisjs/core/build/standalone';
export default class MakeMigration extends BaseCommand {
static commandName: string;
static description: string;
static settings: {
loadApp: boolean;
};
/**
* The name of the migration file. We use this to create the migration
* file and generate the table name
*/
name: string;
/**
* Choose a custom pre-defined connection. Otherwise, we use the
* default connection
*/
connection: string;
/**
* Pre select migration directory. If this is defined, we will ignore the paths
* defined inside the config.
*/
folder: string;
/**
* Custom table name for creating a new table
*/
create: string;
/**
* Custom table name for altering an existing table
*/
table: string;
/**
* Not a valid connection
*/
private printNotAValidConnection;
/**
* Returns the directory for creating the migration file
*/
private getDirectory;
/**
* Execute command
*/
run(): Promise<void>;
}
+198
View File
@@ -0,0 +1,198 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* 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);
};
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = require("path");
const helpers_1 = require("@poppinss/utils/build/helpers");
const standalone_1 = require("@adonisjs/core/build/standalone");
class MakeMigration extends standalone_1.BaseCommand {
constructor() {
super(...arguments);
/**
* The name of the migration file. We use this to create the migration
* file and generate the table name
*/
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Choose a custom pre-defined connection. Otherwise, we use the
* default connection
*/
Object.defineProperty(this, "connection", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Pre select migration directory. If this is defined, we will ignore the paths
* defined inside the config.
*/
Object.defineProperty(this, "folder", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Custom table name for creating a new table
*/
Object.defineProperty(this, "create", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Custom table name for altering an existing table
*/
Object.defineProperty(this, "table", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
/**
* Not a valid connection
*/
printNotAValidConnection(connection) {
this.logger.error(`"${connection}" is not a valid connection name. Double check "config/database" file`);
}
/**
* Returns the directory for creating the migration file
*/
async getDirectory(migrationPaths) {
if (this.folder) {
return this.folder;
}
let directories = migrationPaths?.length ? migrationPaths : ['database/migrations'];
if (directories.length === 1) {
return directories[0];
}
return this.prompt.choice('Select the migrations folder', directories, { name: 'folder' });
}
/**
* Execute command
*/
async run() {
const db = this.application.container.use('Adonis/Lucid/Database');
this.connection = this.connection || db.primaryConnectionName;
const connection = db.getRawConnection(this.connection || db.primaryConnectionName);
/**
* Invalid database connection
*/
if (!connection) {
this.printNotAValidConnection(this.connection);
this.exitCode = 1;
return;
}
/**
* Not allowed together, hence we must notify the user about the same
*/
if (this.table && this.create) {
this.logger.warning('--table and --create cannot be used together. Ignoring --create');
}
/**
* The folder for creating the schema file
*/
const folder = await this.getDirectory((connection.config.migrations || {}).paths);
/**
* Using the user defined table name or an empty string. We can attempt to
* build the table name from the migration file name, but let's do that
* later.
*/
const tableName = this.table || this.create || '';
/**
* Template stub
*/
const stub = (0, path_1.join)(__dirname, '..', 'templates', this.table ? 'migration-alter.txt' : 'migration-make.txt');
/**
* Prepend timestamp to keep schema files in the order they
* have been created
*/
const prefix = `${new Date().getTime()}_`;
this.generator
.addFile(this.name, { pattern: 'snakecase', form: 'plural', prefix })
.stub(stub)
.destinationDir(folder)
.appRoot(this.application.cliCwd || this.application.appRoot)
.useMustache()
.apply({
toClassName() {
return function (filename, render) {
const migrationClassName = helpers_1.string.camelCase(tableName || render(filename).replace(prefix, ''));
return `${migrationClassName.charAt(0).toUpperCase()}${migrationClassName.slice(1)}`;
};
},
toTableName() {
return function (filename, render) {
return tableName || helpers_1.string.snakeCase(render(filename).replace(prefix, ''));
};
},
});
await this.generator.run();
}
}
Object.defineProperty(MakeMigration, "commandName", {
enumerable: true,
configurable: true,
writable: true,
value: 'make:migration'
});
Object.defineProperty(MakeMigration, "description", {
enumerable: true,
configurable: true,
writable: true,
value: 'Make a new migration file'
});
Object.defineProperty(MakeMigration, "settings", {
enumerable: true,
configurable: true,
writable: true,
value: {
loadApp: true,
}
});
__decorate([
standalone_1.args.string({ description: 'Name of the migration file' }),
__metadata("design:type", String)
], MakeMigration.prototype, "name", void 0);
__decorate([
standalone_1.flags.string({
description: 'The connection flag is used to lookup the directory for the migration file',
}),
__metadata("design:type", String)
], MakeMigration.prototype, "connection", void 0);
__decorate([
standalone_1.flags.string({ description: 'Pre-select a migration directory' }),
__metadata("design:type", String)
], MakeMigration.prototype, "folder", void 0);
__decorate([
standalone_1.flags.string({ description: 'Define the table name for creating a new table' }),
__metadata("design:type", String)
], MakeMigration.prototype, "create", void 0);
__decorate([
standalone_1.flags.string({ description: 'Define the table name for altering an existing table' }),
__metadata("design:type", String)
], MakeMigration.prototype, "table", void 0);
exports.default = MakeMigration;
+40
View File
@@ -0,0 +1,40 @@
import { BaseCommand } from '@adonisjs/core/build/standalone';
export default class MakeModel extends BaseCommand {
static commandName: string;
static description: string;
static settings: {
loadApp: boolean;
};
/**
* The name of the model file.
*/
name: string;
/**
* Defines if we generate the migration for the model.
*/
migration: boolean;
/**
* Defines if we generate the controller for the model.
*/
controller: boolean;
/**
* Defines if we generate the factory for the model.
*/
factory: boolean;
/**
* Run migrations
*/
private runMakeMigration;
/**
* Make controller
*/
private runMakeController;
/**
* Make factory
*/
private runMakeFactory;
/**
* Execute command
*/
run(): Promise<void>;
}
+164
View File
@@ -0,0 +1,164 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* 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);
};
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = require("path");
const standalone_1 = require("@adonisjs/core/build/standalone");
class MakeModel extends standalone_1.BaseCommand {
constructor() {
super(...arguments);
/**
* The name of the model file.
*/
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Defines if we generate the migration for the model.
*/
Object.defineProperty(this, "migration", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Defines if we generate the controller for the model.
*/
Object.defineProperty(this, "controller", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Defines if we generate the factory for the model.
*/
Object.defineProperty(this, "factory", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
/**
* Run migrations
*/
async runMakeMigration() {
if (!this.migration) {
return;
}
const makeMigration = await this.kernel.exec('make:migration', [this.name]);
this.exitCode = makeMigration.exitCode;
this.error = makeMigration.error;
}
/**
* Make controller
*/
async runMakeController() {
if (!this.controller) {
return;
}
const makeController = await this.kernel.exec('make:controller', [this.name]);
this.exitCode = makeController.exitCode;
this.error = makeController.error;
}
/**
* Make factory
*/
async runMakeFactory() {
if (!this.factory) {
return;
}
const makeFactory = await this.kernel.exec('make:factory', [this.name]);
this.exitCode = makeFactory.exitCode;
this.error = makeFactory.error;
}
/**
* Execute command
*/
async run() {
const stub = (0, path_1.join)(__dirname, '..', 'templates', 'model.txt');
const path = this.application.resolveNamespaceDirectory('models');
this.generator
.addFile(this.name, { pattern: 'pascalcase', form: 'singular' })
.stub(stub)
.destinationDir(path || 'app/Models')
.useMustache()
.appRoot(this.application.cliCwd || this.application.appRoot);
await this.generator.run();
await this.runMakeMigration();
if (this.exitCode) {
return;
}
await this.runMakeController();
await this.runMakeFactory();
}
}
Object.defineProperty(MakeModel, "commandName", {
enumerable: true,
configurable: true,
writable: true,
value: 'make:model'
});
Object.defineProperty(MakeModel, "description", {
enumerable: true,
configurable: true,
writable: true,
value: 'Make a new Lucid model'
});
Object.defineProperty(MakeModel, "settings", {
enumerable: true,
configurable: true,
writable: true,
value: {
loadApp: true,
}
});
__decorate([
standalone_1.args.string({ description: 'Name of the model class' }),
__metadata("design:type", String)
], MakeModel.prototype, "name", void 0);
__decorate([
standalone_1.flags.boolean({
name: 'migration',
alias: 'm',
description: 'Generate the migration for the model',
}),
__metadata("design:type", Boolean)
], MakeModel.prototype, "migration", void 0);
__decorate([
standalone_1.flags.boolean({
name: 'controller',
alias: 'c',
description: 'Generate the controller for the model',
}),
__metadata("design:type", Boolean)
], MakeModel.prototype, "controller", void 0);
__decorate([
standalone_1.flags.boolean({
name: 'factory',
alias: 'f',
description: 'Generate a factory for the model',
}),
__metadata("design:type", Boolean)
], MakeModel.prototype, "factory", void 0);
exports.default = MakeModel;
+13
View File
@@ -0,0 +1,13 @@
import { BaseCommand } from '@adonisjs/core/build/standalone';
export default class MakeSeeder extends BaseCommand {
static commandName: string;
static description: string;
/**
* The name of the seeder file.
*/
name: string;
/**
* Execute command
*/
run(): Promise<void>;
}
+66
View File
@@ -0,0 +1,66 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* 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);
};
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = require("path");
const standalone_1 = require("@adonisjs/core/build/standalone");
class MakeSeeder extends standalone_1.BaseCommand {
constructor() {
super(...arguments);
/**
* The name of the seeder file.
*/
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
/**
* Execute command
*/
async run() {
const stub = (0, path_1.join)(__dirname, '..', 'templates', 'seeder.txt');
const path = this.application.rcFile.directories.seeds;
this.generator
.addFile(this.name, { pattern: 'pascalcase', form: 'singular' })
.stub(stub)
.destinationDir(path || 'database/Seeders')
.useMustache()
.appRoot(this.application.cliCwd || this.application.appRoot);
await this.generator.run();
}
}
Object.defineProperty(MakeSeeder, "commandName", {
enumerable: true,
configurable: true,
writable: true,
value: 'make:seeder'
});
Object.defineProperty(MakeSeeder, "description", {
enumerable: true,
configurable: true,
writable: true,
value: 'Make a new Seeder file'
});
__decorate([
standalone_1.args.string({ description: 'Name of the seeder class' }),
__metadata("design:type", String)
], MakeSeeder.prototype, "name", void 0);
exports.default = MakeSeeder;
+39
View File
@@ -0,0 +1,39 @@
import { BaseCommand } from '@adonisjs/core/build/standalone';
import { MigratedFileNode, MigratorContract } from '@ioc:Adonis/Lucid/Migrator';
/**
* Base class to execute migrations and print logs
*/
export default abstract class MigrationsBase extends BaseCommand {
/**
* Should print one-liner compact output
*/
protected compactOutput: boolean;
/**
* Not a valid connection
*/
protected printNotAValidConnection(connection: string): void;
/**
* Prompts to take consent for running migrations in production
*/
protected takeProductionConstent(): Promise<boolean>;
/**
* Returns beautified log message string
*/
protected printLogMessage(file: MigratedFileNode, direction: 'down' | 'up'): void;
/**
* Pretty print sql queries of a file
*/
private prettyPrintSql;
/**
* Log final status with verbose output
*/
private logVerboseFinalStatus;
/**
* Log final status with compact output
*/
private logCompactFinalStatus;
/**
* Runs the migrations using the migrator
*/
protected runMigrations(migrator: MigratorContract, connectionName: string): Promise<void>;
}
+214
View File
@@ -0,0 +1,214 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const pretty_hrtime_1 = __importDefault(require("pretty-hrtime"));
const standalone_1 = require("@adonisjs/core/build/standalone");
const utils_1 = require("../../src/utils");
const prettyPrint_1 = require("../../src/Helpers/prettyPrint");
/**
* Base class to execute migrations and print logs
*/
class MigrationsBase extends standalone_1.BaseCommand {
constructor() {
super(...arguments);
/**
* Should print one-liner compact output
*/
Object.defineProperty(this, "compactOutput", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
}
/**
* Not a valid connection
*/
printNotAValidConnection(connection) {
this.logger.error(`"${connection}" is not a valid connection name. Double check "config/database" file`);
}
/**
* Prompts to take consent for running migrations in production
*/
async takeProductionConstent() {
/**
* Do not prompt when CLI is not interactive
*/
if (!this.isInteractive) {
return false;
}
const question = 'You are in production environment. Want to continue running migrations?';
try {
return await this.prompt.confirm(question);
}
catch (error) {
return false;
}
}
/**
* Returns beautified log message string
*/
printLogMessage(file, direction) {
const color = file.status === 'pending' ? 'gray' : file.status === 'completed' ? 'green' : 'red';
const arrow = this.colors[color]('');
const message = file.status === 'pending'
? direction === 'up'
? 'migrating'
: 'reverting'
: file.status === 'completed'
? direction === 'up'
? 'migrated'
: 'reverted'
: 'error';
this.logger.logUpdate(`${arrow} ${this.colors[color](message)} ${file.file.name}`);
}
/**
* Pretty print sql queries of a file
*/
prettyPrintSql(file, connectionName) {
console.log(this.logger.colors.gray(`------------- ${file.file.name} -------------`));
console.log();
file.queries.map((sql) => {
(0, prettyPrint_1.prettyPrint)({
connection: connectionName,
sql: sql,
ddl: true,
method: (0, utils_1.getDDLMethod)(sql),
bindings: [],
});
console.log();
});
console.log(this.logger.colors.gray('------------- END -------------'));
}
/**
* Log final status with verbose output
*/
logVerboseFinalStatus(migrator, duration) {
switch (migrator.status) {
case 'completed':
const completionMessage = migrator.direction === 'up' ? 'Migrated in' : 'Reverted in';
console.log(`\n${completionMessage} ${this.colors.cyan((0, pretty_hrtime_1.default)(duration))}`);
break;
case 'skipped':
const message = migrator.direction === 'up' ? 'Already up to date' : 'Already at latest batch';
console.log(this.colors.cyan(message));
break;
case 'error':
this.logger.fatal(migrator.error);
this.exitCode = 1;
break;
}
}
/**
* Log final status with compact output
*/
logCompactFinalStatus(processedFiles, migrator, duration) {
let output = '';
let message = '';
let isUp = migrator.direction === 'up';
switch (migrator.status) {
case 'completed':
message = ` ${isUp ? 'Executed' : 'Reverted'} ${processedFiles.size} migrations`;
output = this.colors.grey(message + ` (${(0, pretty_hrtime_1.default)(duration)})`);
break;
case 'skipped':
message = ` ${isUp ? 'Already up to date' : 'Already at latest batch'}`;
output = this.colors.grey(message);
break;
case 'error':
const skippedMigrations = Object.values(migrator.migratedFiles).filter((file) => file.status === 'pending').length;
message = ` Executed ${processedFiles.size} migrations, 1 error, ${skippedMigrations} skipped`;
console.log(this.colors.red(message));
console.log('\n' + this.colors.red(migrator.error.message));
this.exitCode = 1;
break;
}
this.logger.log(output);
}
/**
* Runs the migrations using the migrator
*/
async runMigrations(migrator, connectionName) {
/**
* Pretty print SQL in dry run and return early
*/
if (migrator.dryRun) {
await migrator.run();
Object.keys(migrator.migratedFiles).forEach((file) => {
this.prettyPrintSql(migrator.migratedFiles[file], connectionName);
});
return;
}
/**
* A set of files processed and emitted using event emitter.
*/
const processedFiles = new Set();
let start;
let duration;
/**
* Starting to process a new migration file
*/
migrator.on('migration:start', (file) => {
processedFiles.add(file.file.name);
if (!this.compactOutput) {
this.printLogMessage(file, migrator.direction);
}
});
/**
* Migration completed
*/
migrator.on('migration:completed', (file) => {
if (!this.compactOutput) {
this.printLogMessage(file, migrator.direction);
this.logger.logUpdatePersist();
}
});
/**
* Migration error
*/
migrator.on('migration:error', (file) => {
if (!this.compactOutput) {
this.printLogMessage(file, migrator.direction);
this.logger.logUpdatePersist();
}
});
/**
* Migration completed
*/
migrator.on('upgrade:version', ({ from, to }) => {
this.logger.info(`Upgrading migrations version from "${from}" to "${to}"`);
});
migrator.on('start', () => (start = process.hrtime()));
migrator.on('end', () => (duration = process.hrtime(start)));
/**
* Run migrations
*/
await migrator.run();
/**
* Log all pending files. This will happen, when one of the migration
* fails with an error and then the migrator stops emitting events.
*/
Object.keys(migrator.migratedFiles).forEach((file) => {
if (!processedFiles.has(file) && !this.compactOutput) {
this.printLogMessage(migrator.migratedFiles[file], migrator.direction);
}
});
if (this.compactOutput) {
this.logCompactFinalStatus(processedFiles, migrator, duration);
}
else {
this.logVerboseFinalStatus(migrator, duration);
}
}
}
exports.default = MigrationsBase;
+60
View File
@@ -0,0 +1,60 @@
import { BaseCommand } from '@adonisjs/core/build/standalone';
/**
* This command reset the database by rolling back to batch 0 and then
* re-run all migrations.
*/
export default class Refresh extends BaseCommand {
static commandName: string;
static description: string;
static settings: {
loadApp: boolean;
};
/**
* Custom connection for running migrations.
*/
connection: string;
/**
* Force command execution in production
*/
force: boolean;
/**
* Run seeders
*/
seed: boolean;
/**
* Drop all views in database
*/
dropViews: boolean;
/**
* Drop all types in database
*/
dropTypes: boolean;
/**
* Disable advisory locks
*/
disableLocks: boolean;
/**
* Converting command properties to arguments
*/
private getArgs;
/**
* Converting command properties to db:wipe arguments
*/
private getWipeArgs;
/**
* Wipe the database
*/
private runDbWipe;
/**
* Run migrations
*/
private runMigrations;
/**
* Run seeders
*/
private runDbSeed;
/**
* Handle command
*/
run(): Promise<void>;
}
+201
View File
@@ -0,0 +1,201 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* 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);
};
Object.defineProperty(exports, "__esModule", { value: true });
const standalone_1 = require("@adonisjs/core/build/standalone");
/**
* This command reset the database by rolling back to batch 0 and then
* re-run all migrations.
*/
class Refresh extends standalone_1.BaseCommand {
constructor() {
super(...arguments);
/**
* Custom connection for running migrations.
*/
Object.defineProperty(this, "connection", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Force command execution in production
*/
Object.defineProperty(this, "force", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Run seeders
*/
Object.defineProperty(this, "seed", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Drop all views in database
*/
Object.defineProperty(this, "dropViews", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Drop all types in database
*/
Object.defineProperty(this, "dropTypes", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Disable advisory locks
*/
Object.defineProperty(this, "disableLocks", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
/**
* Converting command properties to arguments
*/
getArgs() {
const args = [];
if (this.force) {
args.push('--force');
}
if (this.connection) {
args.push(`--connection="${this.connection}"`);
}
if (this.disableLocks) {
args.push('--disable-locks');
}
return args;
}
/**
* Converting command properties to db:wipe arguments
*/
getWipeArgs() {
const args = this.getArgs();
if (this.dropTypes) {
args.push('--drop-types');
}
if (this.dropViews) {
args.push('--drop-views');
}
return args;
}
/**
* Wipe the database
*/
async runDbWipe() {
const dbWipe = await this.kernel.exec('db:wipe', this.getWipeArgs());
this.exitCode = dbWipe.exitCode;
this.error = dbWipe.error;
}
/**
* Run migrations
*/
async runMigrations() {
const migrate = await this.kernel.exec('migration:run', this.getArgs());
this.exitCode = migrate.exitCode;
this.error = migrate.error;
}
/**
* Run seeders
*/
async runDbSeed() {
const args = [];
if (this.connection) {
args.push(`--connection="${this.connection}"`);
}
const dbSeed = await this.kernel.exec('db:seed', args);
this.exitCode = dbSeed.exitCode;
this.error = dbSeed.error;
}
/**
* Handle command
*/
async run() {
await this.runDbWipe();
if (this.exitCode) {
return;
}
await this.runMigrations();
if (this.exitCode) {
return;
}
if (this.seed) {
await this.runDbSeed();
}
}
}
Object.defineProperty(Refresh, "commandName", {
enumerable: true,
configurable: true,
writable: true,
value: 'migration:fresh'
});
Object.defineProperty(Refresh, "description", {
enumerable: true,
configurable: true,
writable: true,
value: 'Drop all tables and re-migrate the database'
});
Object.defineProperty(Refresh, "settings", {
enumerable: true,
configurable: true,
writable: true,
value: {
loadApp: true,
}
});
__decorate([
standalone_1.flags.string({ description: 'Define a custom database connection', alias: 'c' }),
__metadata("design:type", String)
], Refresh.prototype, "connection", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Explicitly force command to run in production' }),
__metadata("design:type", Boolean)
], Refresh.prototype, "force", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Run seeders' }),
__metadata("design:type", Boolean)
], Refresh.prototype, "seed", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Drop all views' }),
__metadata("design:type", Boolean)
], Refresh.prototype, "dropViews", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Drop all custom types (Postgres only)' }),
__metadata("design:type", Boolean)
], Refresh.prototype, "dropTypes", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Disable locks acquired to run migrations safely' }),
__metadata("design:type", Boolean)
], Refresh.prototype, "disableLocks", void 0);
exports.default = Refresh;
+52
View File
@@ -0,0 +1,52 @@
import { BaseCommand } from '@adonisjs/core/build/standalone';
/**
* This command reset the database by rolling back to batch 0 and then
* re-run all migrations.
*/
export default class Refresh extends BaseCommand {
static commandName: string;
static description: string;
static settings: {
loadApp: boolean;
};
/**
* Custom connection for running migrations.
*/
connection: string;
/**
* Force command execution in production
*/
force: boolean;
/**
* Perform dry run
*/
dryRun: boolean;
/**
* Run seeders
*/
seed: boolean;
/**
* Disable advisory locks
*/
disableLocks: boolean;
/**
* Converting command properties to arguments
*/
private getArgs;
/**
* Reset all migrations
*/
private resetMigrations;
/**
* Run migrations
*/
private runMigrations;
/**
* Run seeders
*/
private runDbSeed;
/**
* Handle command
*/
run(): Promise<void>;
}
+178
View File
@@ -0,0 +1,178 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* 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);
};
Object.defineProperty(exports, "__esModule", { value: true });
const standalone_1 = require("@adonisjs/core/build/standalone");
/**
* This command reset the database by rolling back to batch 0 and then
* re-run all migrations.
*/
class Refresh extends standalone_1.BaseCommand {
constructor() {
super(...arguments);
/**
* Custom connection for running migrations.
*/
Object.defineProperty(this, "connection", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Force command execution in production
*/
Object.defineProperty(this, "force", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Perform dry run
*/
Object.defineProperty(this, "dryRun", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Run seeders
*/
Object.defineProperty(this, "seed", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Disable advisory locks
*/
Object.defineProperty(this, "disableLocks", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
/**
* Converting command properties to arguments
*/
getArgs() {
const args = [];
if (this.force) {
args.push('--force');
}
if (this.connection) {
args.push(`--connection="${this.connection}"`);
}
if (this.dryRun) {
args.push('--dry-run');
}
if (this.disableLocks) {
args.push('--disable-locks');
}
return args;
}
/**
* Reset all migrations
*/
async resetMigrations() {
const reset = await this.kernel.exec('migration:reset', this.getArgs());
this.exitCode = reset.exitCode;
this.error = reset.error;
}
/**
* Run migrations
*/
async runMigrations() {
const migrate = await this.kernel.exec('migration:run', this.getArgs());
this.exitCode = migrate.exitCode;
this.error = migrate.error;
}
/**
* Run seeders
*/
async runDbSeed() {
const args = [];
if (this.connection) {
args.push(`--connection="${this.connection}"`);
}
const dbSeed = await this.kernel.exec('db:seed', args);
this.exitCode = dbSeed.exitCode;
this.error = dbSeed.error;
}
/**
* Handle command
*/
async run() {
await this.resetMigrations();
if (this.exitCode) {
return;
}
await this.runMigrations();
if (this.exitCode) {
return;
}
if (this.seed) {
await this.runDbSeed();
}
}
}
Object.defineProperty(Refresh, "commandName", {
enumerable: true,
configurable: true,
writable: true,
value: 'migration:refresh'
});
Object.defineProperty(Refresh, "description", {
enumerable: true,
configurable: true,
writable: true,
value: 'Rollback and migrate database'
});
Object.defineProperty(Refresh, "settings", {
enumerable: true,
configurable: true,
writable: true,
value: {
loadApp: true,
}
});
__decorate([
standalone_1.flags.string({ description: 'Define a custom database connection', alias: 'c' }),
__metadata("design:type", String)
], Refresh.prototype, "connection", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Explicitly force command to run in production' }),
__metadata("design:type", Boolean)
], Refresh.prototype, "force", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Do not run actual queries. Instead view the SQL output' }),
__metadata("design:type", Boolean)
], Refresh.prototype, "dryRun", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Run seeders' }),
__metadata("design:type", Boolean)
], Refresh.prototype, "seed", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Disable locks acquired to run migrations safely' }),
__metadata("design:type", Boolean)
], Refresh.prototype, "disableLocks", void 0);
exports.default = Refresh;
+36
View File
@@ -0,0 +1,36 @@
import { BaseCommand } from '@adonisjs/core/build/standalone';
/**
* This command resets the database by rolling back to batch 0. Same
* as calling "migration:rollback --batch=0"
*/
export default class Reset extends BaseCommand {
static commandName: string;
static description: string;
static settings: {
loadApp: boolean;
};
/**
* Custom connection for running migrations.
*/
connection: string;
/**
* Force command execution in production
*/
force: boolean;
/**
* Perform dry run
*/
dryRun: boolean;
/**
* Disable advisory locks
*/
disableLocks: boolean;
/**
* Converting command properties to arguments
*/
private getArgs;
/**
* Handle command
*/
run(): Promise<void>;
}
+129
View File
@@ -0,0 +1,129 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* 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);
};
Object.defineProperty(exports, "__esModule", { value: true });
const standalone_1 = require("@adonisjs/core/build/standalone");
/**
* This command resets the database by rolling back to batch 0. Same
* as calling "migration:rollback --batch=0"
*/
class Reset extends standalone_1.BaseCommand {
constructor() {
super(...arguments);
/**
* Custom connection for running migrations.
*/
Object.defineProperty(this, "connection", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Force command execution in production
*/
Object.defineProperty(this, "force", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Perform dry run
*/
Object.defineProperty(this, "dryRun", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Disable advisory locks
*/
Object.defineProperty(this, "disableLocks", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
/**
* Converting command properties to arguments
*/
getArgs() {
const args = ['--batch=0'];
if (this.force) {
args.push('--force');
}
if (this.connection) {
args.push(`--connection="${this.connection}"`);
}
if (this.dryRun) {
args.push('--dry-run');
}
if (this.disableLocks) {
args.push('--disable-locks');
}
return args;
}
/**
* Handle command
*/
async run() {
const rollback = await this.kernel.exec('migration:rollback', this.getArgs());
this.exitCode = rollback.exitCode;
this.error = rollback.error;
}
}
Object.defineProperty(Reset, "commandName", {
enumerable: true,
configurable: true,
writable: true,
value: 'migration:reset'
});
Object.defineProperty(Reset, "description", {
enumerable: true,
configurable: true,
writable: true,
value: 'Rollback all migrations'
});
Object.defineProperty(Reset, "settings", {
enumerable: true,
configurable: true,
writable: true,
value: {
loadApp: true,
}
});
__decorate([
standalone_1.flags.string({ description: 'Define a custom database connection', alias: 'c' }),
__metadata("design:type", String)
], Reset.prototype, "connection", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Explicitly force command to run in production' }),
__metadata("design:type", Boolean)
], Reset.prototype, "force", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Do not run actual queries. Instead view the SQL output' }),
__metadata("design:type", Boolean)
], Reset.prototype, "dryRun", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Disable locks acquired to run migrations safely' }),
__metadata("design:type", Boolean)
], Reset.prototype, "disableLocks", void 0);
exports.default = Reset;
+62
View File
@@ -0,0 +1,62 @@
import MigrationsBase from './Base';
/**
* The command is meant to migrate the database by executing migrations
* in `down` direction.
*/
export default class Migrate extends MigrationsBase {
static commandName: string;
static description: string;
static settings: {
loadApp: boolean;
};
private migrator;
/**
* Custom connection for running migrations.
*/
connection: string;
/**
* Force run migrations in production
*/
force: boolean;
/**
* Perform dry run
*/
dryRun: boolean;
/**
* Define custom batch, instead of rolling back to the latest batch
*/
batch: number;
/**
* Display migrations result in one compact single-line output
*/
compactOutput: boolean;
/**
* Disable advisory locks
*/
disableLocks: boolean;
/**
* Instantiating the migrator instance
*/
private instantiateMigrator;
/**
* Run as a subcommand. Never close database connections or exit
* process inside this method
*/
private runAsSubCommand;
/**
* Branching out, so that if required we can implement
* "runAsMain" separately from "runAsSubCommand".
*
* For now, they both are the same
*/
private runAsMain;
/**
* Handle command
*/
run(): Promise<void>;
/**
* Lifecycle method invoked by ace after the "run"
* method.
*/
completed(): Promise<void>;
}
+215
View File
@@ -0,0 +1,215 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* 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);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const standalone_1 = require("@adonisjs/core/build/standalone");
const Base_1 = __importDefault(require("./Base"));
/**
* The command is meant to migrate the database by executing migrations
* in `down` direction.
*/
class Migrate extends Base_1.default {
constructor() {
super(...arguments);
Object.defineProperty(this, "migrator", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Custom connection for running migrations.
*/
Object.defineProperty(this, "connection", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Force run migrations in production
*/
Object.defineProperty(this, "force", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Perform dry run
*/
Object.defineProperty(this, "dryRun", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Define custom batch, instead of rolling back to the latest batch
*/
Object.defineProperty(this, "batch", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Display migrations result in one compact single-line output
*/
Object.defineProperty(this, "compactOutput", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
/**
* Disable advisory locks
*/
Object.defineProperty(this, "disableLocks", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
/**
* Instantiating the migrator instance
*/
instantiateMigrator() {
const db = this.application.container.use('Adonis/Lucid/Database');
const Migrator = this.application.container.resolveBinding('Adonis/Lucid/Migrator');
this.migrator = new Migrator(db, this.application, {
direction: 'down',
connectionName: this.connection,
batch: this.batch,
dryRun: this.dryRun,
disableLocks: this.disableLocks,
});
}
/**
* Run as a subcommand. Never close database connections or exit
* process inside this method
*/
async runAsSubCommand() {
const db = this.application.container.use('Adonis/Lucid/Database');
this.connection = this.connection || db.primaryConnectionName;
/**
* Continue with migrations when not in prod or force flag
* is passed
*/
let continueMigrations = !this.application.inProduction || this.force;
if (!continueMigrations) {
continueMigrations = await this.takeProductionConstent();
}
/**
* Do not continue when in prod and the prompt was cancelled
*/
if (!continueMigrations) {
return;
}
/**
* Invalid database connection
*/
if (!db.manager.has(this.connection)) {
this.printNotAValidConnection(this.connection);
this.exitCode = 1;
return;
}
this.instantiateMigrator();
await this.runMigrations(this.migrator, this.connection);
}
/**
* Branching out, so that if required we can implement
* "runAsMain" separately from "runAsSubCommand".
*
* For now, they both are the same
*/
runAsMain() {
return this.runAsSubCommand();
}
/**
* Handle command
*/
async run() {
if (this.isMain) {
await this.runAsMain();
}
else {
await this.runAsSubCommand();
}
}
/**
* Lifecycle method invoked by ace after the "run"
* method.
*/
async completed() {
if (this.migrator && this.isMain) {
await this.migrator.close();
}
}
}
Object.defineProperty(Migrate, "commandName", {
enumerable: true,
configurable: true,
writable: true,
value: 'migration:rollback'
});
Object.defineProperty(Migrate, "description", {
enumerable: true,
configurable: true,
writable: true,
value: 'Rollback migrations to a specific batch number'
});
Object.defineProperty(Migrate, "settings", {
enumerable: true,
configurable: true,
writable: true,
value: {
loadApp: true,
}
});
__decorate([
standalone_1.flags.string({ description: 'Define a custom database connection', alias: 'c' }),
__metadata("design:type", String)
], Migrate.prototype, "connection", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Explictly force to run migrations in production' }),
__metadata("design:type", Boolean)
], Migrate.prototype, "force", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Do not run actual queries. Instead view the SQL output' }),
__metadata("design:type", Boolean)
], Migrate.prototype, "dryRun", void 0);
__decorate([
standalone_1.flags.number({
description: 'Define custom batch number for rollback. Use 0 to rollback to initial state',
}),
__metadata("design:type", Number)
], Migrate.prototype, "batch", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'A compact single-line output' }),
__metadata("design:type", Boolean)
], Migrate.prototype, "compactOutput", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Disable locks acquired to run migrations safely' }),
__metadata("design:type", Boolean)
], Migrate.prototype, "disableLocks", void 0);
exports.default = Migrate;
+58
View File
@@ -0,0 +1,58 @@
import MigrationsBase from './Base';
/**
* The command is meant to migrate the database by executing migrations
* in `up` direction.
*/
export default class Migrate extends MigrationsBase {
static commandName: string;
static description: string;
static settings: {
loadApp: boolean;
};
private migrator;
/**
* Custom connection for running migrations.
*/
connection: string;
/**
* Force run migrations in production
*/
force: boolean;
/**
* Perform dry run
*/
dryRun: boolean;
/**
* Display migrations result in one compact single-line output
*/
compactOutput: boolean;
/**
* Disable advisory locks
*/
disableLocks: boolean;
/**
* Instantiating the migrator instance
*/
private instantiateMigrator;
/**
* Run as a subcommand. Never close database connections or exit
* process inside this method
*/
private runAsSubCommand;
/**
* Branching out, so that if required we can implement
* "runAsMain" separately from "runAsSubCommand".
*
* For now, they both are the same
*/
private runAsMain;
/**
* Handle command
*/
run(): Promise<void>;
/**
* Lifecycle method invoked by ace after the "run"
* method.
*/
completed(): Promise<void>;
}
+199
View File
@@ -0,0 +1,199 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* 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);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const standalone_1 = require("@adonisjs/core/build/standalone");
const Base_1 = __importDefault(require("./Base"));
/**
* The command is meant to migrate the database by executing migrations
* in `up` direction.
*/
class Migrate extends Base_1.default {
constructor() {
super(...arguments);
Object.defineProperty(this, "migrator", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Custom connection for running migrations.
*/
Object.defineProperty(this, "connection", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Force run migrations in production
*/
Object.defineProperty(this, "force", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Perform dry run
*/
Object.defineProperty(this, "dryRun", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Display migrations result in one compact single-line output
*/
Object.defineProperty(this, "compactOutput", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
/**
* Disable advisory locks
*/
Object.defineProperty(this, "disableLocks", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
/**
* Instantiating the migrator instance
*/
instantiateMigrator() {
const db = this.application.container.use('Adonis/Lucid/Database');
const Migrator = this.application.container.resolveBinding('Adonis/Lucid/Migrator');
this.migrator = new Migrator(db, this.application, {
direction: 'up',
connectionName: this.connection,
dryRun: this.dryRun,
disableLocks: this.disableLocks,
});
}
/**
* Run as a subcommand. Never close database connections or exit
* process inside this method
*/
async runAsSubCommand() {
const db = this.application.container.use('Adonis/Lucid/Database');
this.connection = this.connection || db.primaryConnectionName;
/**
* Continue with migrations when not in prod or force flag
* is passed
*/
let continueMigrations = !this.application.inProduction || this.force;
if (!continueMigrations) {
continueMigrations = await this.takeProductionConstent();
}
/**
* Do not continue when in prod and the prompt was cancelled
*/
if (!continueMigrations) {
return;
}
/**
* Invalid database connection
*/
if (!db.manager.has(this.connection)) {
this.printNotAValidConnection(this.connection);
this.exitCode = 1;
return;
}
this.instantiateMigrator();
await this.runMigrations(this.migrator, this.connection);
}
/**
* Branching out, so that if required we can implement
* "runAsMain" separately from "runAsSubCommand".
*
* For now, they both are the same
*/
async runAsMain() {
await this.runAsSubCommand();
}
/**
* Handle command
*/
async run() {
if (this.isMain) {
await this.runAsMain();
}
else {
await this.runAsSubCommand();
}
}
/**
* Lifecycle method invoked by ace after the "run"
* method.
*/
async completed() {
if (this.migrator && this.isMain) {
await this.migrator.close();
}
}
}
Object.defineProperty(Migrate, "commandName", {
enumerable: true,
configurable: true,
writable: true,
value: 'migration:run'
});
Object.defineProperty(Migrate, "description", {
enumerable: true,
configurable: true,
writable: true,
value: 'Migrate database by running pending migrations'
});
Object.defineProperty(Migrate, "settings", {
enumerable: true,
configurable: true,
writable: true,
value: {
loadApp: true,
}
});
__decorate([
standalone_1.flags.string({ description: 'Define a custom database connection', alias: 'c' }),
__metadata("design:type", String)
], Migrate.prototype, "connection", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Explicitly force to run migrations in production' }),
__metadata("design:type", Boolean)
], Migrate.prototype, "force", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Do not run actual queries. Instead view the SQL output' }),
__metadata("design:type", Boolean)
], Migrate.prototype, "dryRun", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'A compact single-line output' }),
__metadata("design:type", Boolean)
], Migrate.prototype, "compactOutput", void 0);
__decorate([
standalone_1.flags.boolean({ description: 'Disable locks acquired to run migrations safely' }),
__metadata("design:type", Boolean)
], Migrate.prototype, "disableLocks", void 0);
exports.default = Migrate;
+54
View File
@@ -0,0 +1,54 @@
import { BaseCommand } from '@adonisjs/core/build/standalone';
/**
* The command is meant to migrate the database by execute migrations
* in `up` direction.
*/
export default class Status extends BaseCommand {
static commandName: string;
static description: string;
static settings: {
loadApp: boolean;
};
private migrator;
/**
* Define custom connection
*/
connection: string;
/**
* Not a valid connection
*/
protected printNotAValidConnection(connection: string): void;
/**
* Colorizes the status string
*/
private colorizeStatus;
/**
* Instantiating the migrator instance
*/
private instantiateMigrator;
/**
* Render list inside a table
*/
private renderList;
/**
* Run as a subcommand. Never close database connections or exit
* process inside this method
*/
private runAsSubCommand;
/**
* Branching out, so that if required we can implement
* "runAsMain" separately from "runAsSubCommand".
*
* For now, they both are the same
*/
private runAsMain;
/**
* Handle command
*/
run(): Promise<void>;
/**
* Lifecycle method invoked by ace after the "run"
* method.
*/
completed(): Promise<void>;
}
+165
View File
@@ -0,0 +1,165 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* 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);
};
Object.defineProperty(exports, "__esModule", { value: true });
const standalone_1 = require("@adonisjs/core/build/standalone");
/**
* The command is meant to migrate the database by execute migrations
* in `up` direction.
*/
class Status extends standalone_1.BaseCommand {
constructor() {
super(...arguments);
Object.defineProperty(this, "migrator", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Define custom connection
*/
Object.defineProperty(this, "connection", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
/**
* Not a valid connection
*/
printNotAValidConnection(connection) {
this.logger.error(`"${connection}" is not a valid connection name. Double check "config/database" file`);
}
/**
* Colorizes the status string
*/
colorizeStatus(status) {
switch (status) {
case 'pending':
return this.colors.yellow('pending');
case 'migrated':
return this.colors.green('completed');
case 'corrupt':
return this.colors.red('corrupt');
}
}
/**
* Instantiating the migrator instance
*/
instantiateMigrator() {
const db = this.application.container.use('Adonis/Lucid/Database');
const Migrator = this.application.container.resolveBinding('Adonis/Lucid/Migrator');
this.migrator = new Migrator(db, this.application, {
direction: 'up',
connectionName: this.connection,
});
}
/**
* Render list inside a table
*/
renderList(list) {
const table = this.ui.table();
table.head(['Name', 'Status', 'Batch', 'Message']);
/**
* Push a new row to the table
*/
list.forEach((node) => {
table.row([
node.name,
this.colorizeStatus(node.status),
node.batch ? String(node.batch) : 'NA',
node.status === 'corrupt' ? 'The migration file is missing on filesystem' : '',
]);
});
table.render();
}
/**
* Run as a subcommand. Never close database connections or exit
* process inside this method
*/
async runAsSubCommand() {
const db = this.application.container.use('Adonis/Lucid/Database');
this.connection = this.connection || db.primaryConnectionName;
/**
* Not a valid connection
*/
if (!db.manager.has(this.connection)) {
this.printNotAValidConnection(this.connection);
this.exitCode = 1;
return;
}
this.instantiateMigrator();
this.renderList(await this.migrator.getList());
}
/**
* Branching out, so that if required we can implement
* "runAsMain" separately from "runAsSubCommand".
*
* For now, they both are the same
*/
async runAsMain() {
await this.runAsSubCommand();
}
/**
* Handle command
*/
async run() {
if (this.isMain) {
await this.runAsMain();
}
else {
await this.runAsSubCommand();
}
}
/**
* Lifecycle method invoked by ace after the "run"
* method.
*/
async completed() {
if (this.migrator && this.isMain) {
await this.migrator.close();
}
}
}
Object.defineProperty(Status, "commandName", {
enumerable: true,
configurable: true,
writable: true,
value: 'migration:status'
});
Object.defineProperty(Status, "description", {
enumerable: true,
configurable: true,
writable: true,
value: 'View migrations status'
});
Object.defineProperty(Status, "settings", {
enumerable: true,
configurable: true,
writable: true,
value: {
loadApp: true,
}
});
__decorate([
standalone_1.flags.string({ description: 'Define a custom database connection', alias: 'c' }),
__metadata("design:type", String)
], Status.prototype, "connection", void 0);
exports.default = Status;
+2
View File
@@ -0,0 +1,2 @@
declare const _default: string[];
export default _default;
+25
View File
@@ -0,0 +1,25 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = [
'@adonisjs/lucid/build/commands/DbSeed',
'@adonisjs/lucid/build/commands/DbWipe',
'@adonisjs/lucid/build/commands/DbTruncate',
'@adonisjs/lucid/build/commands/MakeModel',
'@adonisjs/lucid/build/commands/MakeMigration',
'@adonisjs/lucid/build/commands/MakeSeeder',
'@adonisjs/lucid/build/commands/MakeFactory',
'@adonisjs/lucid/build/commands/Migration/Run',
'@adonisjs/lucid/build/commands/Migration/Rollback',
'@adonisjs/lucid/build/commands/Migration/Status',
'@adonisjs/lucid/build/commands/Migration/Reset',
'@adonisjs/lucid/build/commands/Migration/Refresh',
'@adonisjs/lucid/build/commands/Migration/Fresh',
];
+194
View File
@@ -0,0 +1,194 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = require("path");
const fs_1 = require("fs");
/**
* Prompt choices for the database server selection
*/
const DB_SERVER_PROMPT_CHOICES = [
{
name: 'sqlite',
message: 'SQLite',
},
{
name: 'mysql',
message: 'MySQL / MariaDB',
},
{
name: 'pg',
message: 'PostgreSQL',
},
{
name: 'oracle',
message: 'OracleDB',
},
{
name: 'mssql',
message: 'Microsoft SQL Server',
},
];
/**
* Environment variables used by different database
* drivers
*/
const DB_SERVER_ENV_VALUES = {
sqlite: {},
mysql: {
MYSQL_HOST: '127.0.0.1',
MYSQL_PORT: 3306,
MYSQL_USER: 'lucid',
MYSQL_PASSWORD: '',
MYSQL_DB_NAME: 'lucid',
},
pg: {
PG_HOST: '127.0.0.1',
PG_PORT: 5432,
PG_USER: 'lucid',
PG_PASSWORD: '',
PG_DB_NAME: 'lucid',
},
oracle: {
ORACLE_HOST: '127.0.0.1',
ORACLE_PORT: 1521,
ORACLE_USER: 'lucid',
ORACLE_PASSWORD: '',
ORACLE_DB_NAME: 'lucid',
},
mssql: {
MSSQL_SERVER: '127.0.0.1',
MSSQL_PORT: 1433,
MSSQL_USER: 'lucid',
MSSQL_PASSWORD: '',
MSSQL_DB_NAME: 'lucid',
},
};
/**
* Packages required by different drivers
*/
const DB_DRIVER_PACKAGES = {
sqlite: 'sqlite3',
mysql: 'mysql2',
pg: 'pg',
oracle: 'oracledb',
mssql: 'mssql',
};
/**
* Prompts user for the drivers they want to use
*/
function getDbDrivers(sink) {
return sink
.getPrompt()
.multiple('Select the database driver you want to use', DB_SERVER_PROMPT_CHOICES, {
validate(choices) {
return choices && choices.length ? true : 'Select at least one database driver to continue';
},
});
}
/**
* Returns absolute path to the stub relative from the templates
* directory
*/
function getStub(...relativePaths) {
return (0, path_1.join)(__dirname, 'templates', ...relativePaths);
}
/**
* Instructions to be executed when setting up the package.
*/
async function instructions(projectRoot, app, sink) {
/**
* Get drivers
*/
const drivers = await getDbDrivers(sink);
/**
* Create Config file
*/
const configPath = app.configPath('database.ts');
const databaseConfig = new sink.files.MustacheFile(projectRoot, configPath, getStub('database.txt'));
databaseConfig.overwrite = true;
databaseConfig
.apply({
sqlite: drivers.includes('sqlite'),
mysql: drivers.includes('mysql'),
psql: drivers.includes('pg'),
oracle: drivers.includes('oracle'),
mssql: drivers.includes('mssql'),
})
.commit();
const configDir = app.directoriesMap.get('config') || 'config';
sink.logger.action('create').succeeded(`${configDir}/database.ts`);
/**
* Setup .env file
*/
const env = new sink.files.EnvFile(projectRoot);
env.set('DB_CONNECTION', drivers[0]);
/**
* Unset old values
*/
Object.keys(DB_SERVER_ENV_VALUES).forEach((driver) => {
Object.keys(DB_SERVER_ENV_VALUES[driver]).forEach((key) => {
env.unset(key);
});
});
drivers.forEach((driver) => {
Object.keys(DB_SERVER_ENV_VALUES[driver]).forEach((key) => {
env.set(key, DB_SERVER_ENV_VALUES[driver][key]);
});
});
env.commit();
sink.logger.action('update').succeeded('.env,.env.example');
/**
* Create tmp dir when sqlite is selected
*/
if (drivers.includes('sqlite') && !(0, fs_1.existsSync)(app.tmpPath())) {
(0, fs_1.mkdirSync)(app.tmpPath());
const tmpDir = app.directoriesMap.get('tmp') || 'tmp';
sink.logger.action('create').succeeded(`./${tmpDir}`);
}
/**
* Install required dependencies
*/
const pkg = new sink.files.PackageJsonFile(projectRoot);
/**
* Remove existing dependencies
*/
Object.keys(DB_DRIVER_PACKAGES).forEach((driver) => {
if (!drivers.includes(driver)) {
pkg.uninstall(DB_DRIVER_PACKAGES[driver], false);
}
});
pkg.install('luxon', undefined, false);
drivers.forEach((driver) => {
pkg.install(DB_DRIVER_PACKAGES[driver], undefined, false);
});
const logLines = [
`Installing: ${sink.logger.colors.gray(pkg.getInstalls(false).list.join(', '))}`,
];
/**
* Find the list of packages we have to remove
*/
const packagesToRemove = pkg
.getUninstalls(false)
.list.filter((name) => pkg.get(`dependencies.${name}`));
if (packagesToRemove.length) {
logLines.push(`Removing: ${sink.logger.colors.gray(packagesToRemove.join(', '))}`);
}
const spinner = sink.logger.await(logLines.join(' '));
try {
await pkg.commitAsync();
spinner.update('Packages installed');
}
catch (error) {
spinner.update('Unable to install packages');
sink.logger.fatal(error);
}
spinner.stop();
}
exports.default = instructions;
+52
View File
@@ -0,0 +1,52 @@
The package has been configured successfully. The database configuration stored inside `config/database.ts` file relies on the following environment variables and hence we recommend validating them.
**Open the `env.ts` file and paste the following code inside the `Env.rules` object.**
```ts
DB_CONNECTION: Env.schema.string(),
```
## Variables for the MYSQL driver
```ts
MYSQL_HOST: Env.schema.string({ format: 'host' }),
MYSQL_PORT: Env.schema.number(),
MYSQL_USER: Env.schema.string(),
MYSQL_PASSWORD: Env.schema.string.optional(),
MYSQL_DB_NAME: Env.schema.string(),
```
- The `MYSQL_HOST` should always be present and formatted as a valid `host`.
- The `MYSQL_PORT` should always be present and a valid number.
- The `MYSQL_USER` and `MYSQL_PASSWORD` are required to authenticate with the database server. The password is marked as optional since many local database servers are configured to work without passwords.
- The `MYSQL_DB_NAME` is the database name you want to connect with.
## Variables for the PostgreSQL driver
```ts
PG_HOST: Env.schema.string({ format: 'host' }),
PG_PORT: Env.schema.number(),
PG_USER: Env.schema.string(),
PG_PASSWORD: Env.schema.string.optional(),
PG_DB_NAME: Env.schema.string(),
```
## Variables for the MSSQL driver
```ts
MSSQL_SERVER: Env.schema.string({ format: 'host' }),
MSSQL_PORT: Env.schema.number(),
MSSQL_USER: Env.schema.string(),
MSSQL_PASSWORD: Env.schema.string.optional(),
MSSQL_DB_NAME: Env.schema.string(),
```
## Variables for the OracleDB driver
```ts
ORACLE_HOST: Env.schema.string({ format: 'host' }),
ORACLE_PORT: Env.schema.number(),
ORACLE_USER: Env.schema.string(),
ORACLE_PASSWORD: Env.schema.string.optional(),
ORACLE_DB_NAME: Env.schema.string(),
```
+62
View File
@@ -0,0 +1,62 @@
/// <reference types="@adonisjs/application/build/adonis-typings" />
import { ApplicationContract } from '@ioc:Adonis/Core/Application';
/**
* Database service provider
*/
export default class DatabaseServiceProvider {
protected app: ApplicationContract;
constructor(app: ApplicationContract);
static needsApplication: boolean;
/**
* Register the database binding
*/
private registerDatabase;
/**
* Registers ORM
*/
private registerOrm;
/**
* Registers schema class
*/
private registerSchema;
/**
* Registers schema class
*/
private registerFactory;
/**
* Registers schema class
*/
private registerBaseSeeder;
/**
* Registers the health checker
*/
private registerHealthChecker;
/**
* Register the migrator used for database migration
*/
private registerMigrator;
/**
* Extends the validator by defining validation rules
*/
private defineValidationRules;
/**
* Defines REPL bindings
*/
private defineReplBindings;
/**
* Define test utilities for database
*/
private defineTestUtils;
/**
* Called when registering providers
*/
register(): void;
/**
* Called when all bindings are in place
*/
boot(): void;
/**
* Gracefully close connections during shutdown
*/
shutdown(): Promise<void>;
}
+183
View File
@@ -0,0 +1,183 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Database service provider
*/
class DatabaseServiceProvider {
constructor(app) {
Object.defineProperty(this, "app", {
enumerable: true,
configurable: true,
writable: true,
value: app
});
}
/**
* Register the database binding
*/
registerDatabase() {
this.app.container.singleton('Adonis/Lucid/Database', () => {
const config = this.app.container.resolveBinding('Adonis/Core/Config').get('database', {});
const Logger = this.app.container.resolveBinding('Adonis/Core/Logger');
const Profiler = this.app.container.resolveBinding('Adonis/Core/Profiler');
const Emitter = this.app.container.resolveBinding('Adonis/Core/Event');
const { Database } = require('../src/Database');
return new Database(config, Logger, Profiler, Emitter);
});
}
/**
* Registers ORM
*/
registerOrm() {
this.app.container.singleton('Adonis/Lucid/Orm', () => {
const { Adapter } = require('../src/Orm/Adapter');
const { scope } = require('../src/Helpers/scope');
const decorators = require('../src/Orm/Decorators');
const { BaseModel } = require('../src/Orm/BaseModel');
const { ModelPaginator } = require('../src/Orm/Paginator');
const { SnakeCaseNamingStrategy } = require('../src/Orm/NamingStrategies/SnakeCase');
/**
* Attaching adapter to the base model. Each model is allowed to define
* a different adapter.
*/
BaseModel.$adapter = new Adapter(this.app.container.resolveBinding('Adonis/Lucid/Database'));
BaseModel.$container = this.app.container;
return {
BaseModel,
ModelPaginator,
SnakeCaseNamingStrategy,
scope,
...decorators,
};
});
}
/**
* Registers schema class
*/
registerSchema() {
this.app.container.singleton('Adonis/Lucid/Schema', () => {
const { Schema } = require('../src/Schema');
return Schema;
});
}
/**
* Registers schema class
*/
registerFactory() {
this.app.container.singleton('Adonis/Lucid/Factory', () => {
const { FactoryManager } = require('../src/Factory');
return new FactoryManager();
});
}
/**
* Registers schema class
*/
registerBaseSeeder() {
this.app.container.singleton('Adonis/Lucid/Seeder', () => {
const { BaseSeeder } = require('../src/BaseSeeder');
return BaseSeeder;
});
}
/**
* Registers the health checker
*/
registerHealthChecker() {
/**
* Do not register health checks in the repl environment
*/
if (this.app.environment === 'repl') {
return;
}
this.app.container.withBindings(['Adonis/Core/HealthCheck', 'Adonis/Lucid/Database'], (HealthCheck, Db) => {
if (Db.hasHealthChecksEnabled) {
HealthCheck.addChecker('lucid', 'Adonis/Lucid/Database');
}
});
}
/**
* Register the migrator used for database migration
*/
registerMigrator() {
this.app.container.bind('Adonis/Lucid/Migrator', () => {
const { Migrator } = require('../src/Migrator');
return Migrator;
});
}
/**
* Extends the validator by defining validation rules
*/
defineValidationRules() {
/**
* Do not register validation rules in the "repl" environment
*/
if (this.app.environment === 'repl') {
return;
}
this.app.container.withBindings(['Adonis/Core/Validator', 'Adonis/Lucid/Database', 'Adonis/Core/Logger'], (Validator, Db, Logger) => {
const { extendValidator } = require('../src/Bindings/Validator');
extendValidator(Validator.validator, Db, Logger);
});
}
/**
* Defines REPL bindings
*/
defineReplBindings() {
if (this.app.environment !== 'repl') {
return;
}
this.app.container.withBindings(['Adonis/Addons/Repl'], (Repl) => {
const { defineReplBindings } = require('../src/Bindings/Repl');
defineReplBindings(this.app, Repl);
});
}
/**
* Define test utilities for database
*/
defineTestUtils() {
this.app.container.withBindings(['Adonis/Core/TestUtils', 'Adonis/Core/Ace'], (testUtils, ace) => {
const { defineTestUtils } = require('../src/Bindings/TestUtils');
return new defineTestUtils(testUtils, ace);
});
}
/**
* Called when registering providers
*/
register() {
this.registerDatabase();
this.registerOrm();
this.registerSchema();
this.registerFactory();
this.registerBaseSeeder();
this.registerMigrator();
}
/**
* Called when all bindings are in place
*/
boot() {
this.registerHealthChecker();
this.defineValidationRules();
this.defineReplBindings();
this.defineTestUtils();
}
/**
* Gracefully close connections during shutdown
*/
async shutdown() {
await this.app.container.resolveBinding('Adonis/Lucid/Database').manager.closeAll();
}
}
exports.default = DatabaseServiceProvider;
Object.defineProperty(DatabaseServiceProvider, "needsApplication", {
enumerable: true,
configurable: true,
writable: true,
value: true
});
+11
View File
@@ -0,0 +1,11 @@
import { QueryClientContract } from '@ioc:Adonis/Lucid/Database';
export declare class BaseSeeder {
client: QueryClientContract;
/**
* @deprecated
*/
static developmentOnly: boolean;
static environment: string[];
constructor(client: QueryClientContract);
run(): Promise<void>;
}
+23
View File
@@ -0,0 +1,23 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseSeeder = void 0;
class BaseSeeder {
constructor(client) {
Object.defineProperty(this, "client", {
enumerable: true,
configurable: true,
writable: true,
value: client
});
}
async run() { }
}
exports.BaseSeeder = BaseSeeder;
+7
View File
@@ -0,0 +1,7 @@
/// <reference types="@adonisjs/application/build/adonis-typings" />
import { ReplContract } from '@ioc:Adonis/Addons/Repl';
import { ApplicationContract } from '@ioc:Adonis/Core/Application';
/**
* Define REPL bindings
*/
export declare function defineReplBindings(app: ApplicationContract, Repl: ReplContract): void;
+59
View File
@@ -0,0 +1,59 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.defineReplBindings = void 0;
const helpers_1 = require("@poppinss/utils/build/helpers");
/**
* Helper to define REPL state
*/
function setupReplState(repl, key, value) {
repl.server.context[key] = value;
repl.notify(`Loaded ${key} module. You can access it using the "${repl.colors.underline(key)}" variable`);
}
/**
* Define REPL bindings
*/
function defineReplBindings(app, Repl) {
/**
* Load all models to the models property
*/
Repl.addMethod('loadModels', (repl) => {
const modelsPath = app.resolveNamespaceDirectory('models') || 'app/Models';
console.log(repl.colors.dim(`recursively reading models from "${modelsPath}"`));
const modelsAbsPath = app.makePath(modelsPath);
setupReplState(repl, 'models', (0, helpers_1.requireAll)(modelsAbsPath));
}, {
description: 'Recursively load Lucid models to the "models" property',
});
/**
* Load database provider to the Db provider
*/
Repl.addMethod('loadDb', (repl) => {
setupReplState(repl, 'Db', app.container.use('Adonis/Lucid/Database'));
}, {
description: 'Load database provider to the "Db" property',
});
/**
* Load all factories to the factories property
*/
Repl.addMethod('loadFactories', (repl) => {
const factoriesPath = app.resolveNamespaceDirectory('factories') || 'database/factories';
console.log(repl.colors.dim(`recursively reading factories from "${factoriesPath}"`));
const factoriesAbsPath = app.makePath(factoriesPath);
const loadedFactories = (0, helpers_1.requireAll)(factoriesAbsPath);
if (!loadedFactories) {
return;
}
setupReplState(repl, 'factories', Object.values(loadedFactories).reduce((acc, items) => ({ ...acc, ...items }), {}));
}, {
description: 'Recursively load factories to the "factories" property',
});
}
exports.defineReplBindings = defineReplBindings;
+6
View File
@@ -0,0 +1,6 @@
import type { TestUtilsContract } from '@ioc:Adonis/Core/TestUtils';
import type Ace from '@ioc:Adonis/Core/Ace';
/**
* Define database testing utilities
*/
export declare function defineTestUtils(testUtils: TestUtilsContract, ace: typeof Ace): void;
+33
View File
@@ -0,0 +1,33 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.defineTestUtils = void 0;
const Seeder_1 = require("../TestUtils/Seeder");
const Migration_1 = require("../TestUtils/Migration");
const Truncator_1 = require("../TestUtils/Truncator");
/**
* Define database testing utilities
*/
function defineTestUtils(testUtils, ace) {
testUtils.constructor.macro('db', (connectionName) => {
return {
migrate() {
return new Migration_1.TestsMigrator(ace, connectionName).run();
},
seed() {
return new Seeder_1.TestsSeeder(ace, connectionName).run();
},
truncate() {
return new Truncator_1.TestsTruncator(ace, connectionName).run();
},
};
});
}
exports.defineTestUtils = defineTestUtils;
+9
View File
@@ -0,0 +1,9 @@
/// <reference types="@adonisjs/validator" />
/// <reference types="@adonisjs/logger/build/adonis-typings/logger" />
import { LoggerContract } from '@ioc:Adonis/Core/Logger';
import { DatabaseContract } from '@ioc:Adonis/Lucid/Database';
import { validator as validatorStatic } from '@ioc:Adonis/Core/Validator';
/**
* Extends the validator by adding `unique` and `exists`
*/
export declare function extendValidator(validator: typeof validatorStatic, database: DatabaseContract, logger: LoggerContract): void;
+205
View File
@@ -0,0 +1,205 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.extendValidator = void 0;
const luxon_1 = require("luxon");
const utils_1 = require("@poppinss/utils");
/**
* Checks for database rows for `exists` and `unique` rule.
*/
class DbRowCheck {
constructor(ruleName, database, helpers) {
Object.defineProperty(this, "ruleName", {
enumerable: true,
configurable: true,
writable: true,
value: ruleName
});
Object.defineProperty(this, "database", {
enumerable: true,
configurable: true,
writable: true,
value: database
});
Object.defineProperty(this, "helpers", {
enumerable: true,
configurable: true,
writable: true,
value: helpers
});
}
/**
* Applies user defined where constraints on the query builder
*/
applyWhere(query, constraints, refs) {
if (!constraints.length) {
return;
}
constraints.forEach(({ key, operator, value, ref }) => {
const val = ref ? refs[ref].value : value;
if (operator === 'in') {
query.whereIn(key, val);
}
else {
query.where(key, val);
}
});
}
/**
* Applies user defined where not constraints on the query builder
*/
applyWhereNot(query, constraints, refs) {
if (!constraints.length) {
return;
}
constraints.forEach(({ key, operator, value, ref }) => {
const val = ref ? refs[ref].value : value;
if (operator === 'in') {
query.whereNotIn(key, val);
}
else {
query.whereNot(key, val);
}
});
}
/**
* Normalizes constraints
*/
normalizeConstraints(constraints) {
const normalized = [];
if (!constraints) {
return normalized;
}
/**
* Normalize object into an array of objects
*/
return Object.keys(constraints).reduce((result, key) => {
const value = constraints[key];
if (this.helpers.isRef(value)) {
result.push({ key, ref: value.key, operator: Array.isArray(value.value) ? 'in' : 'eq' });
}
else {
result.push({ key, value, operator: Array.isArray(value) ? 'in' : 'eq' });
}
return result;
}, normalized);
}
/**
* Compile validation options
*/
compile(options) {
/**
* Ensure options are defined with table and column name
*/
if (!options || !options.table || !options.column) {
throw new utils_1.Exception(`"${this.ruleName}" rule expects a "table" and a "column" name`);
}
/**
* Emit warning
*/
if (options.constraints) {
process.emitWarning('DeprecationWarning', '"options.constraints" have been depreciated. Use "options.where" instead.');
}
return {
table: options.table,
column: options.column,
caseInsensitive: !!options.caseInsensitive,
connection: options.connection,
dateFormat: options.dateFormat,
where: this.normalizeConstraints(options.where || options.constraints),
whereNot: this.normalizeConstraints(options.whereNot),
};
}
/**
* Validate value
*/
async validate(value, { table, column, where, whereNot, connection, caseInsensitive, dateFormat }, { pointer, errorReporter, arrayExpressionPointer, refs }) {
const client = this.database.connection(connection);
const query = client.from(table).select(1);
/**
* Convert datetime to a string
*/
if (luxon_1.DateTime.isDateTime(value)) {
const format = dateFormat || client.dialect.dateTimeFormat;
value = value.toFormat(format);
}
/**
* https://www.sqlite.org/lang_corefunc.html#lower
* https://docs.aws.amazon.com/redshift/latest/dg/r_LOWER.html
* https://dev.mysql.com/doc/refman/8.0/en/string-functions.html#function_lower
* https://www.postgresql.org/docs/9.1/functions-string.html
* https://docs.microsoft.com/en-us/sql/t-sql/functions/lower-transact-sql?view=sql-server-ver15
* https://coderwall.com/p/6yhsuq/improve-case-insensitive-queries-in-postgres-using-smarter-indexes
*/
if (caseInsensitive) {
query.whereRaw(`lower(${column}) = ?`, [this.database.raw(`lower(?)`, [value])]);
}
else {
query.where(column, value);
}
this.applyWhere(query, where, refs);
this.applyWhereNot(query, whereNot, refs);
const row = await query.first();
if (this.ruleName === 'exists') {
if (!row) {
errorReporter.report(pointer, this.ruleName, `${this.ruleName} validation failure`, arrayExpressionPointer);
}
return;
}
if (this.ruleName === 'unique') {
if (row) {
errorReporter.report(pointer, this.ruleName, `${this.ruleName} validation failure`, arrayExpressionPointer);
}
return;
}
}
}
/**
* Extends the validator by adding `unique` and `exists`
*/
function extendValidator(validator, database, logger) {
/**
* Exists rule to ensure the value exists in the database
*/
const existsChecker = new DbRowCheck('exists', database, validator.helpers);
validator.rule('exists', async (value, compiledOptions, options) => {
try {
await existsChecker.validate(value, compiledOptions, options);
}
catch (error) {
logger.fatal({ err: error }, '"exists" validation rule failed');
options.errorReporter.report(options.pointer, 'exists', 'exists validation failure', options.arrayExpressionPointer);
}
}, (options) => {
return {
compiledOptions: existsChecker.compile(options[0]),
async: true,
};
});
/**
* Unique rule to check if value is unique or not
*/
const uniqueChecker = new DbRowCheck('unique', database, validator.helpers);
validator.rule('unique', async (value, compiledOptions, options) => {
try {
await uniqueChecker.validate(value, compiledOptions, options);
}
catch (error) {
logger.fatal({ err: error }, '"unique" validation rule failed');
options.errorReporter.report(options.pointer, 'unique', 'unique validation failure', options.arrayExpressionPointer);
}
}, (options) => {
return {
compiledOptions: uniqueChecker.compile(options[0]),
async: true,
};
});
}
exports.extendValidator = extendValidator;
+15
View File
@@ -0,0 +1,15 @@
/// <reference types="@adonisjs/logger/build/adonis-typings/logger" />
import { LoggerContract } from '@ioc:Adonis/Core/Logger';
/**
* Custom knex logger that uses adonisjs logger under the
* hood.
*/
export declare class Logger {
name: string;
adonisLogger: LoggerContract;
warn: any;
error: any;
deprecate: any;
debug: any;
constructor(name: string, adonisLogger: LoggerContract);
}
+65
View File
@@ -0,0 +1,65 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Logger = void 0;
/**
* Custom knex logger that uses adonisjs logger under the
* hood.
*/
class Logger {
constructor(name, adonisLogger) {
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: name
});
Object.defineProperty(this, "adonisLogger", {
enumerable: true,
configurable: true,
writable: true,
value: adonisLogger
});
Object.defineProperty(this, "warn", {
enumerable: true,
configurable: true,
writable: true,
value: function (message) {
this.adonisLogger.warn(message);
}.bind(this)
});
Object.defineProperty(this, "error", {
enumerable: true,
configurable: true,
writable: true,
value: function (message) {
this.adonisLogger.error(message);
}.bind(this)
});
Object.defineProperty(this, "deprecate", {
enumerable: true,
configurable: true,
writable: true,
value: function (message) {
this.adonisLogger.info(message);
}.bind(this)
});
Object.defineProperty(this, "debug", {
enumerable: true,
configurable: true,
writable: true,
value: function (message) {
this.warn('"debug" property inside config is depreciated. We recommend using "db:query" event for enrich logging');
this.adonisLogger.debug(message);
}.bind(this)
});
}
}
exports.Logger = Logger;
+86
View File
@@ -0,0 +1,86 @@
/// <reference path="../../adonis-typings/index.d.ts" />
/// <reference types="@adonisjs/logger/build/adonis-typings/logger" />
/// <reference types="@adonisjs/events/build/adonis-typings" />
import { EmitterContract } from '@ioc:Adonis/Core/Event';
import { LoggerContract } from '@ioc:Adonis/Core/Logger';
import { HealthReportEntry } from '@ioc:Adonis/Core/HealthCheck';
import { ReportNode, ConnectionNode, ConnectionConfig, ConnectionManagerContract } from '@ioc:Adonis/Lucid/Database';
/**
* Connection manager job is to manage multiple named connections. You can add any number
* or connections by registering their config only once and then make use of `connect`
* and `close` methods to create and destroy db connections.
*/
export declare class ConnectionManager implements ConnectionManagerContract {
private logger;
private emitter;
/**
* List of managed connections
*/
connections: ConnectionManagerContract['connections'];
/**
* Connections for which the config was patched. They must get removed
* overtime, unless application is behaving unstable.
*/
private orphanConnections;
constructor(logger: LoggerContract, emitter: EmitterContract);
/**
* Handles disconnection of a connection
*/
private handleDisconnect;
/**
* Handles event when a new connection is added
*/
private handleConnect;
/**
* Monitors a given connection by listening for lifecycle events
*/
private monitorConnection;
/**
* Add a named connection with it's configuration. Make sure to call `connect`
* before using the connection to make database queries.
*/
add(connectionName: string, config: ConnectionConfig): void;
/**
* Connect to the database using config for a given named connection
*/
connect(connectionName: string): void;
/**
* Patching the config
*/
patch(connectionName: string, config: ConnectionConfig): void;
/**
* Returns the connection node for a given named connection
*/
get(connectionName: string): ConnectionNode | undefined;
/**
* Returns a boolean telling if we have connection details for
* a given named connection. This method doesn't tell if
* connection is connected or not.
*/
has(connectionName: string): boolean;
/**
* Returns a boolean telling if connection has been established
* with the database or not
*/
isConnected(connectionName: string): boolean;
/**
* Closes a given connection and can optionally release it from the
* tracking list
*/
close(connectionName: string, release?: boolean): Promise<void>;
/**
* Close all tracked connections
*/
closeAll(release?: boolean): Promise<void>;
/**
* Release a connection. This will disconnect the connection
* and will delete it from internal list
*/
release(connectionName: string): Promise<void>;
/**
* Returns the report for all the connections marked for healthChecks.
*/
report(): Promise<HealthReportEntry & {
meta: ReportNode[];
}>;
}
+254
View File
@@ -0,0 +1,254 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConnectionManager = void 0;
/// <reference path="../../adonis-typings/index.ts" />
const utils_1 = require("@poppinss/utils");
const index_1 = require("./index");
/**
* Connection manager job is to manage multiple named connections. You can add any number
* or connections by registering their config only once and then make use of `connect`
* and `close` methods to create and destroy db connections.
*/
class ConnectionManager {
constructor(logger, emitter) {
Object.defineProperty(this, "logger", {
enumerable: true,
configurable: true,
writable: true,
value: logger
});
Object.defineProperty(this, "emitter", {
enumerable: true,
configurable: true,
writable: true,
value: emitter
});
/**
* List of managed connections
*/
Object.defineProperty(this, "connections", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
/**
* Connections for which the config was patched. They must get removed
* overtime, unless application is behaving unstable.
*/
Object.defineProperty(this, "orphanConnections", {
enumerable: true,
configurable: true,
writable: true,
value: new Set()
});
}
/**
* Handles disconnection of a connection
*/
handleDisconnect(connection) {
/**
* We received the close event on the orphan connection and not the connection
* that is in use
*/
if (this.orphanConnections.has(connection)) {
this.orphanConnections.delete(connection);
this.emitter.emit('db:connection:disconnect', connection);
this.logger.trace({ connection: connection.name }, 'disconnecting connection inside manager');
return;
}
const internalConnection = this.get(connection.name);
/**
* This will be false, when connection was released at the
* time of closing
*/
if (!internalConnection) {
return;
}
this.emitter.emit('db:connection:disconnect', connection);
this.logger.trace({ connection: connection.name }, 'disconnecting connection inside manager');
delete internalConnection.connection;
internalConnection.state = 'closed';
}
/**
* Handles event when a new connection is added
*/
handleConnect(connection) {
const internalConnection = this.get(connection.name);
if (!internalConnection) {
return;
}
this.emitter.emit('db:connection:connect', connection);
internalConnection.state = 'open';
}
/**
* Monitors a given connection by listening for lifecycle events
*/
monitorConnection(connection) {
connection.on('disconnect', ($connection) => this.handleDisconnect($connection));
connection.on('connect', ($connection) => this.handleConnect($connection));
connection.on('error', (error, $connection) => {
this.emitter.emit('db:connection:error', [error, $connection]);
});
}
/**
* Add a named connection with it's configuration. Make sure to call `connect`
* before using the connection to make database queries.
*/
add(connectionName, config) {
/**
* Noop when connection already exists. If one wants to change the config, they
* must release the old connection and add a new one
*/
if (this.has(connectionName)) {
return;
}
this.logger.trace({ connection: connectionName }, 'adding new connection to the manager');
this.connections.set(connectionName, {
name: connectionName,
config: config,
state: 'registered',
});
}
/**
* Connect to the database using config for a given named connection
*/
connect(connectionName) {
const connection = this.connections.get(connectionName);
if (!connection) {
throw new utils_1.Exception(`Cannot connect to unregistered connection ${connectionName}`, 500, 'E_UNMANAGED_DB_CONNECTION');
}
/**
* Ignore when the there is already a connection.
*/
if (this.isConnected(connection.name)) {
return;
}
/**
* Create a new connection and monitor it's state
*/
connection.connection = new index_1.Connection(connection.name, connection.config, this.logger);
this.monitorConnection(connection.connection);
connection.connection.connect();
}
/**
* Patching the config
*/
patch(connectionName, config) {
const connection = this.get(connectionName);
/**
* If connection is missing, then simply add it
*/
if (!connection) {
return this.add(connectionName, config);
}
/**
* Move the current connection to the orphan connections. We need
* to keep a seperate track of old connections to make sure
* they cleanup after some time
*/
if (connection.connection) {
this.orphanConnections.add(connection.connection);
connection.connection.disconnect();
}
/**
* Updating config and state. Next call to connect will use the
* new config
*/
connection.state = 'migrating';
connection.config = config;
/**
* Removing the connection right away, so that the next call to `connect`
* creates a new one with new config
*/
delete connection.connection;
}
/**
* Returns the connection node for a given named connection
*/
get(connectionName) {
return this.connections.get(connectionName);
}
/**
* Returns a boolean telling if we have connection details for
* a given named connection. This method doesn't tell if
* connection is connected or not.
*/
has(connectionName) {
return this.connections.has(connectionName);
}
/**
* Returns a boolean telling if connection has been established
* with the database or not
*/
isConnected(connectionName) {
if (!this.has(connectionName)) {
return false;
}
const connection = this.get(connectionName);
return !!connection.connection && connection.state === 'open';
}
/**
* Closes a given connection and can optionally release it from the
* tracking list
*/
async close(connectionName, release = false) {
if (this.isConnected(connectionName)) {
const connection = this.get(connectionName);
await connection.connection.disconnect();
connection.state = 'closing';
}
if (release) {
await this.release(connectionName);
}
}
/**
* Close all tracked connections
*/
async closeAll(release = false) {
await Promise.all(Array.from(this.connections.keys()).map((name) => this.close(name, release)));
}
/**
* Release a connection. This will disconnect the connection
* and will delete it from internal list
*/
async release(connectionName) {
if (this.isConnected(connectionName)) {
await this.close(connectionName, true);
}
else {
this.connections.delete(connectionName);
}
}
/**
* Returns the report for all the connections marked for healthChecks.
*/
async report() {
const reports = await Promise.all(Array.from(this.connections.keys())
.filter((one) => this.get(one).config.healthCheck)
.map((one) => {
this.connect(one);
return this.get(one).connection.getReport();
}));
const healthy = !reports.find((report) => !!report.error);
return {
displayName: 'Database',
health: {
healthy,
message: healthy
? 'All connections are healthy'
: 'One or more connections are not healthy',
},
meta: reports,
};
}
}
exports.ConnectionManager = ConnectionManager;
+127
View File
@@ -0,0 +1,127 @@
/// <reference path="../../adonis-typings/index.d.ts" />
/// <reference types="@adonisjs/logger/build/adonis-typings/logger" />
/// <reference types="node" />
import { Pool } from 'tarn';
import { Knex } from 'knex';
import { EventEmitter } from 'events';
import { LoggerContract } from '@ioc:Adonis/Core/Logger';
import { ConnectionConfig, ConnectionContract, ReportNode } from '@ioc:Adonis/Lucid/Database';
/**
* Connection class manages a given database connection. Internally it uses
* knex to build the database connection with appropriate database
* driver.
*/
export declare class Connection extends EventEmitter implements ConnectionContract {
readonly name: string;
config: ConnectionConfig;
private logger;
/**
* Reference to knex. The instance is created once the `open`
* method is invoked
*/
client?: Knex;
/**
* Read client when read/write replicas are defined in the config, otherwise
* it is a reference to the `client`.
*/
readClient?: Knex;
/**
* Connection dialect name
*/
dialectName: ConnectionContract['dialectName'];
/**
* A boolean to know if connection operates on read/write
* replicas
*/
hasReadWriteReplicas: boolean;
/**
* Config for one or more read replicas. Only exists, when replicas are
* defined
*/
private readReplicas;
/**
* The round robin counter for reading config
*/
private roundRobinCounter;
constructor(name: string, config: ConnectionConfig, logger: LoggerContract);
/**
* Validates the config to ensure that read/write replicas are defined
* properly.
*/
private validateConfig;
/**
* Cleanup references
*/
private cleanup;
/**
* Does cleanup by removing knex reference and removing all listeners.
* For the same of simplicity, we get rid of both read and write
* clients, when anyone of them disconnects.
*/
private monitorPoolResources;
/**
* Returns normalized config object for write replica to be
* used by knex
*/
private getWriteConfig;
/**
* Returns the config for read replicas.
*/
private getReadConfig;
/**
* Resolves connection config for the writer connection
*/
private writeConfigResolver;
/**
* Resolves connection config for the reader connection
*/
private readConfigResolver;
/**
* Creates the write connection.
*/
private setupWriteConnection;
/**
* Creates the read connection. If there aren't any replicas in use, then
* it will use the write client instead.
*/
private setupReadConnection;
/**
* Checks all the read hosts by running a query on them. Stops
* after first error.
*/
private checkReadHosts;
/**
* Checks for the write host
*/
private checkWriteHost;
/**
* Returns the pool instance for the given connection
*/
get pool(): null | Pool<any>;
/**
* Returns the pool instance for the read connection. When replicas are
* not in use, then read/write pools are same.
*/
get readPool(): null | Pool<any>;
/**
* Returns a boolean indicating if the connection is ready for making
* database queries. If not, one must call `connect`.
*/
get ready(): boolean;
/**
* Opens the connection by creating knex instance
*/
connect(): void;
/**
* Closes DB connection by destroying knex instance. The `connection`
* object must be free for garbage collection.
*
* In case of error this method will emit `close:error` event followed
* by the `close` event.
*/
disconnect(): Promise<void>;
/**
* Returns the healthcheck report for the connection
*/
getReport(): Promise<ReportNode>;
}
+405
View File
@@ -0,0 +1,405 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Connection = void 0;
const knex_1 = __importDefault(require("knex"));
const events_1 = require("events");
const utils_1 = require("@poppinss/utils");
const knex_dynamic_connection_1 = require("knex-dynamic-connection");
const helpers_1 = require("knex/lib/util/helpers");
const Logger_1 = require("./Logger");
/**
* Connection class manages a given database connection. Internally it uses
* knex to build the database connection with appropriate database
* driver.
*/
class Connection extends events_1.EventEmitter {
constructor(name, config, logger) {
super();
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: name
});
Object.defineProperty(this, "config", {
enumerable: true,
configurable: true,
writable: true,
value: config
});
Object.defineProperty(this, "logger", {
enumerable: true,
configurable: true,
writable: true,
value: logger
});
/**
* Reference to knex. The instance is created once the `open`
* method is invoked
*/
Object.defineProperty(this, "client", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Read client when read/write replicas are defined in the config, otherwise
* it is a reference to the `client`.
*/
Object.defineProperty(this, "readClient", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Connection dialect name
*/
Object.defineProperty(this, "dialectName", {
enumerable: true,
configurable: true,
writable: true,
value: (0, helpers_1.resolveClientNameWithAliases)(this.config.client)
});
/**
* A boolean to know if connection operates on read/write
* replicas
*/
Object.defineProperty(this, "hasReadWriteReplicas", {
enumerable: true,
configurable: true,
writable: true,
value: !!(this.config.replicas &&
this.config.replicas.read &&
this.config.replicas.write)
});
/**
* Config for one or more read replicas. Only exists, when replicas are
* defined
*/
Object.defineProperty(this, "readReplicas", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
/**
* The round robin counter for reading config
*/
Object.defineProperty(this, "roundRobinCounter", {
enumerable: true,
configurable: true,
writable: true,
value: 0
});
this.validateConfig();
}
/**
* Validates the config to ensure that read/write replicas are defined
* properly.
*/
validateConfig() {
if (this.config.replicas) {
if (!this.config.replicas.read || !this.config.replicas.write) {
throw new utils_1.Exception('Make sure to define read/write replicas or use connection property', 500, 'E_INCOMPLETE_REPLICAS_CONFIG');
}
if (!this.config.replicas.read.connection || !this.config.replicas.read.connection) {
throw new utils_1.Exception('Make sure to define connection property inside read/write replicas', 500, 'E_INVALID_REPLICAS_CONFIG');
}
}
}
/**
* Cleanup references
*/
cleanup() {
this.client = undefined;
this.readClient = undefined;
this.readReplicas = [];
this.roundRobinCounter = 0;
}
/**
* Does cleanup by removing knex reference and removing all listeners.
* For the same of simplicity, we get rid of both read and write
* clients, when anyone of them disconnects.
*/
monitorPoolResources() {
/**
* Pool has destroyed and hence we must cleanup resources
* as well.
*/
this.pool.on('poolDestroySuccess', () => {
this.logger.trace({ connection: this.name }, 'pool destroyed, cleaning up resource');
this.cleanup();
this.emit('disconnect', this);
this.removeAllListeners();
});
if (this.readPool !== this.pool) {
this.readPool.on('poolDestroySuccess', () => {
this.logger.trace({ connection: this.name }, 'pool destroyed, cleaning up resource');
this.cleanup();
this.emit('disconnect', this);
this.removeAllListeners();
});
}
}
/**
* Returns normalized config object for write replica to be
* used by knex
*/
getWriteConfig() {
if (!this.config.replicas) {
return this.config;
}
const { replicas, ...config } = this.config;
/**
* Give preference to the replica write connection when and merge values from
* the main connection object when defined.
*/
if (typeof replicas.write.connection === 'string' || typeof config.connection === 'string') {
config.connection = replicas.write.connection;
}
else {
config.connection = Object.assign({}, config.connection, replicas.write.connection);
}
/**
* Add pool to the config when pool config defined on main connection
* or the write replica
*/
if (config.pool || replicas.write.pool) {
config.pool = Object.assign({}, config.pool, replicas.write.pool);
}
return config;
}
/**
* Returns the config for read replicas.
*/
getReadConfig() {
if (!this.config.replicas) {
return this.config;
}
const { replicas, ...config } = this.config;
/**
* Reading replicas and storing them as a reference, so that we
* can pick a config from replicas as round robin.
*/
this.readReplicas = replicas.read.connection.map((one) => {
if (typeof one === 'string' || typeof config.connection === 'string') {
return one;
}
else {
return Object.assign({}, config.connection, one);
}
});
/**
* Add database property on the main connection, since knexjs needs it
* internally
*/
config.connection = {
database: this.readReplicas[0].database,
};
/**
* Add pool to the config when pool config defined on main connection
* or the read replica
*/
if (config.pool || replicas.read.pool) {
config.pool = Object.assign({}, config.pool, replicas.read.pool);
}
return config;
}
/**
* Resolves connection config for the writer connection
*/
writeConfigResolver(originalConfig) {
return originalConfig.connection;
}
/**
* Resolves connection config for the reader connection
*/
readConfigResolver(originalConfig) {
if (!this.readReplicas.length) {
return originalConfig.connection;
}
const index = this.roundRobinCounter++ % this.readReplicas.length;
this.logger.trace({ connection: this.name }, `round robin using host at ${index} index`);
return this.readReplicas[index];
}
/**
* Creates the write connection.
*/
setupWriteConnection() {
this.client = (0, knex_1.default)(Object.assign({ log: new Logger_1.Logger(this.name, this.logger) }, this.getWriteConfig(), {
debug: false,
}));
(0, knex_dynamic_connection_1.patchKnex)(this.client, this.writeConfigResolver.bind(this));
}
/**
* Creates the read connection. If there aren't any replicas in use, then
* it will use the write client instead.
*/
setupReadConnection() {
if (!this.hasReadWriteReplicas) {
this.readClient = this.client;
return;
}
this.logger.trace({ connection: this.name }, 'setting up read/write replicas');
this.readClient = (0, knex_1.default)(Object.assign({ log: new Logger_1.Logger(this.name, this.logger) }, this.getReadConfig(), {
debug: false,
}));
(0, knex_dynamic_connection_1.patchKnex)(this.readClient, this.readConfigResolver.bind(this));
}
/**
* Checks all the read hosts by running a query on them. Stops
* after first error.
*/
async checkReadHosts() {
const configCopy = Object.assign({ log: new Logger_1.Logger(this.name, this.logger) }, this.config, {
debug: false,
});
let error = null;
// eslint-disable-next-line @typescript-eslint/naming-convention
for (let _ of this.readReplicas) {
configCopy.connection = this.readConfigResolver(this.config);
this.logger.trace({ connection: this.name }, 'spawing health check read connection');
const client = (0, knex_1.default)(configCopy);
try {
if (this.dialectName === 'oracledb') {
await client.raw('SELECT 1 + 1 AS result FROM dual');
}
else {
await client.raw('SELECT 1 + 1 AS result');
}
}
catch (err) {
error = err;
}
/**
* Cleanup client connection
*/
await client.destroy();
this.logger.trace({ connection: this.name }, 'destroying health check read connection');
/**
* Return early when there is an error
*/
if (error) {
break;
}
}
return error;
}
/**
* Checks for the write host
*/
async checkWriteHost() {
try {
if (this.dialectName === 'oracledb') {
await this.client.raw('SELECT 1 + 1 AS result FROM dual');
}
else {
await this.client.raw('SELECT 1 + 1 AS result');
}
}
catch (error) {
return error;
}
}
/**
* Returns the pool instance for the given connection
*/
get pool() {
return this.client ? this.client.client.pool : null;
}
/**
* Returns the pool instance for the read connection. When replicas are
* not in use, then read/write pools are same.
*/
get readPool() {
return this.readClient ? this.readClient.client.pool : null;
}
/**
* Returns a boolean indicating if the connection is ready for making
* database queries. If not, one must call `connect`.
*/
get ready() {
return !!(this.client || this.readClient);
}
/**
* Opens the connection by creating knex instance
*/
connect() {
try {
this.setupWriteConnection();
this.setupReadConnection();
this.monitorPoolResources();
this.emit('connect', this);
}
catch (error) {
this.emit('error', error, this);
throw error;
}
}
/**
* Closes DB connection by destroying knex instance. The `connection`
* object must be free for garbage collection.
*
* In case of error this method will emit `close:error` event followed
* by the `close` event.
*/
async disconnect() {
this.logger.trace({ connection: this.name }, 'destroying connection');
/**
* Disconnect write client
*/
if (this.client) {
try {
await this.client.destroy();
}
catch (error) {
this.emit('disconnect:error', error, this);
}
}
/**
* Disconnect read client when it exists and both clients
* aren't same
*/
if (this.readClient && this.readClient !== this.client) {
try {
await this.readClient.destroy();
}
catch (error) {
this.emit('disconnect:error', error, this);
}
}
}
/**
* Returns the healthcheck report for the connection
*/
async getReport() {
const error = await this.checkWriteHost();
let readError;
if (!error && this.hasReadWriteReplicas) {
readError = await this.checkReadHosts();
}
return {
connection: this.name,
message: readError
? 'Unable to reach one of the read hosts'
: error
? 'Unable to reach the database server'
: 'Connection is healthy',
error: error || readError || null,
};
}
}
exports.Connection = Connection;
@@ -0,0 +1,106 @@
import { SimplePaginatorContract, SimplePaginatorMetaKeys } from '@ioc:Adonis/Lucid/Database';
/**
* Simple paginator works with the data set provided by the standard
* `offset` and `limit` based pagination.
*/
export declare class SimplePaginator extends Array implements SimplePaginatorContract<any> {
private totalNumber;
readonly perPage: number;
readonly currentPage: number;
private qs;
private url;
private rows;
/**
* Naming strategy for the pagination meta keys
*/
static namingStrategy: {
paginationMetaKeys(): SimplePaginatorMetaKeys;
};
/**
* Can be defined at per instance level as well
*/
namingStrategy: {
paginationMetaKeys(): SimplePaginatorMetaKeys;
};
/**
* The first page is always 1
*/
readonly firstPage: number;
/**
* Find if results set is empty or not
*/
readonly isEmpty: boolean;
/**
* Casting `total` to a number. Later, we can think of situations
* to cast it to a bigint
*/
readonly total: number;
/**
* Find if there are total records or not. This is not same as
* `isEmpty`.
*
* The `isEmpty` reports about the current set of results. However `hasTotal`
* reports about the total number of records, regardless of the current.
*/
readonly hasTotal: boolean;
/**
* The Last page number
*/
readonly lastPage: number;
/**
* Find if there are more pages to come
*/
readonly hasMorePages: boolean;
/**
* Find if there are enough results to be paginated or not
*/
readonly hasPages: boolean;
constructor(totalNumber: number, perPage: number, currentPage: number, ...rows: any[]);
/**
* A reference to the result rows
*/
all(): any[];
/**
* Returns JSON meta data
*/
getMeta(): any;
/**
* Returns JSON representation of the paginated
* data
*/
toJSON(): {
meta: any;
data: any[];
};
/**
* Define query string to be appended to the pagination links
*/
queryString(values: {
[key: string]: any;
}): this;
/**
* Define base url for making the pagination links
*/
baseUrl(url: string): this;
/**
* Returns url for a given page. Doesn't validates the integrity of the
* page
*/
getUrl(page: number): string;
/**
* Returns url for the next page
*/
getNextPageUrl(): string | null;
/**
* Returns URL for the previous page
*/
getPreviousPageUrl(): string | null;
/**
* Returns an array of urls under a given range
*/
getUrlsForRange(start: number, end: number): {
url: string;
page: number;
isActive: boolean;
}[];
}
@@ -0,0 +1,230 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.SimplePaginator = void 0;
const qs_1 = require("qs");
const SnakeCase_1 = require("../../Orm/NamingStrategies/SnakeCase");
/**
* Simple paginator works with the data set provided by the standard
* `offset` and `limit` based pagination.
*/
class SimplePaginator extends Array {
constructor(totalNumber, perPage, currentPage, ...rows) {
super(...rows);
Object.defineProperty(this, "totalNumber", {
enumerable: true,
configurable: true,
writable: true,
value: totalNumber
});
Object.defineProperty(this, "perPage", {
enumerable: true,
configurable: true,
writable: true,
value: perPage
});
Object.defineProperty(this, "currentPage", {
enumerable: true,
configurable: true,
writable: true,
value: currentPage
});
Object.defineProperty(this, "qs", {
enumerable: true,
configurable: true,
writable: true,
value: {}
});
Object.defineProperty(this, "url", {
enumerable: true,
configurable: true,
writable: true,
value: '/'
});
Object.defineProperty(this, "rows", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Can be defined at per instance level as well
*/
Object.defineProperty(this, "namingStrategy", {
enumerable: true,
configurable: true,
writable: true,
value: SimplePaginator.namingStrategy
});
/**
* The first page is always 1
*/
Object.defineProperty(this, "firstPage", {
enumerable: true,
configurable: true,
writable: true,
value: 1
});
/**
* Find if results set is empty or not
*/
Object.defineProperty(this, "isEmpty", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Casting `total` to a number. Later, we can think of situations
* to cast it to a bigint
*/
Object.defineProperty(this, "total", {
enumerable: true,
configurable: true,
writable: true,
value: Number(this.totalNumber)
});
/**
* Find if there are total records or not. This is not same as
* `isEmpty`.
*
* The `isEmpty` reports about the current set of results. However `hasTotal`
* reports about the total number of records, regardless of the current.
*/
Object.defineProperty(this, "hasTotal", {
enumerable: true,
configurable: true,
writable: true,
value: this.total > 0
});
/**
* The Last page number
*/
Object.defineProperty(this, "lastPage", {
enumerable: true,
configurable: true,
writable: true,
value: Math.max(Math.ceil(this.total / this.perPage), 1)
});
/**
* Find if there are more pages to come
*/
Object.defineProperty(this, "hasMorePages", {
enumerable: true,
configurable: true,
writable: true,
value: this.lastPage > this.currentPage
});
/**
* Find if there are enough results to be paginated or not
*/
Object.defineProperty(this, "hasPages", {
enumerable: true,
configurable: true,
writable: true,
value: this.lastPage !== 1
});
this.rows = rows;
this.isEmpty = this.rows.length === 0;
}
/**
* A reference to the result rows
*/
all() {
return this.rows;
}
/**
* Returns JSON meta data
*/
getMeta() {
const metaKeys = this.namingStrategy.paginationMetaKeys();
return {
[metaKeys.total]: this.total,
[metaKeys.perPage]: this.perPage,
[metaKeys.currentPage]: this.currentPage,
[metaKeys.lastPage]: this.lastPage,
[metaKeys.firstPage]: this.firstPage,
[metaKeys.firstPageUrl]: this.getUrl(1),
[metaKeys.lastPageUrl]: this.getUrl(this.lastPage),
[metaKeys.nextPageUrl]: this.getNextPageUrl(),
[metaKeys.previousPageUrl]: this.getPreviousPageUrl(),
};
}
/**
* Returns JSON representation of the paginated
* data
*/
toJSON() {
return {
meta: this.getMeta(),
data: this.all(),
};
}
/**
* Define query string to be appended to the pagination links
*/
queryString(values) {
this.qs = values;
return this;
}
/**
* Define base url for making the pagination links
*/
baseUrl(url) {
this.url = url;
return this;
}
/**
* Returns url for a given page. Doesn't validates the integrity of the
* page
*/
getUrl(page) {
const qs = (0, qs_1.stringify)(Object.assign({}, this.qs, { page: page < 1 ? 1 : page }));
return `${this.url}?${qs}`;
}
/**
* Returns url for the next page
*/
getNextPageUrl() {
if (this.hasMorePages) {
return this.getUrl(this.currentPage + 1);
}
return null;
}
/**
* Returns URL for the previous page
*/
getPreviousPageUrl() {
if (this.currentPage > 1) {
return this.getUrl(this.currentPage - 1);
}
return null;
}
/**
* Returns an array of urls under a given range
*/
getUrlsForRange(start, end) {
let urls = [];
for (let i = start; i <= end; i++) {
urls.push({ url: this.getUrl(i), page: i, isActive: i === this.currentPage });
}
return urls;
}
}
exports.SimplePaginator = SimplePaginator;
/**
* Naming strategy for the pagination meta keys
*/
Object.defineProperty(SimplePaginator, "namingStrategy", {
enumerable: true,
configurable: true,
writable: true,
value: new SnakeCase_1.SnakeCaseNamingStrategy()
});
@@ -0,0 +1,721 @@
/// <reference path="../../../adonis-typings/index.d.ts" />
import { Knex } from 'knex';
import { Macroable } from 'macroable';
import { ChainableContract, DBQueryCallback } from '@ioc:Adonis/Lucid/Database';
/**
* The chainable query builder to consturct SQL queries for selecting, updating and
* deleting records.
*
* The API internally uses the knex query builder. However, many of methods may have
* different API.
*/
export declare abstract class Chainable extends Macroable implements ChainableContract {
knexQuery: Knex.QueryBuilder;
private queryCallback;
keysResolver?: ((columnName: string) => string) | undefined;
hasAggregates: boolean;
hasGroupBy: boolean;
hasUnion: boolean;
/**
* Collection where clauses in a 2nd array. Calling `wrapExisting`
* adds a new stack item
*/
private whereStack;
/**
* Returns the recent most array from the where stack
*/
private getRecentStackItem;
/**
* Returns the wrapping method for a given where method
*/
private getWrappingMethod;
/**
* Applies the where clauses
*/
protected applyWhere(): void;
/**
* An array of selected columns
*/
get columns(): ChainableContract['columns'];
/**
* Custom alias for the query results. Ignored if it not a
* subquery
*/
subQueryAlias?: string;
constructor(knexQuery: Knex.QueryBuilder, queryCallback: DBQueryCallback, keysResolver?: ((columnName: string) => string) | undefined);
/**
* Raises exception when only one argument is passed to a where
* clause and it is a string. It means the value is undefined
*/
private validateWhereSingleArgument;
/**
* Returns the value pair for the `whereBetween` clause
*/
private getBetweenPair;
/**
* Normalizes the columns aggregates functions to something
* knex can process.
*/
private normalizeAggregateColumns;
/**
* Resolves the column name considering raw queries as well.
*/
private resolveColumn;
/**
* Resolves column names
*/
protected resolveKey(columns: any, checkForObject?: boolean, returnValue?: any): any;
/**
* Apply existing query flags to a new query builder. This is
* done during clone operation
*/
protected applyQueryFlags(query: Chainable): void;
/**
* Transforms the value to something that knex can internally understand and
* handle. It includes.
*
* 1. Returning the `knexBuilder` for sub queries.
* 2. Returning the `knex.refBuilder` for reference builder.
* 2. Returning the `knexBuilder` for raw queries.
* 3. Wrapping callbacks, so that the end user receives an instance Lucid query
* builder and not knex query builder.
*/
protected transformValue(value: any): any;
/**
* Transforms the user callback to something that knex
* can internally process
*/
protected transformCallback(value: any): any;
/**
* Returns the underlying knex raw query builder for Lucid raw
* query builder
*/
protected transformRaw(value: any): any;
/**
* Define columns for selection
*/
select(...args: any[]): this;
/**
* Select table for the query. Re-calling this method multiple times will
* use the last selected table
*/
from(table: any): this;
/**
* Wrap existing where clauses to its own group
*/
wrapExisting(): this;
/**
* Add a `where` clause
*/
where(key: any, operator?: any, value?: any): this;
/**
* Add a `or where` clause
*/
orWhere(key: any, operator?: any, value?: any): this;
/**
* Alias for `where`
*/
andWhere(key: any, operator?: any, value?: any): this;
/**
* Adding `where not` clause
*/
whereNot(key: any, operator?: any, value?: any): this;
/**
* Adding `or where not` clause
*/
orWhereNot(key: any, operator?: any, value?: any): this;
/**
* Alias for [[whereNot]]
*/
andWhereNot(key: any, operator?: any, value?: any): this;
/**
* Add a where clause on a given column
*/
whereColumn(column: any, operator: any, comparisonColumn?: any): this;
/**
* Add a orWhere clause on a given column
*/
orWhereColumn(column: any, operator: any, comparisonColumn?: any): this;
/**
* Alias for whereColumn
*/
andWhereColumn(column: any, operator: any, comparisonColumn?: any): this;
/**
* Add a whereNot clause on a given column
*/
whereNotColumn(column: any, operator: any, comparisonColumn?: any): this;
/**
* Add a orWhereNotColumn clause on a given column
*/
orWhereNotColumn(column: any, operator: any, comparisonColumn?: any): this;
/**
* Alias for whereNotColumn
*/
andWhereNotColumn(column: any, operator: any, comparisonColumn?: any): this;
/**
* Adding a `where in` clause
*/
whereIn(columns: any, value: any): this;
/**
* Adding a `or where in` clause
*/
orWhereIn(columns: any, value: any): this;
/**
* Alias for [[whereIn]]
*/
andWhereIn(key: any, value: any): this;
/**
* Adding a `where not in` clause
*/
whereNotIn(columns: any, value: any): this;
/**
* Adding a `or where not in` clause
*/
orWhereNotIn(columns: any, value: any): this;
/**
* Alias for [[whereNotIn]]
*/
andWhereNotIn(key: any, value: any): this;
/**
* Adding `where not null` clause
*/
whereNull(key: any): this;
/**
* Adding `or where not null` clause
*/
orWhereNull(key: any): this;
/**
* Alias for [[whereNull]]
*/
andWhereNull(key: any): this;
/**
* Adding `where not null` clause
*/
whereNotNull(key: any): this;
/**
* Adding `or where not null` clause
*/
orWhereNotNull(key: any): this;
/**
* Alias for [[whereNotNull]]
*/
andWhereNotNull(key: any): this;
/**
* Add a `where exists` clause
*/
whereExists(value: any): this;
/**
* Add a `or where exists` clause
*/
orWhereExists(value: any): this;
/**
* Alias for [[whereExists]]
*/
andWhereExists(value: any): this;
/**
* Add a `where not exists` clause
*/
whereNotExists(value: any): this;
/**
* Add a `or where not exists` clause
*/
orWhereNotExists(value: any): this;
/**
* Alias for [[whereNotExists]]
*/
andWhereNotExists(value: any): this;
/**
* Add where between clause
*/
whereBetween(key: any, value: [any, any]): this;
/**
* Add where between clause
*/
orWhereBetween(key: any, value: any): this;
/**
* Alias for [[whereBetween]]
*/
andWhereBetween(key: any, value: any): this;
/**
* Add where between clause
*/
whereNotBetween(key: any, value: any): this;
/**
* Add where between clause
*/
orWhereNotBetween(key: any, value: any): this;
/**
* Alias for [[whereNotBetween]]
*/
andWhereNotBetween(key: any, value: any): this;
/**
* Adding a where clause using raw sql
*/
whereRaw(sql: any, bindings?: any): this;
/**
* Adding a or where clause using raw sql
*/
orWhereRaw(sql: any, bindings?: any): this;
/**
* Alias for [[whereRaw]]
*/
andWhereRaw(sql: any, bindings?: any): this;
/**
* Add a `where like` clause
*/
whereLike(key: any, value: any): this;
/**
* Add a `where like` clause
*/
orWhereLike(key: any, value?: any): this;
/**
* Add a `where like` clause
*/
andWhereLike(key: any, value?: any): this;
/**
* Add a `where like` clause
*/
whereILike(key: any, value?: any): this;
/**
* Add a `where like` clause
*/
orWhereILike(key: any, value?: any): this;
/**
* Add a `where like` clause
*/
andWhereILike(key: any, value?: any): this;
/**
* Define a where clause with value that matches for JSON
*/
whereJson(column: string, value: any): this;
/**
* Define a or where clause with value that matches for JSON
*/
orWhereJson(column: string, value: any): this;
/**
* Define a where clause with value that matches for JSON
*
* @alias whereJson
*/
andWhereJson(column: string, value: any): this;
/**
* Define a where clause with value that matches for JSON
*/
whereNotJson(column: string, value: any): this;
/**
* Define a or where clause with value that matches for JSON
*/
orWhereNotJson(column: string, value: any): this;
/**
* Define a where clause with value that matches for JSON
*
* @alias whereNotJson
*/
andWhereNotJson(column: string, value: any): this;
/**
* Define a where clause with value that matches for a superset of
* JSON
*/
whereJsonSuperset(column: string, value: any): this;
/**
* Define a or where clause with value that matches for a superset of
* JSON
*/
orWhereJsonSuperset(column: string, value: any): this;
/**
* Define or where clause with value that matches for a superset of
* JSON
*
* @alias whereJsonSuperset
*/
andWhereJsonSuperset(column: string, value: any): this;
/**
* Define a where clause with value that matches for a superset of
* JSON
*/
whereNotJsonSuperset(column: string, value: any): this;
/**
* Define a or where clause with value that matches for a superset of
* JSON
*/
orWhereNotJsonSuperset(column: string, value: any): this;
/**
* Define or where clause with value that matches for a superset of
* JSON
*
* @alias whereNotJsonSuperset
*/
andWhereNotJsonSuperset(column: string, value: any): this;
/**
* Define a where clause with value that matches for a subset of
* JSON
*/
whereJsonSubset(column: string, value: any): this;
/**
* Define a or where clause with value that matches for a subset of
* JSON
*/
orWhereJsonSubset(column: string, value: any): this;
/**
* Define or where clause with value that matches for a subset of
* JSON
*
* @alias whereJsonSubset
*/
andWhereJsonSubset(column: string, value: any): this;
/**
* Define a where clause with value that matches for a subset of
* JSON
*/
whereNotJsonSubset(column: string, value: any): this;
/**
* Define a or where clause with value that matches for a subset of
* JSON
*/
orWhereNotJsonSubset(column: string, value: any): this;
/**
* Define or where clause with value that matches for a subset of
* JSON
*
* @alias whereNotJsonSubset
*/
andWhereNotJsonSubset(column: string, value: any): this;
/**
* Adds a where clause with comparison of a value returned
* by a JsonPath given an operator and a value.
*/
whereJsonPath(column: string, jsonPath: string, operator: any, value?: any): this;
/**
* Adds a or where clause with comparison of a value returned
* by a JsonPath given an operator and a value.
*/
orWhereJsonPath(column: string, jsonPath: string, operator: any, value?: any): this;
/**
* Adds a where clause with comparison of a value returned
* by a JsonPath given an operator and a value.
*
* @alias whereJsonPath
*/
andWhereJsonPath(column: string, jsonPath: string, operator: any, value?: any): this;
/**
* Add a join clause
*/
join(table: any, first: any, operator?: any, second?: any): this;
/**
* Add an inner join clause
*/
innerJoin(table: any, first: any, operator?: any, second?: any): this;
/**
* Add a left join clause
*/
leftJoin(table: any, first: any, operator?: any, second?: any): this;
/**
* Add a left outer join clause
*/
leftOuterJoin(table: any, first: any, operator?: any, second?: any): this;
/**
* Add a right join clause
*/
rightJoin(table: any, first: any, operator?: any, second?: any): this;
/**
* Add a right outer join clause
*/
rightOuterJoin(table: any, first: any, operator?: any, second?: any): this;
/**
* Add a full outer join clause
*/
fullOuterJoin(table: any, first: any, operator?: any, second?: any): this;
/**
* Add a cross join clause
*/
crossJoin(table: any, first: any, operator?: any, second?: any): this;
/**
* Add join clause as a raw query
*/
joinRaw(sql: any, bindings?: any): this;
/**
* Adds a having clause. The having clause breaks for `postgreSQL` when
* referencing alias columns, since PG doesn't support alias columns
* being referred within `having` clause. The end user has to
* use raw queries in this case.
*/
having(key: any, operator?: any, value?: any): this;
/**
* Adds or having clause. The having clause breaks for `postgreSQL` when
* referencing alias columns, since PG doesn't support alias columns
* being referred within `having` clause. The end user has to
* use raw queries in this case.
*/
orHaving(key: any, operator?: any, value?: any): this;
/**
* Alias for [[having]]
*/
andHaving(key: any, operator?: any, value?: any): this;
/**
* Adding having in clause to the query
*/
havingIn(key: any, value: any): this;
/**
* Adding or having in clause to the query
*/
orHavingIn(key: any, value: any): this;
/**
* Alias for [[havingIn]]
*/
andHavingIn(key: any, value: any): this;
/**
* Adding having not in clause to the query
*/
havingNotIn(key: any, value: any): this;
/**
* Adding or having not in clause to the query
*/
orHavingNotIn(key: any, value: any): this;
/**
* Alias for [[havingNotIn]]
*/
andHavingNotIn(key: any, value: any): this;
/**
* Adding having null clause
*/
havingNull(key: any): this;
/**
* Adding or having null clause
*/
orHavingNull(key: any): this;
/**
* Alias for [[havingNull]] clause
*/
andHavingNull(key: any): this;
/**
* Adding having not null clause
*/
havingNotNull(key: any): this;
/**
* Adding or having not null clause
*/
orHavingNotNull(key: any): this;
/**
* Alias for [[havingNotNull]] clause
*/
andHavingNotNull(key: any): this;
/**
* Adding `having exists` clause
*/
havingExists(value: any): this;
/**
* Adding `or having exists` clause
*/
orHavingExists(value: any): this;
/**
* Alias for [[havingExists]]
*/
andHavingExists(value: any): this;
/**
* Adding `having not exists` clause
*/
havingNotExists(value: any): this;
/**
* Adding `or having not exists` clause
*/
orHavingNotExists(value: any): this;
/**
* Alias for [[havingNotExists]]
*/
andHavingNotExists(value: any): this;
/**
* Adding `having between` clause
*/
havingBetween(key: any, value: any): this;
/**
* Adding `or having between` clause
*/
orHavingBetween(key: any, value: any): this;
/**
* Alias for [[havingBetween]]
*/
andHavingBetween(key: any, value: any): this;
/**
* Adding `having not between` clause
*/
havingNotBetween(key: any, value: any): this;
/**
* Adding `or having not between` clause
*/
orHavingNotBetween(key: any, value: any): this;
/**
* Alias for [[havingNotBetween]]
*/
andHavingNotBetween(key: any, value: any): this;
/**
* Adding a where clause using raw sql
*/
havingRaw(sql: any, bindings?: any): this;
/**
* Adding a where clause using raw sql
*/
orHavingRaw(sql: any, bindings?: any): this;
/**
* Alias for [[havingRaw]]
*/
andHavingRaw(sql: any, bindings?: any): this;
/**
* Add distinct clause
*/
distinct(...columns: any[]): this;
/**
* Add distinctOn clause
*/
distinctOn(...columns: any[]): this;
/**
* Add group by clause
*/
groupBy(...columns: any[]): this;
/**
* Add group by clause as a raw query
*/
groupByRaw(sql: any, bindings?: any): this;
/**
* Add order by clause
*/
orderBy(column: any, direction?: any): this;
/**
* Add order by clause as a raw query
*/
orderByRaw(sql: any, bindings?: any): this;
/**
* Define select offset
*/
offset(value: number): this;
/**
* Define results limit
*/
limit(value: number): this;
/**
* Define union queries
*/
union(queries: any, wrap?: boolean): this;
/**
* Define union all queries
*/
unionAll(queries: any, wrap?: boolean): this;
/**
* Define intersect queries
*/
intersect(queries: any, wrap?: boolean): this;
/**
* Clear select columns
*/
clearSelect(): this;
/**
* Clear where clauses
*/
clearWhere(): this;
/**
* Clear order by
*/
clearOrder(): this;
/**
* Clear having
*/
clearHaving(): this;
/**
* Clear limit
*/
clearLimit(): this;
/**
* Clear offset
*/
clearOffset(): this;
/**
* Specify `FOR UPDATE` lock mode for a given
* query
*/
forUpdate(...tableNames: string[]): this;
/**
* Specify `FOR SHARE` lock mode for a given
* query
*/
forShare(...tableNames: string[]): this;
/**
* Skip locked rows
*/
skipLocked(): this;
/**
* Fail when query wants a locked row
*/
noWait(): this;
/**
* Define `with` CTE
*/
with(alias: any, query: any, columns?: string[]): this;
/**
* Define `with` CTE with recursive keyword
*/
withRecursive(alias: any, query: any, columns?: string[]): this;
/**
* Define `with materialized` CTE
*/
withMaterialized(alias: any, query: any, columns?: string[]): this;
/**
* Define not `with materialized` CTE
*/
withNotMaterialized(alias: any, query: any, columns?: string[]): this;
/**
* Define schema for the table
*/
withSchema(schema: any): this;
/**
* Define table alias
*/
as(alias: any): this;
/**
* Count rows for the current query
*/
count(columns: any, alias?: any): this;
/**
* Count distinct rows for the current query
*/
countDistinct(columns: any, alias?: any): this;
/**
* Make use of `min` aggregate function
*/
min(columns: any, alias?: any): this;
/**
* Make use of `max` aggregate function
*/
max(columns: any, alias?: any): this;
/**
* Make use of `avg` aggregate function
*/
avg(columns: any, alias?: any): this;
/**
* Make use of distinct `avg` aggregate function
*/
avgDistinct(columns: any, alias?: any): this;
/**
* Make use of `sum` aggregate function
*/
sum(columns: any, alias?: any): this;
/**
* Make use of distinct `sum` aggregate function
*/
sumDistinct(columns: any, alias?: any): this;
/**
* A shorthand for applying offset and limit based upon
* the current page
*/
forPage(page: number, perPage: number): this;
/**
* Define a query to constraint to be defined when condition is truthy
*/
if(condition: any, matchCallback: (query: this) => any, noMatchCallback?: (query: this) => any): this;
/**
* Define a query to constraint to be defined when condition is falsy
*/
unless(condition: any, matchCallback: (query: this) => any, noMatchCallback?: (query: this) => any): this;
/**
* Define matching blocks just like `if/else if and else`.
*/
match(...blocks: ([condition: any, callback: (query: this) => any] | ((query: this) => any))[]): this;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,137 @@
/// <reference path="../../../adonis-typings/index.d.ts" />
import { Knex } from 'knex';
import { DialectContract, QueryClientContract, TransactionClientContract, DatabaseQueryBuilderContract } from '@ioc:Adonis/Lucid/Database';
import { Chainable } from './Chainable';
import { SimplePaginator } from '../Paginator/SimplePaginator';
/**
* Database query builder exposes the API to construct and run queries for selecting,
* updating and deleting records.
*/
export declare class DatabaseQueryBuilder extends Chainable implements DatabaseQueryBuilderContract {
client: QueryClientContract;
keysResolver?: ((columnName: string) => string) | undefined;
/**
* Custom data someone want to send to the profiler and the
* query event
*/
private customReporterData;
/**
* Control whether to debug the query or not. The initial
* value is inherited from the query client
*/
private debugQueries;
/**
* Required by macroable
*/
protected static macros: {};
protected static getters: {};
constructor(builder: Knex.QueryBuilder, client: QueryClientContract, keysResolver?: ((columnName: string) => string) | undefined);
/**
* Ensures that we are not executing `update` or `del` when using read only
* client
*/
private ensureCanPerformWrites;
/**
* Returns the log data
*/
private getQueryData;
/**
* Define custom reporter data. It will be merged with
* the existing data
*/
reporterData(data: any): this;
/**
* Delete rows under the current query
*/
del(returning?: string | string[]): this;
/**
* Alias for [[del]]
*/
delete(returning?: string | string[]): this;
/**
* Clone the current query builder
*/
clone(): DatabaseQueryBuilder;
/**
* Define returning columns
*/
returning(columns: any): this;
/**
* Perform update by incrementing value for a given column. Increments
* can be clubbed with `update` as well
*/
increment(column: any, counter?: any): this;
/**
* Perform update by decrementing value for a given column. Decrements
* can be clubbed with `update` as well
*/
decrement(column: any, counter?: any): this;
/**
* Perform update
*/
update(column: any, value?: any, returning?: string | string[]): this;
/**
* Fetch and return first results from the results set. This method
* will implicitly set a `limit` on the query
*/
first(): Promise<any>;
/**
* Fetch and return first results from the results set. This method
* will implicitly set a `limit` on the query
*/
firstOrFail(): Promise<any>;
/**
* Define a query to constraint to be defined when condition is truthy
*/
ifDialect(dialects: DialectContract['name'] | DialectContract['name'][], matchCallback: (query: this) => any, noMatchCallback?: (query: this) => any): this;
/**
* Define a query to constraint to be defined when condition is falsy
*/
unlessDialect(dialects: DialectContract['name'] | DialectContract['name'][], matchCallback: (query: this) => any, noMatchCallback?: (query: this) => any): this;
/**
* Turn on/off debugging for this query
*/
debug(debug: boolean): this;
/**
* Define query timeout
*/
timeout(time: number, options?: {
cancel: boolean;
}): this;
/**
* Returns SQL query as a string
*/
toQuery(): string;
/**
* Run query inside the given transaction
*/
useTransaction(transaction: TransactionClientContract): this;
/**
* Executes the query
*/
exec(): Promise<any>;
/**
* Paginate through rows inside a given table
*/
paginate(page: number, perPage?: number): Promise<SimplePaginator>;
/**
* Get sql representation of the query
*/
toSQL(): Knex.Sql;
/**
* Implementation of `then` for the promise API
*/
then(resolve: any, reject?: any): any;
/**
* Implementation of `catch` for the promise API
*/
catch(reject: any): any;
/**
* Implementation of `finally` for the promise API
*/
finally(fullfilled: any): Promise<any>;
/**
* Required when Promises are extended
*/
get [Symbol.toStringTag](): string;
}
@@ -0,0 +1,318 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.DatabaseQueryBuilder = void 0;
const utils_1 = require("@poppinss/utils");
const Chainable_1 = require("./Chainable");
const QueryRunner_1 = require("../../QueryRunner");
const SimplePaginator_1 = require("../Paginator/SimplePaginator");
/**
* Wrapping the user function for a query callback and give them
* a new instance of the `DatabaseQueryBuilder` and not
* knex.QueryBuilder
*/
const queryCallback = (userFn, keysResolver) => {
return (builder) => {
/**
* Sub queries don't need the client, since client is used to execute the query
* and subqueries are not executed seperately. That's why we just pass
* an empty object.
*
* Other option is to have this method for each instance of the class, but this
* is waste of resources.
*/
const query = new DatabaseQueryBuilder(builder, {}, keysResolver);
userFn(query);
query['applyWhere']();
};
};
/**
* Database query builder exposes the API to construct and run queries for selecting,
* updating and deleting records.
*/
class DatabaseQueryBuilder extends Chainable_1.Chainable {
constructor(builder, client, keysResolver) {
super(builder, queryCallback, keysResolver);
Object.defineProperty(this, "client", {
enumerable: true,
configurable: true,
writable: true,
value: client
});
Object.defineProperty(this, "keysResolver", {
enumerable: true,
configurable: true,
writable: true,
value: keysResolver
});
/**
* Custom data someone want to send to the profiler and the
* query event
*/
Object.defineProperty(this, "customReporterData", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Control whether to debug the query or not. The initial
* value is inherited from the query client
*/
Object.defineProperty(this, "debugQueries", {
enumerable: true,
configurable: true,
writable: true,
value: this.client.debug
});
}
/**
* Ensures that we are not executing `update` or `del` when using read only
* client
*/
ensureCanPerformWrites() {
if (this.client && this.client.mode === 'read') {
throw new utils_1.Exception('Updates and deletes cannot be performed in read mode');
}
}
/**
* Returns the log data
*/
getQueryData() {
return {
connection: this.client.connectionName,
inTransaction: this.client.isTransaction,
...this.customReporterData,
};
}
/**
* Define custom reporter data. It will be merged with
* the existing data
*/
reporterData(data) {
this.customReporterData = data;
return this;
}
/**
* Delete rows under the current query
*/
del(returning) {
this.ensureCanPerformWrites();
returning ? this.knexQuery.del(returning) : this.knexQuery.del();
return this;
}
/**
* Alias for [[del]]
*/
delete(returning) {
return this.del(returning);
}
/**
* Clone the current query builder
*/
clone() {
const clonedQuery = new DatabaseQueryBuilder(this.knexQuery.clone(), this.client);
this.applyQueryFlags(clonedQuery);
clonedQuery.debug(this.debugQueries);
this.customReporterData && clonedQuery.reporterData(this.customReporterData);
return clonedQuery;
}
/**
* Define returning columns
*/
returning(columns) {
if (this.client.dialect.supportsReturningStatement) {
columns = Array.isArray(columns)
? columns.map((column) => this.resolveKey(column))
: this.resolveKey(columns);
this.knexQuery.returning(columns);
}
return this;
}
/**
* Perform update by incrementing value for a given column. Increments
* can be clubbed with `update` as well
*/
increment(column, counter) {
this.ensureCanPerformWrites();
this.knexQuery.increment(this.resolveKey(column, true), counter);
return this;
}
/**
* Perform update by decrementing value for a given column. Decrements
* can be clubbed with `update` as well
*/
decrement(column, counter) {
this.ensureCanPerformWrites();
this.knexQuery.decrement(this.resolveKey(column, true), counter);
return this;
}
/**
* Perform update
*/
update(column, value, returning) {
this.ensureCanPerformWrites();
if (value === undefined && returning === undefined) {
this.knexQuery.update(this.resolveKey(column, true));
}
else if (returning === undefined) {
this.knexQuery.update(this.resolveKey(column), value);
}
else {
this.knexQuery.update(this.resolveKey(column), value, returning);
}
return this;
}
/**
* Fetch and return first results from the results set. This method
* will implicitly set a `limit` on the query
*/
async first() {
const result = await this.limit(1)['exec']();
return result[0] || null;
}
/**
* Fetch and return first results from the results set. This method
* will implicitly set a `limit` on the query
*/
async firstOrFail() {
const row = await this.first();
if (!row) {
throw new utils_1.Exception('Row not found', 404, 'E_ROW_NOT_FOUND');
}
return row;
}
/**
* Define a query to constraint to be defined when condition is truthy
*/
ifDialect(dialects, matchCallback, noMatchCallback) {
dialects = Array.isArray(dialects) ? dialects : [dialects];
if (dialects.includes(this.client.dialect.name)) {
matchCallback(this);
}
else if (noMatchCallback) {
noMatchCallback(this);
}
return this;
}
/**
* Define a query to constraint to be defined when condition is falsy
*/
unlessDialect(dialects, matchCallback, noMatchCallback) {
dialects = Array.isArray(dialects) ? dialects : [dialects];
if (!dialects.includes(this.client.dialect.name)) {
matchCallback(this);
}
else if (noMatchCallback) {
noMatchCallback(this);
}
return this;
}
/**
* Turn on/off debugging for this query
*/
debug(debug) {
this.debugQueries = debug;
return this;
}
/**
* Define query timeout
*/
timeout(time, options) {
this.knexQuery['timeout'](time, options);
return this;
}
/**
* Returns SQL query as a string
*/
toQuery() {
this.applyWhere();
return this.knexQuery.toQuery();
}
/**
* Run query inside the given transaction
*/
useTransaction(transaction) {
this.knexQuery.transacting(transaction.knexClient);
return this;
}
/**
* Executes the query
*/
async exec() {
this.applyWhere();
return new QueryRunner_1.QueryRunner(this.client, this.debugQueries, this.getQueryData()).run(this.knexQuery);
}
/**
* Paginate through rows inside a given table
*/
async paginate(page, perPage = 20) {
/**
* Cast to number
*/
page = Number(page);
perPage = Number(perPage);
const countQuery = this.client
.query()
.from(this.clone().clearOrder().clearLimit().clearOffset().as('subQuery'))
.count('* as total');
const aggregates = await countQuery.exec();
const total = aggregates[0].total;
const results = total > 0 ? await this.forPage(page, perPage).exec() : [];
return new SimplePaginator_1.SimplePaginator(total, perPage, page, ...results);
}
/**
* Get sql representation of the query
*/
toSQL() {
this.applyWhere();
return this.knexQuery.toSQL();
}
/**
* Implementation of `then` for the promise API
*/
then(resolve, reject) {
return this.exec().then(resolve, reject);
}
/**
* Implementation of `catch` for the promise API
*/
catch(reject) {
return this.exec().catch(reject);
}
/**
* Implementation of `finally` for the promise API
*/
finally(fullfilled) {
return this.exec().finally(fullfilled);
}
/**
* Required when Promises are extended
*/
get [Symbol.toStringTag]() {
return this.constructor.name;
}
}
exports.DatabaseQueryBuilder = DatabaseQueryBuilder;
/**
* Required by macroable
*/
Object.defineProperty(DatabaseQueryBuilder, "macros", {
enumerable: true,
configurable: true,
writable: true,
value: {}
});
Object.defineProperty(DatabaseQueryBuilder, "getters", {
enumerable: true,
configurable: true,
writable: true,
value: {}
});
@@ -0,0 +1,111 @@
/// <reference path="../../../adonis-typings/index.d.ts" />
import { Knex } from 'knex';
import { Macroable } from 'macroable';
import { QueryClientContract, TransactionClientContract, InsertQueryBuilderContract } from '@ioc:Adonis/Lucid/Database';
/**
* Exposes the API for performing SQL inserts
*/
export declare class InsertQueryBuilder extends Macroable implements InsertQueryBuilderContract {
knexQuery: Knex.QueryBuilder;
client: QueryClientContract;
/**
* Custom data someone want to send to the profiler and the
* query event
*/
private customReporterData;
/**
* Control whether to debug the query or not. The initial
* value is inherited from the query client
*/
private debugQueries;
/**
* Required by macroable
*/
protected static macros: {};
protected static getters: {};
constructor(knexQuery: Knex.QueryBuilder, client: QueryClientContract);
/**
* Returns the log data
*/
private getQueryData;
/**
* Transforms the value to something that knex can internally understand and
* handle. It includes.
*
* 1. Returning the `knexBuilder` for sub queries.
* 2. Returning the `knexBuilder` for raw queries.
*/
protected transformValue(value: any): any;
/**
* Returns the underlying knex raw query builder for Lucid raw
* query builder
*/
protected transformRaw(value: any): any;
/**
* Define custom reporter data. It will be merged with
* the existing data
*/
reporterData(data: any): this;
/**
* Define table for performing the insert query
*/
table(table: any): this;
/**
* Define schema for the table
*/
withSchema(schema: any): this;
/**
* Define returning columns for the insert query
*/
returning(column: any): any;
/**
* Perform insert query
*/
insert(columns: any): this;
/**
* Insert multiple rows in a single query
*/
multiInsert(columns: any): this;
/**
* Turn on/off debugging for this query
*/
debug(debug: boolean): this;
/**
* Define query timeout
*/
timeout(time: number, options?: {
cancel: boolean;
}): this;
/**
* Returns SQL query as a string
*/
toQuery(): string;
/**
* Run query inside the given transaction
*/
useTransaction(transaction: TransactionClientContract): this;
/**
* Executes the query
*/
exec(): Promise<any>;
/**
* Get sql representation of the query
*/
toSQL(): Knex.Sql;
/**
* Implementation of `then` for the promise API
*/
then(resolve: any, reject?: any): any;
/**
* Implementation of `catch` for the promise API
*/
catch(reject: any): any;
/**
* Implementation of `finally` for the promise API
*/
finally(fullfilled: any): Promise<any>;
/**
* Required when Promises are extended
*/
get [Symbol.toStringTag](): string;
}
+231
View File
@@ -0,0 +1,231 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.InsertQueryBuilder = void 0;
const macroable_1 = require("macroable");
const Raw_1 = require("./Raw");
const QueryRunner_1 = require("../../QueryRunner");
const Raw_2 = require("../StaticBuilder/Raw");
const Reference_1 = require("../StaticBuilder/Reference");
/**
* Exposes the API for performing SQL inserts
*/
class InsertQueryBuilder extends macroable_1.Macroable {
constructor(knexQuery, client) {
super();
Object.defineProperty(this, "knexQuery", {
enumerable: true,
configurable: true,
writable: true,
value: knexQuery
});
Object.defineProperty(this, "client", {
enumerable: true,
configurable: true,
writable: true,
value: client
});
/**
* Custom data someone want to send to the profiler and the
* query event
*/
Object.defineProperty(this, "customReporterData", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Control whether to debug the query or not. The initial
* value is inherited from the query client
*/
Object.defineProperty(this, "debugQueries", {
enumerable: true,
configurable: true,
writable: true,
value: this.client.debug
});
}
/**
* Returns the log data
*/
getQueryData() {
return {
connection: this.client.connectionName,
inTransaction: this.client.isTransaction,
...this.customReporterData,
};
}
/**
* Transforms the value to something that knex can internally understand and
* handle. It includes.
*
* 1. Returning the `knexBuilder` for sub queries.
* 2. Returning the `knexBuilder` for raw queries.
*/
transformValue(value) {
if (value instanceof Reference_1.ReferenceBuilder) {
return value.toKnex(this.knexQuery.client);
}
return this.transformRaw(value);
}
/**
* Returns the underlying knex raw query builder for Lucid raw
* query builder
*/
transformRaw(value) {
if (value instanceof Raw_1.RawQueryBuilder) {
return value['knexQuery'];
}
if (value instanceof Raw_2.RawBuilder) {
return value.toKnex(this.knexQuery.client);
}
return value;
}
/**
* Define custom reporter data. It will be merged with
* the existing data
*/
reporterData(data) {
this.customReporterData = data;
return this;
}
/**
* Define table for performing the insert query
*/
table(table) {
this.knexQuery.table(table);
return this;
}
/**
* Define schema for the table
*/
withSchema(schema) {
this.knexQuery.withSchema(schema);
return this;
}
/**
* Define returning columns for the insert query
*/
returning(column) {
if (this.client.dialect.supportsReturningStatement) {
this.knexQuery.returning(column);
}
return this;
}
/**
* Perform insert query
*/
insert(columns) {
if (columns && Array.isArray(columns)) {
columns = columns.map((column) => {
return column && typeof column === 'object'
? Object.keys(column).reduce((result, key) => {
result[key] = this.transformValue(column[key]);
return result;
}, {})
: column;
});
}
else if (columns && typeof columns === 'object') {
columns = Object.keys(columns).reduce((result, key) => {
result[key] = this.transformValue(columns[key]);
return result;
}, {});
}
this.knexQuery.insert(columns);
return this;
}
/**
* Insert multiple rows in a single query
*/
multiInsert(columns) {
return this.insert(columns);
}
/**
* Turn on/off debugging for this query
*/
debug(debug) {
this.debugQueries = debug;
return this;
}
/**
* Define query timeout
*/
timeout(time, options) {
this.knexQuery['timeout'](time, options);
return this;
}
/**
* Returns SQL query as a string
*/
toQuery() {
return this.knexQuery.toQuery();
}
/**
* Run query inside the given transaction
*/
useTransaction(transaction) {
this.knexQuery.transacting(transaction.knexClient);
return this;
}
/**
* Executes the query
*/
async exec() {
return new QueryRunner_1.QueryRunner(this.client, this.debugQueries, this.getQueryData()).run(this.knexQuery);
}
/**
* Get sql representation of the query
*/
toSQL() {
return this.knexQuery.toSQL();
}
/**
* Implementation of `then` for the promise API
*/
then(resolve, reject) {
return this.exec().then(resolve, reject);
}
/**
* Implementation of `catch` for the promise API
*/
catch(reject) {
return this.exec().catch(reject);
}
/**
* Implementation of `finally` for the promise API
*/
finally(fullfilled) {
return this.exec().finally(fullfilled);
}
/**
* Required when Promises are extended
*/
get [Symbol.toStringTag]() {
return this.constructor.name;
}
}
exports.InsertQueryBuilder = InsertQueryBuilder;
/**
* Required by macroable
*/
Object.defineProperty(InsertQueryBuilder, "macros", {
enumerable: true,
configurable: true,
writable: true,
value: {}
});
Object.defineProperty(InsertQueryBuilder, "getters", {
enumerable: true,
configurable: true,
writable: true,
value: {}
});
+76
View File
@@ -0,0 +1,76 @@
/// <reference path="../../../adonis-typings/index.d.ts" />
import { Knex } from 'knex';
import { QueryClientContract, RawQueryBuilderContract, TransactionClientContract } from '@ioc:Adonis/Lucid/Database';
/**
* Exposes the API to execute raw queries
*/
export declare class RawQueryBuilder implements RawQueryBuilderContract {
knexQuery: Knex.Raw;
client: QueryClientContract;
/**
* Custom data someone want to send to the profiler and the
* query event
*/
private customReporterData;
/**
* Control whether to debug the query or not. The initial
* value is inherited from the query client
*/
private debugQueries;
constructor(knexQuery: Knex.Raw, client: QueryClientContract);
/**
* Returns the log data
*/
private getQueryData;
/**
* Define custom reporter data. It will be merged with
* the existing data
*/
reporterData(data: any): this;
/**
* Wrap the query with before/after strings.
*/
wrap(before: string, after: string): this;
/**
* Turn on/off debugging for this query
*/
debug(debug: boolean): this;
/**
* Define query timeout
*/
timeout(time: number, options?: {
cancel: boolean;
}): this;
/**
* Returns SQL query as a string
*/
toQuery(): string;
/**
* Run query inside the given transaction
*/
useTransaction(transaction: TransactionClientContract): this;
/**
* Executes the query
*/
exec(): Promise<any>;
/**
* Get sql representation of the query
*/
toSQL(): Knex.Sql;
/**
* Implementation of `then` for the promise API
*/
then(resolve: any, reject?: any): any;
/**
* Implementation of `catch` for the promise API
*/
catch(reject: any): any;
/**
* Implementation of `finally` for the promise API
*/
finally(fullfilled: any): Promise<any>;
/**
* Required when Promises are extended
*/
get [Symbol.toStringTag](): string;
}
+140
View File
@@ -0,0 +1,140 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.RawQueryBuilder = void 0;
const QueryRunner_1 = require("../../QueryRunner");
/**
* Exposes the API to execute raw queries
*/
class RawQueryBuilder {
constructor(knexQuery, client) {
Object.defineProperty(this, "knexQuery", {
enumerable: true,
configurable: true,
writable: true,
value: knexQuery
});
Object.defineProperty(this, "client", {
enumerable: true,
configurable: true,
writable: true,
value: client
});
/**
* Custom data someone want to send to the profiler and the
* query event
*/
Object.defineProperty(this, "customReporterData", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Control whether to debug the query or not. The initial
* value is inherited from the query client
*/
Object.defineProperty(this, "debugQueries", {
enumerable: true,
configurable: true,
writable: true,
value: this.client.debug
});
}
/**
* Returns the log data
*/
getQueryData() {
return {
connection: this.client.connectionName,
inTransaction: this.client.isTransaction,
...this.customReporterData,
};
}
/**
* Define custom reporter data. It will be merged with
* the existing data
*/
reporterData(data) {
this.customReporterData = data;
return this;
}
/**
* Wrap the query with before/after strings.
*/
wrap(before, after) {
this.knexQuery.wrap(before, after);
return this;
}
/**
* Turn on/off debugging for this query
*/
debug(debug) {
this.debugQueries = debug;
return this;
}
/**
* Define query timeout
*/
timeout(time, options) {
this.knexQuery['timeout'](time, options);
return this;
}
/**
* Returns SQL query as a string
*/
toQuery() {
return this.knexQuery.toQuery();
}
/**
* Run query inside the given transaction
*/
useTransaction(transaction) {
this.knexQuery.transacting(transaction.knexClient);
return this;
}
/**
* Executes the query
*/
async exec() {
return new QueryRunner_1.QueryRunner(this.client, this.debugQueries, this.getQueryData()).run(this.knexQuery);
}
/**
* Get sql representation of the query
*/
toSQL() {
return this.knexQuery.toSQL();
}
/**
* Implementation of `then` for the promise API
*/
then(resolve, reject) {
return this.exec().then(resolve, reject);
}
/**
* Implementation of `catch` for the promise API
*/
catch(reject) {
return this.exec().catch(reject);
}
/**
* Implementation of `finally` for the promise API
*/
finally(fullfilled) {
return this.exec().finally(fullfilled);
}
/**
* Required when Promises are extended
*/
get [Symbol.toStringTag]() {
return this.constructor.name;
}
}
exports.RawQueryBuilder = RawQueryBuilder;
+22
View File
@@ -0,0 +1,22 @@
/// <reference path="../../../adonis-typings/index.d.ts" />
import { Knex } from 'knex';
import { RawBuilderContract } from '@ioc:Adonis/Lucid/Database';
/**
* Exposes the API to construct raw queries. If you want to execute
* raw queries, you can use the RawQueryBuilder
*/
export declare class RawBuilder implements RawBuilderContract {
private sql;
private bindings?;
private wrapBefore;
private wrapAfter;
constructor(sql: string, bindings?: any);
/**
* Wrap the query with before/after strings.
*/
wrap(before: string, after: string): this;
/**
* Converts the raw query to knex raw query instance
*/
toKnex(client: Knex.Client): Knex.Raw;
}
+62
View File
@@ -0,0 +1,62 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.RawBuilder = void 0;
/**
* Exposes the API to construct raw queries. If you want to execute
* raw queries, you can use the RawQueryBuilder
*/
class RawBuilder {
constructor(sql, bindings) {
Object.defineProperty(this, "sql", {
enumerable: true,
configurable: true,
writable: true,
value: sql
});
Object.defineProperty(this, "bindings", {
enumerable: true,
configurable: true,
writable: true,
value: bindings
});
Object.defineProperty(this, "wrapBefore", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "wrapAfter", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
/**
* Wrap the query with before/after strings.
*/
wrap(before, after) {
this.wrapAfter = after;
this.wrapBefore = before;
return this;
}
/**
* Converts the raw query to knex raw query instance
*/
toKnex(client) {
const rawQuery = client.raw(this.sql, this.bindings);
if (this.wrapBefore && this.wrapAfter) {
rawQuery.wrap(this.wrapBefore, this.wrapAfter);
}
return rawQuery;
}
}
exports.RawBuilder = RawBuilder;
@@ -0,0 +1,25 @@
/// <reference path="../../../adonis-typings/index.d.ts" />
import { Knex } from 'knex';
import { ReferenceBuilderContract } from '@ioc:Adonis/Lucid/Database';
/**
* Reference builder to create SQL reference values
*/
export declare class ReferenceBuilder implements ReferenceBuilderContract {
private ref;
private client;
private schema;
private alias;
constructor(ref: string, client: Knex.Client);
/**
* Define schema
*/
withSchema(schema: string): this;
/**
* Define alias
*/
as(alias: string): this;
/**
* Converts reference to knex
*/
toKnex(client?: Knex.Client): Knex.Ref<any, any>;
}
@@ -0,0 +1,66 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReferenceBuilder = void 0;
/**
* Reference builder to create SQL reference values
*/
class ReferenceBuilder {
constructor(ref, client) {
Object.defineProperty(this, "ref", {
enumerable: true,
configurable: true,
writable: true,
value: ref
});
Object.defineProperty(this, "client", {
enumerable: true,
configurable: true,
writable: true,
value: client
});
Object.defineProperty(this, "schema", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "alias", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
/**
* Define schema
*/
withSchema(schema) {
this.schema = schema;
return this;
}
/**
* Define alias
*/
as(alias) {
this.alias = alias;
return this;
}
/**
* Converts reference to knex
*/
toKnex(client) {
const ref = (client || this.client).ref(this.ref);
this.schema && ref.withSchema(this.schema);
this.alias && ref.as(this.alias);
return ref;
}
}
exports.ReferenceBuilder = ReferenceBuilder;
+163
View File
@@ -0,0 +1,163 @@
/// <reference path="../../adonis-typings/index.d.ts" />
/// <reference path="../../adonis-typings/querybuilder.d.ts" />
/// <reference path="../../adonis-typings/database.d.ts" />
/// <reference path="../../adonis-typings/model.d.ts" />
/// <reference path="../../adonis-typings/orm.d.ts" />
/// <reference path="../../adonis-typings/relations.d.ts" />
/// <reference types="@adonisjs/logger/build/adonis-typings/logger" />
/// <reference types="@adonisjs/profiler/build/adonis-typings/profiler" />
/// <reference types="@adonisjs/events/build/adonis-typings" />
/// <reference types="@adonisjs/core/build/adonis-typings/health-check" />
import { Macroable } from 'macroable';
import { EmitterContract } from '@ioc:Adonis/Core/Event';
import { LoggerContract } from '@ioc:Adonis/Core/Logger';
import { ProfilerContract } from '@ioc:Adonis/Core/Profiler';
import { DatabaseConfig, IsolationLevels, DatabaseContract, QueryClientContract, DatabaseClientOptions, TransactionClientContract, ConnectionManagerContract } from '@ioc:Adonis/Lucid/Database';
import { RawBuilder } from './StaticBuilder/Raw';
import { prettyPrint } from '../Helpers/prettyPrint';
import { ModelQueryBuilder } from '../Orm/QueryBuilder';
import { InsertQueryBuilder } from './QueryBuilder/Insert';
import { ReferenceBuilder } from './StaticBuilder/Reference';
import { SimplePaginator } from './Paginator/SimplePaginator';
import { DatabaseQueryBuilder } from './QueryBuilder/Database';
/**
* Database class exposes the API to manage multiple connections and obtain an instance
* of query/transaction clients.
*/
export declare class Database extends Macroable implements DatabaseContract {
private config;
private logger;
private profiler;
private emitter;
/**
* Required by macroable
*/
protected static macros: {};
protected static getters: {};
/**
* Reference to self constructor. TypeScript sucks with "this.constructor"
* https://github.com/microsoft/TypeScript/issues/4586
*/
Database: typeof Database;
/**
* Reference to connections manager
*/
manager: ConnectionManagerContract;
/**
* Primary connection name
*/
primaryConnectionName: string;
/**
* Reference to query builders. We expose them, so that they can be
* extended from outside using macros.
*/
DatabaseQueryBuilder: typeof DatabaseQueryBuilder;
InsertQueryBuilder: typeof InsertQueryBuilder;
ModelQueryBuilder: typeof ModelQueryBuilder;
SimplePaginator: typeof SimplePaginator;
/**
* A store of global transactions
*/
connectionGlobalTransactions: Map<string, TransactionClientContract>;
hasHealthChecksEnabled: boolean;
prettyPrint: typeof prettyPrint;
constructor(config: DatabaseConfig, logger: LoggerContract, profiler: ProfilerContract, emitter: EmitterContract);
/**
* Validate config at runtime
*/
private validateConfig;
/**
* Compute whether health check is enabled or not after registering the connections.
* There are chances that all pre-registered connections are not using health
* checks but a dynamic connection is using it. We don't support that use case
* for now, since it complicates things a lot and forces us to register the
* health checker on demand.
*/
private findIfHealthChecksAreEnabled;
/**
* Registering all connections with the manager, so that we can fetch
* and connect with them whenver required.
*/
private registerConnections;
/**
* Returns the connection node from the connection manager
*/
getRawConnection(name: string): import("@ioc:Adonis/Lucid/Database").ConnectionNode | undefined;
/**
* Returns the query client for a given connection
*/
connection(connection?: string, options?: DatabaseClientOptions): QueryClientContract | TransactionClientContract;
/**
* Returns the knex query builder
*/
knexQuery(): import("knex").Knex.QueryBuilder<any, any>;
/**
* Returns the knex raw query builder
*/
knexRawQuery(sql: string, bindings?: any[]): import("knex").Knex.Raw<any>;
/**
* Returns query builder. Optionally one can define the mode as well
*/
query(options?: DatabaseClientOptions): import("@ioc:Adonis/Lucid/Database").DatabaseQueryBuilderContract<any>;
/**
* Returns insert query builder. Always has to be dual or write mode and
* hence it doesn't matter, since in both `dual` and `write` mode,
* the `write` connection is always used.
*/
insertQuery(options?: DatabaseClientOptions): import("@ioc:Adonis/Lucid/Database").InsertQueryBuilderContract<any[]>;
/**
* Returns a query builder instance for a given model.
*/
modelQuery(model: any, options?: DatabaseClientOptions): import("@ioc:Adonis/Lucid/Orm").ModelQueryBuilderContract<any, any>;
/**
* Returns an instance of raw query builder. Optionally one can
* defined the `read/write` mode in which to execute the
* query
*/
rawQuery(sql: string, bindings?: any, options?: DatabaseClientOptions): import("@ioc:Adonis/Lucid/Database").RawQueryBuilderContract<any>;
/**
* Returns an instance of raw builder. This raw builder queries
* cannot be executed. Use `rawQuery`, if you want to execute
* queries raw queries.
*/
raw(sql: string, bindings?: any): RawBuilder;
/**
* Returns reference builder.
*/
ref(reference: string): ReferenceBuilder;
/**
* Returns instance of a query builder and selects the table
*/
from(table: any): import("@ioc:Adonis/Lucid/Database").DatabaseQueryBuilderContract<any>;
/**
* Returns insert query builder and selects the table
*/
table(table: any): import("@ioc:Adonis/Lucid/Database").InsertQueryBuilderContract<any[]>;
/**
* Returns a transaction instance on the default
* connection
*/
transaction(callback?: {
isolationLevel?: IsolationLevels;
} | ((trx: TransactionClientContract) => Promise<any>), options?: {
isolationLevel?: IsolationLevels;
}): Promise<any>;
/**
* Invokes `manager.report`
*/
report(): Promise<import("@ioc:Adonis/Core/HealthCheck").HealthReportEntry & {
meta: import("@ioc:Adonis/Lucid/Database").ReportNode[];
}>;
/**
* Begin a new global transaction
*/
beginGlobalTransaction(connectionName?: string, options?: Omit<DatabaseClientOptions, 'mode'>): Promise<TransactionClientContract>;
/**
* Commit an existing global transaction
*/
commitGlobalTransaction(connectionName?: string): Promise<void>;
/**
* Rollback an existing global transaction
*/
rollbackGlobalTransaction(connectionName?: string): Promise<void>;
}
+378
View File
@@ -0,0 +1,378 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Database = void 0;
/// <reference path="../../adonis-typings/index.ts" />
const macroable_1 = require("macroable");
const utils_1 = require("@poppinss/utils");
const QueryClient_1 = require("../QueryClient");
const Raw_1 = require("./StaticBuilder/Raw");
const prettyPrint_1 = require("../Helpers/prettyPrint");
const QueryBuilder_1 = require("../Orm/QueryBuilder");
const Manager_1 = require("../Connection/Manager");
const Insert_1 = require("./QueryBuilder/Insert");
const Reference_1 = require("./StaticBuilder/Reference");
const SimplePaginator_1 = require("./Paginator/SimplePaginator");
const Database_1 = require("./QueryBuilder/Database");
/**
* Database class exposes the API to manage multiple connections and obtain an instance
* of query/transaction clients.
*/
class Database extends macroable_1.Macroable {
constructor(config, logger, profiler, emitter) {
super();
Object.defineProperty(this, "config", {
enumerable: true,
configurable: true,
writable: true,
value: config
});
Object.defineProperty(this, "logger", {
enumerable: true,
configurable: true,
writable: true,
value: logger
});
Object.defineProperty(this, "profiler", {
enumerable: true,
configurable: true,
writable: true,
value: profiler
});
Object.defineProperty(this, "emitter", {
enumerable: true,
configurable: true,
writable: true,
value: emitter
});
/**
* Reference to self constructor. TypeScript sucks with "this.constructor"
* https://github.com/microsoft/TypeScript/issues/4586
*/
Object.defineProperty(this, "Database", {
enumerable: true,
configurable: true,
writable: true,
value: Database
});
/**
* Reference to connections manager
*/
Object.defineProperty(this, "manager", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
/**
* Primary connection name
*/
Object.defineProperty(this, "primaryConnectionName", {
enumerable: true,
configurable: true,
writable: true,
value: this.config.connection
});
/**
* Reference to query builders. We expose them, so that they can be
* extended from outside using macros.
*/
Object.defineProperty(this, "DatabaseQueryBuilder", {
enumerable: true,
configurable: true,
writable: true,
value: Database_1.DatabaseQueryBuilder
});
Object.defineProperty(this, "InsertQueryBuilder", {
enumerable: true,
configurable: true,
writable: true,
value: Insert_1.InsertQueryBuilder
});
Object.defineProperty(this, "ModelQueryBuilder", {
enumerable: true,
configurable: true,
writable: true,
value: QueryBuilder_1.ModelQueryBuilder
});
Object.defineProperty(this, "SimplePaginator", {
enumerable: true,
configurable: true,
writable: true,
value: SimplePaginator_1.SimplePaginator
});
/**
* A store of global transactions
*/
Object.defineProperty(this, "connectionGlobalTransactions", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
Object.defineProperty(this, "hasHealthChecksEnabled", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
Object.defineProperty(this, "prettyPrint", {
enumerable: true,
configurable: true,
writable: true,
value: prettyPrint_1.prettyPrint
});
this.validateConfig();
this.manager = new Manager_1.ConnectionManager(this.logger, this.emitter);
this.registerConnections();
this.findIfHealthChecksAreEnabled();
}
/**
* Validate config at runtime
*/
validateConfig() {
const validator = new utils_1.ManagerConfigValidator(this.config, 'database', 'config/database');
validator.validateDefault('connection');
validator.validateList('connections', 'connection');
}
/**
* Compute whether health check is enabled or not after registering the connections.
* There are chances that all pre-registered connections are not using health
* checks but a dynamic connection is using it. We don't support that use case
* for now, since it complicates things a lot and forces us to register the
* health checker on demand.
*/
findIfHealthChecksAreEnabled() {
for (let [, conn] of this.manager.connections) {
if (conn.config.healthCheck) {
this.hasHealthChecksEnabled = true;
break;
}
}
}
/**
* Registering all connections with the manager, so that we can fetch
* and connect with them whenver required.
*/
registerConnections() {
Object.keys(this.config.connections).forEach((name) => {
this.manager.add(name, this.config.connections[name]);
});
}
/**
* Returns the connection node from the connection manager
*/
getRawConnection(name) {
return this.manager.get(name);
}
/**
* Returns the query client for a given connection
*/
connection(connection = this.primaryConnectionName, options) {
options = options || {};
/**
* Use default profiler, when no profiler is defined when obtaining
* the query client for a given connection.
*/
if (!options.profiler) {
options.profiler = this.profiler;
}
/**
* Connect is noop when already connected
*/
this.manager.connect(connection);
/**
* Disallow modes other than `read` or `write`
*/
if (options.mode && !['read', 'write'].includes(options.mode)) {
throw new utils_1.Exception(`Invalid mode ${options.mode}. Must be read or write`);
}
/**
* Return the global transaction when it already exists.
*/
if (this.connectionGlobalTransactions.has(connection)) {
this.logger.trace({ connection }, 'using pre-existing global transaction connection');
const globalTransactionClient = this.connectionGlobalTransactions.get(connection);
return globalTransactionClient;
}
/**
* Fetching connection for the given name
*/
const rawConnection = this.getRawConnection(connection).connection;
/**
* Generating query client for a given connection and setting appropriate
* mode on it
*/
this.logger.trace({ connection }, 'creating query client in %s mode', [options.mode || 'dual']);
const queryClient = options.mode
? new QueryClient_1.QueryClient(options.mode, rawConnection, this.emitter)
: new QueryClient_1.QueryClient('dual', rawConnection, this.emitter);
/**
* Passing profiler to the query client for profiling queries
*/
queryClient.profiler = options.profiler;
return queryClient;
}
/**
* Returns the knex query builder
*/
knexQuery() {
return this.connection(this.primaryConnectionName).knexQuery();
}
/**
* Returns the knex raw query builder
*/
knexRawQuery(sql, bindings) {
return this.connection(this.primaryConnectionName).knexRawQuery(sql, bindings);
}
/**
* Returns query builder. Optionally one can define the mode as well
*/
query(options) {
return this.connection(this.primaryConnectionName, options).query();
}
/**
* Returns insert query builder. Always has to be dual or write mode and
* hence it doesn't matter, since in both `dual` and `write` mode,
* the `write` connection is always used.
*/
insertQuery(options) {
return this.connection(this.primaryConnectionName, options).insertQuery();
}
/**
* Returns a query builder instance for a given model.
*/
modelQuery(model, options) {
return this.connection(this.primaryConnectionName, options).modelQuery(model);
}
/**
* Returns an instance of raw query builder. Optionally one can
* defined the `read/write` mode in which to execute the
* query
*/
rawQuery(sql, bindings, options) {
return this.connection(this.primaryConnectionName, options).rawQuery(sql, bindings);
}
/**
* Returns an instance of raw builder. This raw builder queries
* cannot be executed. Use `rawQuery`, if you want to execute
* queries raw queries.
*/
raw(sql, bindings) {
return new Raw_1.RawBuilder(sql, bindings);
}
/**
* Returns reference builder.
*/
ref(reference) {
return new Reference_1.ReferenceBuilder(reference, this.connection().getReadClient().client);
}
/**
* Returns instance of a query builder and selects the table
*/
from(table) {
return this.connection().from(table);
}
/**
* Returns insert query builder and selects the table
*/
table(table) {
return this.connection().table(table);
}
/**
* Returns a transaction instance on the default
* connection
*/
transaction(callback, options) {
const client = this.connection();
return typeof callback === 'function'
? client.transaction(callback, options)
: client.transaction(callback);
}
/**
* Invokes `manager.report`
*/
report() {
return this.manager.report();
}
/**
* Begin a new global transaction
*/
async beginGlobalTransaction(connectionName, options) {
connectionName = connectionName || this.primaryConnectionName;
/**
* Return global transaction as it is
*/
const globalTrx = this.connectionGlobalTransactions.get(connectionName);
if (globalTrx) {
return globalTrx;
}
/**
* Create a new transaction and store a reference to it
*/
const trx = await this.connection(connectionName, options).transaction();
this.connectionGlobalTransactions.set(trx.connectionName, trx);
/**
* Listen for events to drop the reference when transaction
* is over
*/
trx.on('commit', ($trx) => {
this.connectionGlobalTransactions.delete($trx.connectionName);
});
trx.on('rollback', ($trx) => {
this.connectionGlobalTransactions.delete($trx.connectionName);
});
return trx;
}
/**
* Commit an existing global transaction
*/
async commitGlobalTransaction(connectionName) {
connectionName = connectionName || this.primaryConnectionName;
const trx = this.connectionGlobalTransactions.get(connectionName);
if (!trx) {
throw new utils_1.Exception([
'Cannot commit a non-existing global transaction.',
' Make sure you are not calling "commitGlobalTransaction" twice',
].join(''));
}
await trx.commit();
}
/**
* Rollback an existing global transaction
*/
async rollbackGlobalTransaction(connectionName) {
connectionName = connectionName || this.primaryConnectionName;
const trx = this.connectionGlobalTransactions.get(connectionName);
if (!trx) {
throw new utils_1.Exception([
'Cannot rollback a non-existing global transaction.',
' Make sure you are not calling "commitGlobalTransaction" twice',
].join(''));
}
await trx.rollback();
}
}
exports.Database = Database;
/**
* Required by macroable
*/
Object.defineProperty(Database, "macros", {
enumerable: true,
configurable: true,
writable: true,
value: {}
});
Object.defineProperty(Database, "getters", {
enumerable: true,
configurable: true,
writable: true,
value: {}
});
+6
View File
@@ -0,0 +1,6 @@
/// <reference path="../../adonis-typings/index.d.ts" />
import { DialectContract } from '@ioc:Adonis/Lucid/Database';
import { BaseSqliteDialect } from './SqliteBase';
export declare class BetterSqliteDialect extends BaseSqliteDialect implements DialectContract {
readonly name = "better-sqlite3";
}
+24
View File
@@ -0,0 +1,24 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.BetterSqliteDialect = void 0;
const SqliteBase_1 = require("./SqliteBase");
class BetterSqliteDialect extends SqliteBase_1.BaseSqliteDialect {
constructor() {
super(...arguments);
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: 'better-sqlite3'
});
}
}
exports.BetterSqliteDialect = BetterSqliteDialect;
+44
View File
@@ -0,0 +1,44 @@
/// <reference path="../../adonis-typings/index.d.ts" />
import { DialectContract, MssqlConfig, QueryClientContract } from '@ioc:Adonis/Lucid/Database';
export declare class MssqlDialect implements DialectContract {
private client;
private config;
readonly name = "mssql";
readonly supportsAdvisoryLocks = false;
readonly supportsViews = false;
readonly supportsTypes = false;
readonly supportsReturningStatement = true;
/**
* Reference to the database version. Knex.js fetches the version after
* the first database query, so it will be set to undefined initially
*/
readonly version: any;
/**
* The default format for datetime column. The date formats is
* valid for luxon date parsing library
*/
readonly dateTimeFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZ";
constructor(client: QueryClientContract, config: MssqlConfig);
/**
* Returns an array of table names
*/
getAllTables(): Promise<any[]>;
/**
* Truncate mssql table. Disabling foreign key constriants alone is
* not enough for SQL server.
*
* One has to drop all FK constraints and then re-create them, and
* this all is too much work
*/
truncate(table: string, _: boolean): Promise<void>;
/**
* Drop all tables inside the database
*/
dropAllTables(): Promise<void>;
getAllViews(): Promise<string[]>;
getAllTypes(): Promise<string[]>;
dropAllViews(): Promise<void>;
dropAllTypes(): Promise<void>;
getAdvisoryLock(): Promise<boolean>;
releaseAdvisoryLock(): Promise<boolean>;
}
+143
View File
@@ -0,0 +1,143 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.MssqlDialect = void 0;
/// <reference path="../../adonis-typings/index.ts" />
const Raw_1 = require("../Database/StaticBuilder/Raw");
class MssqlDialect {
constructor(client, config) {
Object.defineProperty(this, "client", {
enumerable: true,
configurable: true,
writable: true,
value: client
});
Object.defineProperty(this, "config", {
enumerable: true,
configurable: true,
writable: true,
value: config
});
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: 'mssql'
});
Object.defineProperty(this, "supportsAdvisoryLocks", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
Object.defineProperty(this, "supportsViews", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
Object.defineProperty(this, "supportsTypes", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
Object.defineProperty(this, "supportsReturningStatement", {
enumerable: true,
configurable: true,
writable: true,
value: true
});
/**
* Reference to the database version. Knex.js fetches the version after
* the first database query, so it will be set to undefined initially
*/
Object.defineProperty(this, "version", {
enumerable: true,
configurable: true,
writable: true,
value: this.client.getReadClient()['context']['client'].version
});
/**
* The default format for datetime column. The date formats is
* valid for luxon date parsing library
*/
Object.defineProperty(this, "dateTimeFormat", {
enumerable: true,
configurable: true,
writable: true,
value: "yyyy-MM-dd'T'HH:mm:ss.SSSZZ"
});
}
/**
* Returns an array of table names
*/
async getAllTables() {
const tables = await this.client
.query()
.from('information_schema.tables')
.select('table_name as table_name')
.where('table_type', 'BASE TABLE')
.where('table_catalog', new Raw_1.RawBuilder('DB_NAME()'))
.whereNot('table_name', 'like', 'spt_%')
.andWhereNot('table_name', 'MSreplication_options')
.orderBy('table_name', 'asc');
return tables.map(({ table_name }) => table_name);
}
/**
* Truncate mssql table. Disabling foreign key constriants alone is
* not enough for SQL server.
*
* One has to drop all FK constraints and then re-create them, and
* this all is too much work
*/
async truncate(table, _) {
return this.client.knexQuery().table(table).truncate();
}
/**
* Drop all tables inside the database
*/
async dropAllTables() {
await this.client.rawQuery(`
DECLARE @sql NVARCHAR(MAX) = N'';
SELECT @sql += 'ALTER TABLE '
+ QUOTENAME(OBJECT_SCHEMA_NAME(parent_object_id)) + '.' + + QUOTENAME(OBJECT_NAME(parent_object_id))
+ ' DROP CONSTRAINT ' + QUOTENAME(name) + ';'
FROM sys.foreign_keys;
EXEC sp_executesql @sql;
`);
const ignoredTables = (this.config.wipe?.ignoreTables || [])
.map((table) => `"${table}"`)
.join(', ');
await this.client.rawQuery(`
EXEC sp_MSforeachtable 'DROP TABLE \\?',
@whereand='AND o.Name NOT IN (${ignoredTables || '""'})'
`);
}
async getAllViews() {
throw new Error('"getAllViews" method not implemented is not implemented for mssql. Create a PR to add the feature');
}
async getAllTypes() {
throw new Error('"getAllTypes" method not implemented is not implemented for mssql. Create a PR to add the feature');
}
async dropAllViews() {
throw new Error('"dropAllViews" method not implemented is not implemented for mssql. Create a PR to add the feature');
}
async dropAllTypes() {
throw new Error('"dropAllTypes" method not implemented is not implemented for mssql. Create a PR to add the feature');
}
getAdvisoryLock() {
throw new Error('Support for advisory locks is not implemented for mssql. Create a PR to add the feature');
}
releaseAdvisoryLock() {
throw new Error('Support for advisory locks is not implemented for mssql. Create a PR to add the feature');
}
}
exports.MssqlDialect = MssqlDialect;
+59
View File
@@ -0,0 +1,59 @@
/// <reference path="../../adonis-typings/index.d.ts" />
import { DialectContract, MysqlConfig, QueryClientContract } from '@ioc:Adonis/Lucid/Database';
export declare class MysqlDialect implements DialectContract {
private client;
private config;
readonly name = "mysql";
readonly supportsAdvisoryLocks = true;
readonly supportsViews = true;
readonly supportsTypes = false;
readonly supportsReturningStatement = false;
/**
* Reference to the database version. Knex.js fetches the version after
* the first database query, so it will be set to undefined initially
*/
readonly version: any;
/**
* The default format for datetime column. The date formats is
* valid for luxon date parsing library
*/
readonly dateTimeFormat = "yyyy-MM-dd HH:mm:ss";
constructor(client: QueryClientContract, config: MysqlConfig);
/**
* Truncate mysql table with option to cascade
*/
truncate(table: string, cascade?: boolean): Promise<void>;
/**
* Returns an array of table names
*/
getAllTables(): Promise<string[]>;
/**
* Returns an array of all views names
*/
getAllViews(): Promise<string[]>;
/**
* Returns an array of all types names
*/
getAllTypes(): Promise<string[]>;
/**
* Drop all tables inside the database
*/
dropAllTables(): Promise<void>;
/**
* Drop all views inside the database
*/
dropAllViews(): Promise<void>;
/**
* Drop all custom types inside the database
*/
dropAllTypes(): Promise<void>;
/**
* Attempts to add advisory lock to the database and
* returns it's status.
*/
getAdvisoryLock(key: string, timeout?: number): Promise<boolean>;
/**
* Releases the advisory lock
*/
releaseAdvisoryLock(key: string): Promise<boolean>;
}
+194
View File
@@ -0,0 +1,194 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.MysqlDialect = void 0;
/// <reference path="../../adonis-typings/index.ts" />
const Raw_1 = require("../Database/StaticBuilder/Raw");
class MysqlDialect {
constructor(client, config) {
Object.defineProperty(this, "client", {
enumerable: true,
configurable: true,
writable: true,
value: client
});
Object.defineProperty(this, "config", {
enumerable: true,
configurable: true,
writable: true,
value: config
});
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: 'mysql'
});
Object.defineProperty(this, "supportsAdvisoryLocks", {
enumerable: true,
configurable: true,
writable: true,
value: true
});
Object.defineProperty(this, "supportsViews", {
enumerable: true,
configurable: true,
writable: true,
value: true
});
Object.defineProperty(this, "supportsTypes", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
Object.defineProperty(this, "supportsReturningStatement", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
/**
* Reference to the database version. Knex.js fetches the version after
* the first database query, so it will be set to undefined initially
*/
Object.defineProperty(this, "version", {
enumerable: true,
configurable: true,
writable: true,
value: this.client.getReadClient()['context']['client'].version
});
/**
* The default format for datetime column. The date formats is
* valid for luxon date parsing library
*/
Object.defineProperty(this, "dateTimeFormat", {
enumerable: true,
configurable: true,
writable: true,
value: 'yyyy-MM-dd HH:mm:ss'
});
}
/**
* Truncate mysql table with option to cascade
*/
async truncate(table, cascade = false) {
if (!cascade) {
return this.client.knexQuery().table(table).truncate();
}
/**
* Cascade and truncate
*/
const trx = await this.client.transaction();
try {
await trx.rawQuery('SET FOREIGN_KEY_CHECKS=0;');
await trx.knexQuery().table(table).truncate();
await trx.rawQuery('SET FOREIGN_KEY_CHECKS=1;');
await trx.commit();
}
catch (error) {
await trx.rollback();
throw error;
}
}
/**
* Returns an array of table names
*/
async getAllTables() {
const tables = await this.client
.query()
.from('information_schema.tables')
.select('table_name as table_name')
.where('TABLE_TYPE', 'BASE TABLE')
.where('table_schema', new Raw_1.RawBuilder('database()'))
.orderBy('table_name', 'asc');
return tables.map(({ table_name }) => table_name);
}
/**
* Returns an array of all views names
*/
async getAllViews() {
const tables = await this.client
.query()
.from('information_schema.tables')
.select('table_name as table_name')
.where('TABLE_TYPE', 'VIEW')
.where('table_schema', new Raw_1.RawBuilder('database()'))
.orderBy('table_name', 'asc');
return tables.map(({ table_name }) => table_name);
}
/**
* Returns an array of all types names
*/
async getAllTypes() {
throw new Error("MySQL doesn't support types");
}
/**
* Drop all tables inside the database
*/
async dropAllTables() {
let tables = await this.getAllTables();
/**
* Filter out tables that are not allowed to be dropped
*/
tables = tables.filter((table) => !(this.config.wipe?.ignoreTables || []).includes(table));
/**
* Add backquote around table names to avoid syntax errors
* in case of a table name with a reserved keyword
*/
tables = tables.map((table) => '`' + table + '`');
if (!tables.length) {
return;
}
/**
* Cascade and truncate
*/
const trx = await this.client.transaction();
try {
await trx.rawQuery('SET FOREIGN_KEY_CHECKS=0;');
await trx.rawQuery(`DROP TABLE ${tables.join(',')};`);
await trx.rawQuery('SET FOREIGN_KEY_CHECKS=1;');
await trx.commit();
}
catch (error) {
await trx.rollback();
throw error;
}
}
/**
* Drop all views inside the database
*/
async dropAllViews() {
const views = await this.getAllViews();
return this.client.rawQuery(`DROP VIEW ${views.join(',')};`);
}
/**
* Drop all custom types inside the database
*/
async dropAllTypes() {
throw new Error("MySQL doesn't support types");
}
/**
* Attempts to add advisory lock to the database and
* returns it's status.
*/
async getAdvisoryLock(key, timeout = 0) {
const response = await this.client.rawQuery(`SELECT GET_LOCK('${key}', ${timeout}) as lock_status;`);
return response[0] && response[0][0] && response[0][0].lock_status === 1;
}
/**
* Releases the advisory lock
*/
async releaseAdvisoryLock(key) {
const response = await this.client.rawQuery(`SELECT RELEASE_LOCK('${key}') as lock_status;`);
return response[0] && response[0][0] && response[0][0].lock_status === 1;
}
}
exports.MysqlDialect = MysqlDialect;
+39
View File
@@ -0,0 +1,39 @@
/// <reference path="../../adonis-typings/index.d.ts" />
import { DialectContract, QueryClientContract } from '@ioc:Adonis/Lucid/Database';
export declare class OracleDialect implements DialectContract {
private client;
readonly name = "oracledb";
readonly supportsAdvisoryLocks = false;
readonly supportsViews = false;
readonly supportsTypes = false;
readonly supportsReturningStatement = true;
/**
* Reference to the database version. Knex.js fetches the version after
* the first database query, so it will be set to undefined initially
*/
readonly version: any;
/**
* The default format for datetime column. The date formats is
* valid for luxon date parsing library
*/
readonly dateTimeFormat = "yyyy-MM-dd HH:mm:ss";
constructor(client: QueryClientContract);
/**
* Not implemented yet
*/
getAllTables(): Promise<any>;
/**
* Truncate pg table with option to cascade and restart identity
*/
truncate(table: string, cascade?: boolean): Promise<any>;
/**
* Not implemented yet
*/
dropAllTables(): Promise<void>;
getAllViews(): Promise<string[]>;
getAllTypes(): Promise<string[]>;
dropAllViews(): Promise<void>;
dropAllTypes(): Promise<void>;
getAdvisoryLock(): Promise<boolean>;
releaseAdvisoryLock(): Promise<boolean>;
}
+110
View File
@@ -0,0 +1,110 @@
"use strict";
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.OracleDialect = void 0;
class OracleDialect {
constructor(client) {
Object.defineProperty(this, "client", {
enumerable: true,
configurable: true,
writable: true,
value: client
});
Object.defineProperty(this, "name", {
enumerable: true,
configurable: true,
writable: true,
value: 'oracledb'
});
Object.defineProperty(this, "supportsAdvisoryLocks", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
Object.defineProperty(this, "supportsViews", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
Object.defineProperty(this, "supportsTypes", {
enumerable: true,
configurable: true,
writable: true,
value: false
});
Object.defineProperty(this, "supportsReturningStatement", {
enumerable: true,
configurable: true,
writable: true,
value: true
});
/**
* Reference to the database version. Knex.js fetches the version after
* the first database query, so it will be set to undefined initially
*/
Object.defineProperty(this, "version", {
enumerable: true,
configurable: true,
writable: true,
value: this.client.getReadClient()['context']['client'].version
});
/**
* The default format for datetime column. The date formats is
* valid for luxon date parsing library
*/
Object.defineProperty(this, "dateTimeFormat", {
enumerable: true,
configurable: true,
writable: true,
value: 'yyyy-MM-dd HH:mm:ss'
});
}
/**
* Not implemented yet
*/
async getAllTables() {
throw new Error('"getAllTables" method is not implemented for oracledb. Create a PR to add the feature');
}
/**
* Truncate pg table with option to cascade and restart identity
*/
async truncate(table, cascade = false) {
return cascade
? this.client.rawQuery(`TRUNCATE ${table} CASCADE;`)
: this.client.rawQuery(`TRUNCATE ${table};`);
}
/**
* Not implemented yet
*/
async dropAllTables() {
throw new Error('"dropAllTables" method is not implemented for oracledb. Create a PR to add the feature');
}
async getAllViews() {
throw new Error('"getAllViews" method is not implemented for oracledb. Create a PR to add the feature.');
}
async getAllTypes() {
throw new Error('"getAllTypes" method is not implemented for oracledb. Create a PR to add the feature.');
}
async dropAllViews() {
throw new Error('"dropAllViews" method is not implemented for oracledb. Create a PR to add the feature.');
}
async dropAllTypes() {
throw new Error('"dropAllTypes" method is not implemented for oracledb. Create a PR to add the feature.');
}
getAdvisoryLock() {
throw new Error('Support for advisory locks is not implemented for oracledb. Create a PR to add the feature');
}
releaseAdvisoryLock() {
throw new Error('Support for advisory locks is not implemented for oracledb. Create a PR to add the feature');
}
}
exports.OracleDialect = OracleDialect;

Some files were not shown because too many files have changed in this diff Show More