@deepkit/framework
Version:
472 lines • 22.1 kB
JavaScript
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);
};
/*@ts-ignore*/
import { __ΩFakerTypes } from '@deepkit/orm-browser-api';
/*@ts-ignore*/
import { __ΩHttpQuery } from '@deepkit/http';
/*@ts-ignore*/
import { __ΩQueryResult } from '@deepkit/orm-browser-api';
/*@ts-ignore*/
import { __ΩDatabaseCommit } from '@deepkit/orm-browser-api';
function __assignType(fn, args) {
fn.__type = args;
return fn;
}
import { isArray, isObject } from '@deepkit/core';
import { Database, MigrateOptions } from '@deepkit/orm';
import { BrowserControllerInterface, DatabaseInfo, EntityPropertySeed, fakerFunctions, getType, SeedDatabase, } from '@deepkit/orm-browser-api';
import { rpc } from '@deepkit/rpc';
import { SQLDatabaseAdapter } from '@deepkit/sql';
import { Logger, LoggerLevel, MemoryLoggerTransport } from '@deepkit/logger';
import { performance } from 'perf_hooks';
import { http } from '@deepkit/http';
import { cast, getPartialSerializeFunction, isReferenceType, ReflectionClass, ReflectionKind, resolveClassType, serializer, } from '@deepkit/type';
let OrmBrowserController = class OrmBrowserController {
constructor(databases) {
this.databases = databases;
}
registerDatabase(...databases) {
this.databases.push(...databases);
}
extractDatabaseInfo(db) {
return new DatabaseInfo(db.name, db.adapter.getName(), db.entityRegistry.all().map(__assignType(v => v.serializeType(), ['v', '', 'P"2!"/"'])));
}
getDb(dbName) {
for (const db of this.databases) {
if (db.name === dbName)
return db;
}
throw new Error(`No database ${dbName} found`);
}
getDbEntity(dbName, entityName) {
for (const db of this.databases) {
if (db.name === dbName) {
for (const entity of db.entityRegistry.all()) {
if (entity.name === entityName)
return [db, entity];
}
}
}
throw new Error(`No entity ${entityName} for in database ${dbName}`);
}
getDatabases() {
const databases = [];
for (const db of this.databases) {
databases.push(this.extractDatabaseInfo(db));
}
return databases;
}
getDatabase(name) {
for (const db of this.databases) {
if (db.name === name)
return this.extractDatabaseInfo(db);
}
throw new Error(`No database ${name} found`);
}
findDatabase(name) {
for (const db of this.databases) {
if (db.name === name)
return db;
}
throw new Error(`No database ${name} found`);
}
async migrate(name) {
const db = this.findDatabase(name);
await db.migrate();
}
async resetAllTables(name) {
const db = this.findDatabase(name);
if (db.adapter instanceof SQLDatabaseAdapter) {
await db.adapter.createTables(db.entityRegistry);
}
}
async getFakerTypes() {
const res = {};
const faker = require('faker');
for (const fn of fakerFunctions) {
const [p1, p2] = fn.split('.');
try {
const example = p2 ? faker[p1][p2]() : faker[p1]();
res[fn] = { example: example, type: getType(example) };
}
catch (error) {
console.log(`warning: faker function not available ${fn}: ${error}`);
}
}
return res;
}
async getMigrations(name) {
const db = this.findDatabase(name);
if (db.adapter instanceof SQLDatabaseAdapter) {
return db.adapter.getMigrations(new MigrateOptions(), db.entityRegistry);
}
return {};
}
async seed(dbName, seed) {
var _a, _b;
const db = this.getDb(dbName);
const oldLevel = db.logger.level;
db.logger.level = LoggerLevel.debug;
try {
const session = db.createSession();
const added = {};
const assignReference = [];
const faker = require('faker');
function fakerValue(path, fakerName) {
const [p1, p2] = fakerName.split('.');
try {
return p2 ? faker[p1][p2]() : faker[p1]();
}
catch (error) {
console.warn(`Could not fake ${path} via faker's ${fakerName}: ${error}`);
}
}
fakerValue.__type = ['path', 'fakerName', 'fakerValue', 'P&2!&2""/#'];
function fake(path, property, propSeed, callback) {
if (!propSeed.fake) {
if (propSeed.value !== undefined)
callback(propSeed.value);
return;
}
if (isReferenceType(property)) {
// if (property.type !== 'class') throw new Error(`${path}: only class properties can be references`);
assignReference.push({
path,
entity: resolveClassType(property).getName(),
reference: propSeed.reference,
properties: propSeed.properties,
callback
});
return;
}
else if (property.kind === ReflectionKind.array) {
const res = [];
if (!propSeed.array)
return res;
const range = propSeed.array.max - propSeed.array.min;
for (let i = 0; i < Math.ceil(Math.random() * range); i++) {
fake(path + '.' + i, property.type, propSeed.array.seed, __assignType((v) => {
res.push(v);
}, ['v', '', 'P"2!"/"']));
}
return callback(res);
}
else if (property.kind === ReflectionKind.class || property.kind === ReflectionKind.objectLiteral) {
const schema = ReflectionClass.from(property);
const item = schema.createDefaultObject();
for (const prop of schema.getProperties()) {
if (!propSeed.properties[prop.name])
continue;
fake(path + '.' + prop.name, prop.type, propSeed.properties[prop.name], __assignType((v) => {
item[prop.name] = v;
}, ['v', '', 'P"2!"/"']));
}
return callback(item);
}
else if (property.kind === ReflectionKind.boolean) {
callback(Math.random() > 0.5);
}
else if (property.kind === ReflectionKind.enum) {
const values = property.values;
callback(values[values.length * Math.random() | 0]);
}
else {
return callback(fakerValue(path, propSeed.faker));
}
}
fake.__type = ['path', 'Type', 'property', () => EntityPropertySeed, 'propSeed', 'v', '', 'callback', 'fake', 'P&2!"w"2#P7$2%P"2&"/\'2("/)'];
function create(entity, properties) {
if (!added[entity.getName()])
added[entity.getName()] = [];
const item = entity.createDefaultObject();
for (const [propName, propSeed] of Object.entries(properties)) {
const property = entity.getProperty(propName);
fake(entity.getClassName() + '.' + propName, property.type, propSeed, __assignType((v) => {
item[property.name] = v;
}, ['v', '', 'P"2!"/"']));
}
for (const reference of entity.getReferences()) {
if (reference.isArray())
continue;
if (reference.isBackReference())
continue;
item[reference.name] = db.getReference(reference.getResolvedReflectionClass(), item[reference.name]);
}
session.add(item);
added[entity.getName()].push(item);
return item;
}
create.__type = [() => ReflectionClass, 'entity', () => EntityPropertySeed, 'properties', 'create', 'PP"7!2"P&P7#LM2$"/%'];
for (const [entityName, entitySeed] of Object.entries(seed.entities)) {
if (!entitySeed || !entitySeed.active)
continue;
const entity = db.getEntity(entityName);
if (entitySeed.truncate) {
await db.query(entity).deleteMany();
}
for (let i = 0; i < entitySeed.amount; i++) {
create(entity, entitySeed.properties);
}
}
//assign references
const dbCandidates = {};
for (const ref of assignReference) {
const entity = db.getEntity(ref.entity);
let candidates = added[_a = ref.entity] || (added[_a] = []);
if (ref.reference === 'random') {
//note: I know there are faster ways, but this gets the job done for now
candidates = dbCandidates[_b = ref.entity] || (dbCandidates[_b] = await db.query(entity).limit(1000).find());
}
if (!candidates.length) {
if (ref.reference === 'random') {
throw new Error(`Entity ${ref.entity} has no items in the database. Used in ${ref.path}.`);
}
if (ref.reference === 'random-seed') {
throw new Error(`Entity ${ref.entity} has no seeded items. Used in ${ref.path}.`);
}
}
if (ref.reference === 'create') {
ref.callback(create(entity, ref.properties));
}
else {
ref.callback(candidates[candidates.length * Math.random() | 0]);
}
}
await session.commit();
}
finally {
db.logger.level = oldLevel;
}
}
async httpQuery(dbName, entityName, query) {
const [, entity] = this.getDbEntity(dbName, entityName);
const res = await this.query(dbName, entityName, query);
if (isArray(res.result)) {
const partial = getPartialSerializeFunction(entity.type, serializer.deserializeRegistry);
res.result = res.result.map(__assignType(v => partial(v), ['v', '', 'P"2!"/"']));
}
return res;
}
async query(dbName, entityName, query) {
const res = {
executionTime: 0,
log: [],
result: undefined
};
const [db, entity] = this.getDbEntity(dbName, entityName);
const oldLogger = db.logger;
const loggerTransport = new MemoryLoggerTransport;
db.setLogger(new Logger([loggerTransport]));
try {
const fn = new Function(`return function(database, ${entity.getClassName()}) {return ${query}}`)();
const start = performance.now();
res.result = await fn(db, entity);
res.executionTime = performance.now() - start;
}
catch (error) {
res.error = String(error);
}
finally {
res.log = loggerTransport.messageStrings;
if (oldLogger)
db.setLogger(oldLogger);
}
return res;
}
async getCount(dbName, entityName, filter) {
const [db, entity] = this.getDbEntity(dbName, entityName);
return await db.query(entity).filter(filter).count();
}
async getItems(dbName, entityName, filter, sort, limit, skip) {
const [db, entity] = this.getDbEntity(dbName, entityName);
const start = performance.now();
const items = await db.query(entity).filter(filter).sort(sort).limit(limit).skip(skip).find();
return { items, executionTime: performance.now() - start };
}
async create(dbName, entityName) {
const [db, entity] = this.getDbEntity(dbName, entityName);
return entity.createDefaultObject();
}
async commit(commit) {
// console.log(inspect(commit, false, 2133));
function isNewIdWrapper(value) {
return isObject(value) && '$___newId' in value;
}
isNewIdWrapper.__type = ['value', 'isNewIdWrapper', 'P"2!!/"'];
for (const [dbName, c] of Object.entries(commit)) {
const db = this.getDb(dbName);
const session = db.createSession();
try {
const updates = [];
for (const [entityName, removes] of Object.entries(c.removed)) {
const entity = db.getEntity(entityName);
const query = session.query(entity);
for (const remove of removes) {
updates.push(query.filter(db.getReference(entity, remove)).deleteOne());
}
}
const addedItems = (Map.Ω = [['\''], ['"']], new Map());
for (const [entityName, added] of Object.entries(c.added)) {
const entity = db.getEntity(entityName);
const addedIds = c.addedIds[entityName];
for (let i = 0; i < added.length; i++) {
addedItems.set(addedIds[i], cast(added[i], undefined, undefined, undefined, entity.type));
}
}
for (const [entityName, ids] of Object.entries(c.addedIds)) {
const entity = db.getEntity(entityName);
const added = c.added[entityName];
for (let i = 0; i < added.length; i++) {
const id = ids[i];
const add = added[i];
const item = addedItems.get(id);
for (const reference of entity.getReferences()) {
if (reference.isBackReference())
continue;
//note, we need to operate on `added` from commit
// since `item` from addedItems got already converted and $___newId is lost.
const v = add[reference.name];
if (reference.isArray()) {
}
else {
if (isNewIdWrapper(v)) {
//reference to not-yet existing record,
//so place the actual item in it, so the UoW accordingly saves
item[reference.name] = addedItems.get(v.$___newId);
}
else {
//regular reference to already existing record,
//so convert to reference
item[reference.name] = db.getReference(reference.getResolvedReflectionClass(), item[reference.name]);
}
}
}
session.add(item);
}
}
await session.commit();
for (const [entityName, changes] of Object.entries(c.changed)) {
const entity = db.getEntity(entityName);
const query = session.query(entity);
for (const change of changes) {
//todo: convert $set from json to class
const $set = change.changes.$set;
if (!$set)
continue;
for (const reference of entity.getReferences()) {
if (reference.isBackReference())
continue;
const v = $set[reference.name];
if (v === undefined)
continue;
if (isNewIdWrapper(v)) {
$set[reference.name] = addedItems.get(v.$___newId);
}
else {
$set[reference.name] = db.getReference(reference.getResolvedReflectionClass(), $set[reference.name]);
}
}
updates.push(query.filter(db.getReference(entity, change.pk)).patchOne(change.changes));
}
}
await Promise.all(updates);
}
catch (error) {
//todo: rollback
throw error;
}
}
}
};
OrmBrowserController.__type = [() => Database, 'databases', 'constructor', () => Database, 'registerDatabase', () => Database, 'db', () => DatabaseInfo, 'extractDatabaseInfo', 'dbName', () => Database, 'getDb', 'entityName', () => Database, () => ReflectionClass, 'getDbEntity', () => DatabaseInfo, 'getDatabases', 'name', () => DatabaseInfo, 'getDatabase', () => Database, 'findDatabase', 'migrate', 'resetAllTables', () => __ΩFakerTypes, 'getFakerTypes', 'sql', 'diff', 'getMigrations', () => SeedDatabase, 'seed', () => __ΩHttpQuery, () => __ΩHttpQuery, () => __ΩHttpQuery, 'query', () => __ΩQueryResult, 'httpQuery', () => __ΩQueryResult, 'filter', 'getCount', 'sort', 'limit', 'skip', 'items', 'executionTime', 'getItems', 'create', () => __ΩDatabaseCommit, 'commit', 'OrmBrowserController', 'PP7!F2"<"0#PP7$@2""0%PP7&2\'P7(0)<P&2*P7+0,<P&2*&2-PP7.P"7/G00<PP71F02P&23P7405P&23P7607<P&23$`08P&23$`09Pn:`0;P&23P&P&F4<&4=MLM`0>P&2*P7?2@$`0@P&oA"2*&oB"2-&oC"2DnE`0FP&2*&2-&2DnG`0DP&2*&2-P&"LM2H\'`0IP&2*&2-P&"LM2HP&"LM2J\'2K\'2LP"F4M\'4NM`0OP&2*&2-"`0PPnQ2R"0R5!x"wS'];
__decorate([
rpc.action(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Array)
], OrmBrowserController.prototype, "getDatabases", null);
__decorate([
rpc.action(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", DatabaseInfo)
], OrmBrowserController.prototype, "getDatabase", null);
__decorate([
rpc.action(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], OrmBrowserController.prototype, "migrate", null);
__decorate([
rpc.action(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], OrmBrowserController.prototype, "resetAllTables", null);
__decorate([
rpc.action(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], OrmBrowserController.prototype, "getFakerTypes", null);
__decorate([
rpc.action(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], OrmBrowserController.prototype, "getMigrations", null);
__decorate([
rpc.action(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, SeedDatabase]),
__metadata("design:returntype", Promise)
], OrmBrowserController.prototype, "seed", null);
__decorate([
http.GET('_orm-browser/query'),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object, Object]),
__metadata("design:returntype", Promise)
], OrmBrowserController.prototype, "httpQuery", null);
__decorate([
rpc.action(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String, String]),
__metadata("design:returntype", Promise)
], OrmBrowserController.prototype, "query", null);
__decorate([
rpc.action(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String, Object]),
__metadata("design:returntype", Promise)
], OrmBrowserController.prototype, "getCount", null);
__decorate([
rpc.action(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String, Object, Object, Number, Number]),
__metadata("design:returntype", Promise)
], OrmBrowserController.prototype, "getItems", null);
__decorate([
rpc.action(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String]),
__metadata("design:returntype", Promise)
], OrmBrowserController.prototype, "create", null);
__decorate([
rpc.action(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], OrmBrowserController.prototype, "commit", null);
OrmBrowserController = __decorate([
rpc.controller(BrowserControllerInterface),
__metadata("design:paramtypes", [Array])
], OrmBrowserController);
export { OrmBrowserController };
//# sourceMappingURL=controller.js.map