UNPKG

epic-sql

Version:
464 lines (463 loc) 26.3 kB
"use strict"; 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;