epic-sql
Version:
A Simple But Powerful SQL ORM!!!
464 lines (463 loc) • 26.3 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Operator = exports.QueryFunction = void 0;
const epic_sql_query_1 = require("epic-sql-query");
const base_schema_1 = require("./base-schema");
const decorators_1 = require("./decorators");
const exceptions_1 = require("./exceptions");
const utils_1 = require("./utils");
var epic_sql_query_2 = require("epic-sql-query");
Object.defineProperty(exports, "QueryFunction", { enumerable: true, get: function () { return epic_sql_query_2.QueryFunction; } });
class Operator {
constructor(Schema, Relations, Manager) {
this.Schema = Schema;
this.Relations = Relations;
this.Manager = Manager;
if (!(this.getSchema() instanceof base_schema_1.BaseSchema))
throw new exceptions_1.SQLException(`Invalid Schema has been provided!`);
}
prepareCreate(input) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
const Schema = input;
const SchemaInfo = Schema.getSchemaInfo();
const ResolvedSchema = {};
// Get Schema Columns
const Columns = Object.values(SchemaInfo.data);
// Resolve Each Column
Columns.forEach((column) => {
// Get Value
const Value = Schema[column.name] ||
(typeof column.defaultValue === "function"
? column.defaultValue()
: column.defaultValue);
// Null Validation
if (!column.primaryKey &&
!column.nullable &&
(Value === null || Value === undefined))
throw new exceptions_1.SQLException(`Value cannot be null for column '${column.name}'!`);
// Enum Validation
if (column.choices instanceof Array)
if (!column.choices.includes(Value))
throw new exceptions_1.SQLException(`Invalid value '${Value}' has been provided for column '${column.name}'! Allowed values are (${column.choices.join(", ")}).`);
// Push Resolved Value
Schema[column.name] = Value;
});
// Event Before Create
yield ((_a = Schema._beforeCreate) === null || _a === void 0 ? void 0 : _a.call(Schema));
// Assign Column Values
Columns.forEach((column) => (ResolvedSchema[`${SchemaInfo.name}.${column.name}`] =
Schema[column.name]));
return ResolvedSchema;
});
}
prepareUpdate(input) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
const Schema = input;
const SchemaInfo = Schema.getSchemaInfo();
const ResolvedSchema = {};
// Get Schema Columns
const Columns = Object.values(SchemaInfo.data);
// Get Schema Indexes
const Indexes = Object.values(SchemaInfo.indexes);
// Resolve Each Column
Columns.forEach((column) => {
// Get Value
const Value = Schema[column.name] ||
(typeof column.onUpdate === "function"
? column.onUpdate()
: column.onUpdate);
// Null Validation
if (!column.primaryKey && !column.nullable && Value === null)
throw new exceptions_1.SQLException(`Value cannot be null for column '${column.name}'!`);
// Enum Validation
if (column.choices instanceof Array && Value !== undefined)
if (!column.choices.includes(Value))
throw new exceptions_1.SQLException(`Invalid value '${Value}' has been provided for column '${column.name}'! Allowed values are (${column.choices.join(", ")}).`);
// Assign Column Values
ResolvedSchema[`${SchemaInfo.name}.${column.name}`] = Value;
});
// Resolve Each Relation Index
yield Promise.all(Indexes.map((Index) => Promise.all(this.Relations.map((Relation) => __awaiter(this, void 0, void 0, function* () {
var _b, _c;
if (Index.type === decorators_1.MetaDataKeys.RELATION_INDEX &&
Relation === ((_b = Index.schema) === null || _b === void 0 ? void 0 : _b.call(Index))) {
// Get Sub Schema
const SubSchema = Schema[Index.name] instanceof Array
? Schema[Index.name][0]
: Schema[Index.name];
const SubSchemaInfo = SubSchema.getSchemaInfo();
// Get Sub Schema Columns
const SubColumns = Object.values(SubSchemaInfo.data);
// Resolve Each Sub Column
SubColumns.forEach((column) => {
// Get Value
const Value = SubSchema[column.name] ||
(typeof column.onUpdate === "function"
? column.onUpdate()
: column.onUpdate);
// Null Validation
if (!column.nullable && Value === null)
throw new exceptions_1.SQLException(`Value cannot be null for column '${column.name}'!`);
// Enum Validation
if (column.choices instanceof Array && Value !== undefined)
if (!column.choices.includes(Value))
throw new exceptions_1.SQLException(`Invalid value '${Value}' has been provided for column '${column.name}'! Allowed values are (${column.choices.join(", ")}).`);
// Assign Column Values
ResolvedSchema[`${SubSchemaInfo.name}.${column.name}`] = Value;
});
// Event Before Update
yield ((_c = SubSchema._beforeUpdate) === null || _c === void 0 ? void 0 : _c.call(SubSchema));
}
})))));
// Event Before Update
yield ((_a = Schema._beforeUpdate) === null || _a === void 0 ? void 0 : _a.call(Schema));
return ResolvedSchema;
});
}
resolveSelect(data) {
const Schema = this.getSchema();
const SchemaInfo = Schema.getSchemaInfo();
// Resolved Results
const ResolvedResults = {};
// Loop over each Row
data.map((Row) => {
const Identifier = SchemaInfo.primaryKey
? Row[`${SchemaInfo.name}.${SchemaInfo.primaryKey.name}`] ||
utils_1.Utils.uuidShort()
: utils_1.Utils.uuidShort();
// New Instance Of current Schema or Use Existing New Instance
const TargetSchema = (ResolvedResults[Identifier] ||
new this.Schema());
// Push Data to the Schema
Object.values(SchemaInfo.data).forEach((column) => {
if (Row[`${SchemaInfo.name}.${column.name}`] !== undefined)
TargetSchema[column.name] = column.resolve(Row[`${SchemaInfo.name}.${column.name}`]);
});
// Process Indexes
Object.values(SchemaInfo.indexes).forEach((Index) => {
// Process Metadata
if (Index.type === decorators_1.MetaDataKeys.METADATA_INDEX) {
if (Row[Index.name] !== undefined)
TargetSchema[Index.name] = typeof Index.resolver === "function"
? Index.resolver(TargetSchema)
: Row[Index.name];
}
else if (Index.type === decorators_1.MetaDataKeys.RELATION_INDEX)
// Process Relation Data
this.Relations.forEach((Relation) => {
if (Index.schema() === Relation) {
// Create New Instance of Relation Schema
const RelationSchema = new Relation();
const RelationSchemaInfo = RelationSchema.getSchemaInfo();
// Push Data to the Relation Schema
Object.values(RelationSchemaInfo.data).forEach((column) => {
if (Row[`${RelationSchemaInfo.name}.${column.name}`] !== undefined)
RelationSchema[column.name] = column.resolve(Row[`${RelationSchemaInfo.name}.${column.name}`]);
});
// Check If Schema is Filled
if (RelationSchema.isFilled())
// Push Relation Schema to Main Schema
TargetSchema[Index.name] =
Index.value === decorators_1.MetaDataKeys.ONE_RELATION
? RelationSchema
: [...(TargetSchema[Index.name] || []), RelationSchema];
}
});
});
// Push Resolved Schema to the List
ResolvedResults[Identifier] = TargetSchema;
});
return Object.values(ResolvedResults);
}
conditions(type, ...conditions) {
const Schema = this.getSchema();
const SchemaInfo = Schema.getSchemaInfo();
// Push Conditions
this.getQueryBuilder()[type](...conditions.map((condition) => Object.keys(condition).reduce((object, column) => {
if (Object.keys(SchemaInfo.data).includes(column))
return Object.assign(Object.assign({}, object), { [`${SchemaInfo.name}.${column}`]: condition[column] });
else if (Object.keys(SchemaInfo.indexes).includes(column)) {
return (this.Relations.reduce((object, Relation) => {
const Index = SchemaInfo.indexes[column];
if (Index.schema && Index.schema() === Relation) {
const SubSchema = new Relation();
const SubSchemaInfo = SubSchema.getSchemaInfo();
return Object.assign(Object.assign({}, object), Object.keys(condition[column]).reduce((subObject, subColumn) => {
if (Object.keys(SubSchemaInfo.data).includes(subColumn))
return Object.assign(Object.assign({}, subObject), {
// @ts-ignore
[`${SubSchemaInfo.name}.${subColumn}`]: condition[column][subColumn] });
else
return subObject;
}, {}));
}
else
return object ? object : null;
}, null) || object);
}
else
return object;
}, {})));
return this;
}
getSchema() {
if (!this.SchemaInstance)
this.SchemaInstance = new this.Schema();
return this.SchemaInstance;
}
getQueryBuilder() {
if (!this.QueryBuilder) {
const Schema = this.getSchema();
const SchemaInfo = Schema.getSchemaInfo();
// Create Query Builder
this.QueryBuilder = new epic_sql_query_1.Query(this.getSchema().createTable(), ...this.Relations.map((Schema) => new Schema().createTable())).target(SchemaInfo.name);
// Add Joins
this.Relations.forEach((Relation) => {
Object.values(SchemaInfo.indexes).forEach((index) => {
var _a;
if (Relation === ((_a = index.schema) === null || _a === void 0 ? void 0 : _a.call(index))) {
const RelationSchema = new Relation();
const RelationSchemaInfo = RelationSchema.getSchemaInfo();
this.QueryBuilder.target(RelationSchemaInfo.name)
.join(RelationSchemaInfo.name)[index.joinType || "left"](`${RelationSchemaInfo.name}.${index.mapping[0]}`, `${SchemaInfo.name}.${index.mapping[1]}`);
}
});
});
}
return this.QueryBuilder;
}
create(input, options) {
return __awaiter(this, void 0, void 0, function* () {
if (!(input instanceof base_schema_1.BaseSchema))
throw new exceptions_1.SQLException(`Invalid input data type has been provided! A '${this.Schema.name}' Schema instance was expected.`);
const Schema = input;
const SchemaInfo = Schema.getSchemaInfo();
// Create Main Statement
const Statements = [
this.getQueryBuilder().insert(yield this.prepareCreate(Schema)),
];
// List Relation Schemas
const Schemas = [Schema];
// Process Relations
yield Promise.all(this.Relations.map((RawSchema) => __awaiter(this, void 0, void 0, function* () {
if (!Object.values(SchemaInfo.indexes).reduce((r, index) => { var _a; return (!r ? ((_a = index.schema) === null || _a === void 0 ? void 0 : _a.call(index)) === RawSchema : r); }, false))
throw new exceptions_1.SQLException(`Schema '${this.Schema.name}' does not have any relation with Schema '${RawSchema.name}'!`);
yield Promise.all(Object.values(SchemaInfo.indexes).map((index) => __awaiter(this, void 0, void 0, function* () {
var _a;
if (RawSchema === ((_a = index.schema) === null || _a === void 0 ? void 0 : _a.call(index)) &&
index.type === decorators_1.MetaDataKeys.RELATION_INDEX)
if (typeof Schema[index.name] === "object" &&
Schema[index.name] !== null) {
// Create New Repository
const Repository = this.Manager.use(index.schema());
yield Promise.all((Schema[index.name] instanceof Array
? Schema[index.name]
: [Schema[index.name]]).map((Input) => __awaiter(this, void 0, void 0, function* () {
// Resolve Mapping
Input[index.mapping[0]] = Schema[index.mapping[1]];
// Validate Schema
if (!index.schema)
throw new exceptions_1.SQLException(`Something went wrong while processing the indexes!`);
else if (!(Input instanceof index.schema()))
throw new exceptions_1.SQLException(`Invalid Schema instance for relation on Column '${index.name}' has been encountered!`);
// Push Relation Schema
Schemas.push(Input);
// Push Relational Statements
Statements.push(...(yield Repository().create(Input, {
returnStatements: true,
})));
})));
}
})));
})));
// Return SQL Statements
if (options === null || options === void 0 ? void 0 : options.returnStatements)
return Statements;
else {
// Execute Querys
const Result = (yield this.Manager.Transaction(() => this.Manager.query(Statements.map((statement) => statement.query).join(""), Statements.reduce((params, statement) => [...params, ...statement.params], []))));
// Check Response
if (Result instanceof Array) {
(Result[0] instanceof Array ? Result[0] : [Result[0]]).map((header, index) => {
const Schema = Schemas[index];
const SchemaInfo = Schema.getSchemaInfo();
// Assign Insert ID
Object.values(SchemaInfo.indexes).forEach((index) => {
if (index.type === decorators_1.MetaDataKeys.PRIMARY_KEY_INDEX)
Schema[index.name] = header.insertId;
});
});
// Event After Create
yield Promise.all(Schemas.map((Schema) => { var _a; return (_a = Schema._afterCreate) === null || _a === void 0 ? void 0 : _a.call(Schema); }));
return Schema;
}
else
throw new exceptions_1.SQLException(`Unexpected response has been encountered!`);
}
});
}
update(input, options) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
if (!(input instanceof base_schema_1.BaseSchema))
throw new exceptions_1.SQLException(`Invalid input data type has been provided! A '${this.Schema.name}' Schema instance was expected.`);
const Schema = input;
const SchemaInfo = Schema.getSchemaInfo();
// Create Main Statement
const Statements = [
this.getQueryBuilder().update(yield this.prepareUpdate(Schema)),
];
if (options === null || options === void 0 ? void 0 : options.returnStatements)
return Statements;
else {
// Execute Transaction
const Result = (yield this.Manager.Transaction(() => this.Manager.query(Statements.map((statement) => statement.query).join(""), Statements.reduce((params, statement) => [...params, ...statement.params], []))));
if (Result instanceof Array) {
if (Result[0] instanceof Array)
Result[0].map((header) => {
if ((header.affectedRows || 0) === 0 && (options === null || options === void 0 ? void 0 : options.throwError))
throw new exceptions_1.SQLException(`Some data has not been updated!`);
});
else if ((Result[0].affectedRows || 0) === 0 &&
(options === null || options === void 0 ? void 0 : options.throwError))
throw new exceptions_1.SQLException(`We are unable to update the data!`);
// Event After Update
yield ((_a = Schema._afterUpdate) === null || _a === void 0 ? void 0 : _a.call(Schema));
// Sub Schema Event After Update
yield Promise.all(Object.values(SchemaInfo.indexes).map((index) => __awaiter(this, void 0, void 0, function* () {
var _b, _c;
if (index.schema && Schema[index.name] instanceof index.schema())
yield ((_c = (_b = Schema[index.name])._afterUpdate) === null || _c === void 0 ? void 0 : _c.call(_b));
})));
return Schema;
}
else
throw new exceptions_1.SQLException(`Unexpected response has been encountered!`);
}
});
}
select(columns = [], options) {
return __awaiter(this, void 0, void 0, function* () {
try {
const Schema = this.getSchema();
const SchemaInfo = Schema.getSchemaInfo();
// Create Main Statement
const Statements = [
this.getQueryBuilder().select(...(columns.length
? [
this.Relations.length && SchemaInfo.primaryKey
? `${SchemaInfo.name}.${SchemaInfo.primaryKey.name}`
: undefined,
...columns,
].filter((v) => v)
: [])),
];
if (options === null || options === void 0 ? void 0 : options.returnStatements)
return Statements;
else {
// Execute Transaction
const Result = (yield this.Manager.query(Statements.map((statement) => statement.query).join(""), Statements.reduce((params, statement) => [...params, ...statement.params], [])));
if (Result instanceof Array) {
const Resolved = this.resolveSelect(Result[0] instanceof Array ? Result[0] : [Result[0]]);
if ((options === null || options === void 0 ? void 0 : options.throwError) && !Resolved.length)
throw new exceptions_1.SQLException(`Not data has been found!`);
return Resolved;
}
else
throw new exceptions_1.SQLException(`Unexpected response has been encountered!`);
}
}
catch (error) {
throw new exceptions_1.SQLException(error.message || error);
}
});
}
delete(schemas, options) {
return __awaiter(this, void 0, void 0, function* () {
// Check Schemas To Delete
if (!(schemas instanceof Array))
throw new exceptions_1.SQLException(`Please provide a valid list of Schemas to Delete!`);
else {
const Schemas = schemas.map((schema) => {
if (!(schema === base_schema_1.BaseSchema))
throw new exceptions_1.SQLException(`Please provide a valid Schema in the Schemas list!`);
else
return new schema();
});
// Create Main Statement
const Statements = [
this.getQueryBuilder().delete(...Schemas.map((Schema) => Schema.getSchemaName())),
];
if (options === null || options === void 0 ? void 0 : options.returnStatements)
return Statements;
else {
// Fetch Data To Delete
const DataToDelete = yield this.select();
// Execute Transaction
const Result = (yield this.Manager.Transaction(() => this.Manager.query(Statements.map((statement) => statement.query).join(""), Statements.reduce((params, statement) => [...params, ...statement.params], []))));
if (Result instanceof Array) {
if (Result[0] instanceof Array)
Result[0].map((header) => {
if ((header.affectedRows || 0) === 0 && (options === null || options === void 0 ? void 0 : options.throwError))
throw new exceptions_1.SQLException(`Some data has not been updated!`);
});
else if ((Result[0].affectedRows || 0) === 0 &&
(options === null || options === void 0 ? void 0 : options.throwError))
throw new exceptions_1.SQLException(`We are unable to update the data!`);
return DataToDelete;
}
else
throw new exceptions_1.SQLException(`Unexpected response has been encountered!`);
}
}
});
}
count(column, options) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
const Results = yield this.select([new epic_sql_query_1.QueryFunction(`COUNT(${column})`, "_Count")], options);
return (options === null || options === void 0 ? void 0 : options.returnStatements) ? Results : ((_a = Results[0]) === null || _a === void 0 ? void 0 : _a._Count) || 0;
});
}
avg(column, options) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
const Results = yield this.select([new epic_sql_query_1.QueryFunction(`AVG(${column})`, "_Average")], options);
return (options === null || options === void 0 ? void 0 : options.returnStatements) ? Results : ((_a = Results[0]) === null || _a === void 0 ? void 0 : _a._Average) || 0;
});
}
search(target, ...columns) {
this.getQueryBuilder().search(target, ...columns);
return this;
}
where(...conditions) {
return this.conditions("where", ...conditions);
}
group(...columns) {
this.getQueryBuilder().group(...columns);
return this;
}
having(...conditions) {
return this.conditions("having", ...conditions);
}
order(columns, sort) {
this.getQueryBuilder().order(columns, sort);
return this;
}
limit(number, offset) {
this.getQueryBuilder().limit(number, offset);
return this;
}
}
exports.Operator = Operator;
;