Compare commits

...

25 Commits

Author SHA1 Message Date
e550a8dbd6 3.1.51 2020-09-25 20:42:39 +00:00
cf6ef25a8d fix(core): update 2020-09-25 20:42:38 +00:00
29c512b0cc 3.1.50 2020-09-24 14:10:55 +00:00
105f69d1c9 fix(core): update 2020-09-24 14:10:54 +00:00
4c375f8465 3.1.49 2020-09-10 10:36:01 +00:00
9466af6a4a fix(core): update 2020-09-10 10:36:00 +00:00
c5aa747f42 3.1.48 2020-09-10 10:12:18 +00:00
b5f2474f65 fix(core): update 2020-09-10 10:12:17 +00:00
85f0d99934 3.1.47 2020-09-09 05:05:42 +00:00
3b2d3d9072 fix(core): update 2020-09-09 05:05:41 +00:00
3ff5c36fdf 3.1.46 2020-09-09 05:00:10 +00:00
a5acc2fe4e fix(core): update 2020-09-09 05:00:09 +00:00
9c81257101 3.1.45 2020-09-09 04:51:57 +00:00
f7342962f4 fix(core): update 2020-09-09 04:51:56 +00:00
bcd10205d3 3.1.44 2020-09-09 03:51:22 +00:00
6cab20f32d fix(core): update 2020-09-09 03:51:21 +00:00
5aaa6ad2d6 3.1.43 2020-09-09 02:50:28 +00:00
635256f2f6 fix(core): update 2020-09-09 02:50:27 +00:00
f799d3efa5 3.1.42 2020-09-09 02:21:16 +00:00
f74020ba96 fix(core): update 2020-09-09 02:21:15 +00:00
f6d6545ff5 3.1.41 2020-08-18 15:10:45 +00:00
85a196c8c1 fix(core): update 2020-08-18 15:10:44 +00:00
adb198af01 3.1.40 2020-08-18 13:28:01 +00:00
6dce9f3ff8 3.1.39 2020-08-18 13:27:05 +00:00
2f6a4ce3e5 fix(core): update 2020-08-18 13:27:04 +00:00
8 changed files with 648 additions and 815 deletions

1240
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "@pushrocks/smartdata", "name": "@pushrocks/smartdata",
"version": "3.1.38", "version": "3.1.51",
"private": false, "private": false,
"description": "do more with data", "description": "do more with data",
"main": "dist_ts/index.js", "main": "dist_ts/index.js",
@ -22,25 +22,26 @@
"homepage": "https://gitlab.com/pushrocks/smartdata#README", "homepage": "https://gitlab.com/pushrocks/smartdata#README",
"dependencies": { "dependencies": {
"@pushrocks/lik": "^4.0.17", "@pushrocks/lik": "^4.0.17",
"@pushrocks/smartlog": "^2.0.36", "@pushrocks/smartlog": "^2.0.39",
"@pushrocks/smartpromise": "^3.0.6", "@pushrocks/smartpromise": "^3.0.6",
"@pushrocks/smartstring": "^3.0.18", "@pushrocks/smartstring": "^3.0.18",
"@pushrocks/smartunique": "^3.0.3", "@tsclass/tsclass": "^3.0.25",
"@types/lodash": "^4.14.159", "@types/lodash": "^4.14.161",
"@types/mongodb": "^3.5.26", "@types/mongodb": "^3.5.27",
"lodash": "^4.17.20", "lodash": "^4.17.20",
"mongodb": "^3.6.0", "mongodb": "^3.6.2",
"runtime-type-checks": "0.0.4" "runtime-type-checks": "0.0.4"
}, },
"devDependencies": { "devDependencies": {
"@gitzone/tsbuild": "^2.1.25", "@gitzone/tsbuild": "^2.1.25",
"@gitzone/tstest": "^1.0.44", "@gitzone/tstest": "^1.0.44",
"@pushrocks/qenv": "^4.0.10", "@pushrocks/qenv": "^4.0.10",
"@pushrocks/smartunique": "^3.0.3",
"@pushrocks/tapbundle": "^3.2.9", "@pushrocks/tapbundle": "^3.2.9",
"@types/mongodb-memory-server": "^2.3.0", "@types/mongodb-memory-server": "^2.3.0",
"@types/node": "^14.6.0", "@types/node": "^14.11.2",
"@types/shortid": "0.0.29", "@types/shortid": "0.0.29",
"mongodb-memory-server": "6.6.3", "mongodb-memory-server": "^6.8.0",
"tslint": "^6.1.3", "tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0" "tslint-config-prettier": "^1.18.0"
}, },

View File

@ -5,10 +5,9 @@ const testQenv = new Qenv(process.cwd(), process.cwd() + '/.nogit/');
// the tested module // the tested module
import * as smartdata from '../ts/index'; import * as smartdata from '../ts/index';
import { smartstring } from '../ts/smartdata.plugins';
import * as smartunique from '@pushrocks/smartunique';
import * as mongoPlugin from 'mongodb-memory-server'; import * as mongoPlugin from 'mongodb-memory-server';
import { smartunique } from '../ts/smartdata.plugins';
// ======================================= // =======================================
// Connecting to the database server // Connecting to the database server
@ -18,10 +17,10 @@ let testDb: smartdata.SmartdataDb;
let smartdataOptions: smartdata.IMongoDescriptor; let smartdataOptions: smartdata.IMongoDescriptor;
let mongod: mongoPlugin.MongoMemoryServer; let mongod: mongoPlugin.MongoMemoryServer;
tap.test('should create a testinstance as database', async () => { tap.skip.test('should create a testinstance as database', async () => {
mongod = new mongoPlugin.MongoMemoryServer(); mongod = new mongoPlugin.MongoMemoryServer({});
console.log('created mongod instance'); console.log('created mongod instance');
await mongod._startUpInstance().catch(err => { await mongod._startUpInstance().catch((err) => {
console.log(err); console.log(err);
}); });
console.log('mongod started'); console.log('mongod started');
@ -34,6 +33,14 @@ tap.test('should create a testinstance as database', async () => {
testDb = new smartdata.SmartdataDb(smartdataOptions); testDb = new smartdata.SmartdataDb(smartdataOptions);
}); });
tap.test('should connect to atlas', async (tools) => {
const databaseName = `test-smartdata-${smartunique.shortId()}`;
testDb = new smartdata.SmartdataDb({
mongoDbUrl: testQenv.getEnvVarOnDemand('MONGO_URL'),
mongoDbName: databaseName,
});
});
tap.test('should establish a connection to mongod', async () => { tap.test('should establish a connection to mongod', async () => {
await testDb.init(); await testDb.init();
}); });
@ -59,6 +66,11 @@ class Car extends smartdata.SmartDataDbDoc<Car, Car> {
@smartdata.svDb() @smartdata.svDb()
public brand: string; public brand: string;
@smartdata.svDb()
deepData = {
sodeep: 'yes'
};
constructor(colorArg: string, brandArg: string) { constructor(colorArg: string, brandArg: string) {
super(); super();
this.color = colorArg; this.color = colorArg;
@ -73,15 +85,42 @@ tap.test('should save the car to the db', async () => {
const myCar2 = new Car('red', 'Volvo'); const myCar2 = new Car('red', 'Volvo');
await myCar2.save(); await myCar2.save();
let counter = 0;
do {
const myCar3 = new Car('red', 'Renault'); const myCar3 = new Car('red', 'Renault');
await myCar3.save(); await myCar3.save();
counter++;
} while (counter < 2000);
}); });
tap.test('expect to get instance of Car', async () => { tap.test('expect to get instance of Car with shallow match', async () => {
let counter = 0;
do {
const timeStart = Date.now();
const myCars = await Car.getInstances<Car>({ const myCars = await Car.getInstances<Car>({
brand: 'Volvo', brand: 'Renault',
}); });
expect(myCars[0].color).to.equal('red'); console.log(`took ${Date.now() - timeStart}`);
expect(myCars[0].deepData.sodeep).to.equal('yes');
expect(myCars[0].brand).to.equal('Renault');
counter++;
} while (counter < 30);
});
tap.test('expect to get instance of Car with deep match', async () => {
let counter = 0;
do {
const timeStart = Date.now();
const myCars2 = await Car.getInstances<Car>({
'deepData.sodeep': 'yes',
} as any);
console.log(`took ${Date.now() - timeStart}`);
expect(myCars2[0].deepData.sodeep).to.equal('yes');
expect(myCars2[0].brand).to.equal('Volvo');
counter++;
} while (counter < 30);
}); });
tap.test('expect to get instance of Car and update it', async () => { tap.test('expect to get instance of Car and update it', async () => {
@ -142,7 +181,9 @@ tap.test('should store a new Truck', async () => {
// ======================================= // =======================================
tap.test('should close the database connection', async (tools) => { tap.test('should close the database connection', async (tools) => {
await testDb.close(); await testDb.close();
try {
await mongod.stop(); await mongod.stop();
} catch (e) {}
}); });
tap.start({ throwOnError: true }); tap.start({ throwOnError: true });

View File

@ -1,6 +1,7 @@
import * as plugins from './smartdata.plugins'; import * as plugins from './smartdata.plugins';
import { SmartdataDb } from './smartdata.classes.db'; import { SmartdataDb } from './smartdata.classes.db';
import { SmartDataDbDoc } from './smartdata.classes.doc'; import { SmartDataDbDoc } from './smartdata.classes.doc';
import { CollectionFactory } from './smartdata.classes.collectionfactory';
export interface IFindOptions { export interface IFindOptions {
limit?: number; limit?: number;
@ -15,23 +16,26 @@ export interface IDocValidationFunc<T> {
export type TDelayedDbCreation = () => SmartdataDb; export type TDelayedDbCreation = () => SmartdataDb;
const collectionFactory = new CollectionFactory();
/** /**
* This is a decorator that will tell the decorated class what dbTable to use * This is a decorator that will tell the decorated class what dbTable to use
* @param dbArg * @param dbArg
*/ */
export function Collection(dbArg: SmartdataDb | TDelayedDbCreation) { export function Collection(dbArg: SmartdataDb | TDelayedDbCreation) {
return function (constructor) { return function classDecorator<T extends { new (...args: any[]): {} }>(constructor: T) {
if (dbArg instanceof SmartdataDb) { return class extends constructor {
// tslint:disable-next-line: no-string-literal public static get collection() {
constructor['smartdataCollection'] = new SmartdataCollection(constructor, dbArg); return collectionFactory.getCollection(constructor.name, dbArg);
} else {
constructor['smartdataDelayedCollection'] = () => {
return new SmartdataCollection(constructor, dbArg());
};
} }
public get collection() {
return collectionFactory.getCollection(constructor.name, dbArg);
}
};
}; };
} }
// tslint:disable-next-line: max-classes-per-file
export class SmartdataCollection<T> { export class SmartdataCollection<T> {
/** /**
* the collection that is used * the collection that is used
@ -42,13 +46,13 @@ export class SmartdataCollection<T> {
public smartdataDb: SmartdataDb; public smartdataDb: SmartdataDb;
public uniqueIndexes: string[] = []; public uniqueIndexes: string[] = [];
constructor(collectedClassArg: T & SmartDataDbDoc<T, unknown>, smartDataDbArg: SmartdataDb) { constructor(classNameArg: string, smartDataDbArg: SmartdataDb) {
// tell the collection where it belongs // tell the collection where it belongs
this.collectionName = collectedClassArg.name; this.collectionName = classNameArg;
this.smartdataDb = smartDataDbArg; this.smartdataDb = smartDataDbArg;
// tell the db class about it (important since Db uses different systems under the hood) // tell the db class about it (important since Db uses different systems under the hood)
this.smartdataDb.addTable(this); this.smartdataDb.addCollection(this);
} }
/** /**
@ -63,9 +67,9 @@ export class SmartdataCollection<T> {
}); });
if (!wantedCollection) { if (!wantedCollection) {
await this.smartdataDb.mongoDb.createCollection(this.collectionName); await this.smartdataDb.mongoDb.createCollection(this.collectionName);
console.log(`Successfully initiated Collection ${this.collectionName}`);
} }
this.mongoDbCollection = await this.smartdataDb.mongoDb.collection(this.collectionName); this.mongoDbCollection = this.smartdataDb.mongoDb.collection(this.collectionName);
// console.log(`Successfully initiated Collection ${this.collectionName}`);
} }
} }

View File

@ -0,0 +1,22 @@
import * as plugins from './smartdata.plugins';
import { SmartdataCollection } from './smartdata.classes.collection';
import { SmartdataDb } from './smartdata.classes.db';
export class CollectionFactory {
public collections: {[key: string]: SmartdataCollection<any>} = {};
public getCollection = (nameArg: string, dbArg: SmartdataDb | (() => SmartdataDb)): SmartdataCollection<any> => {
if (!this.collections[nameArg]) {
this.collections[nameArg] = (() => {
if (dbArg instanceof SmartdataDb) {
// tslint:disable-next-line: no-string-literal
return new SmartdataCollection(nameArg, dbArg);
} else {
dbArg = dbArg();
return new SmartdataCollection(nameArg, dbArg);
}
})();
}
return this.collections[nameArg];
}
}

View File

@ -32,6 +32,8 @@ export class SmartdataDb {
const finalConnectionUrl = this.smartdataOptions.mongoDbUrl const finalConnectionUrl = this.smartdataOptions.mongoDbUrl
.replace('<USERNAME>', this.smartdataOptions.mongoDbUser) .replace('<USERNAME>', this.smartdataOptions.mongoDbUser)
.replace('<username>', this.smartdataOptions.mongoDbUser) .replace('<username>', this.smartdataOptions.mongoDbUser)
.replace('<USER>', this.smartdataOptions.mongoDbUser)
.replace('<user>', this.smartdataOptions.mongoDbUser)
.replace('<PASSWORD>', this.smartdataOptions.mongoDbPass) .replace('<PASSWORD>', this.smartdataOptions.mongoDbPass)
.replace('<password>', this.smartdataOptions.mongoDbPass) .replace('<password>', this.smartdataOptions.mongoDbPass)
.replace('<DBNAME>', this.smartdataOptions.mongoDbName) .replace('<DBNAME>', this.smartdataOptions.mongoDbName)
@ -40,6 +42,8 @@ export class SmartdataDb {
this.mongoDbClient = await plugins.mongodb.MongoClient.connect(finalConnectionUrl, { this.mongoDbClient = await plugins.mongodb.MongoClient.connect(finalConnectionUrl, {
useNewUrlParser: true, useNewUrlParser: true,
useUnifiedTopology: true, useUnifiedTopology: true,
maxPoolSize: 100,
maxIdleTimeMS: 10
}); });
this.mongoDb = this.mongoDbClient.db(this.smartdataOptions.mongoDbName); this.mongoDb = this.mongoDbClient.db(this.smartdataOptions.mongoDbName);
this.status = 'connected'; this.status = 'connected';
@ -57,7 +61,7 @@ export class SmartdataDb {
// handle table to class distribution // handle table to class distribution
public addTable(SmartdataCollectionArg: SmartdataCollection<any>) { public addCollection(SmartdataCollectionArg: SmartdataCollection<any>) {
this.smartdataCollectionMap.add(SmartdataCollectionArg); this.smartdataCollectionMap.add(SmartdataCollectionArg);
} }

View File

@ -12,7 +12,7 @@ export type TDocCreation = 'db' | 'new' | 'mixed';
*/ */
export function svDb() { export function svDb() {
return (target: SmartDataDbDoc<unknown, unknown>, key: string) => { return (target: SmartDataDbDoc<unknown, unknown>, key: string) => {
console.log(`called svDb() on ${key}`); console.log(`called svDb() on >${target.constructor.name}.${key}<`);
if (!target.saveableProperties) { if (!target.saveableProperties) {
target.saveableProperties = []; target.saveableProperties = [];
} }
@ -25,7 +25,7 @@ export function svDb() {
*/ */
export function unI() { export function unI() {
return (target: SmartDataDbDoc<unknown, unknown>, key: string) => { return (target: SmartDataDbDoc<unknown, unknown>, key: string) => {
console.log('called unI'); console.log(`called unI on >>${target.constructor.name}.${key}<<`);
// mark the index as unique // mark the index as unique
if (!target.uniqueIndexes) { if (!target.uniqueIndexes) {
@ -45,7 +45,8 @@ export class SmartDataDbDoc<T, TImplements> {
/** /**
* the collection object an Doc belongs to * the collection object an Doc belongs to
*/ */
public collection: SmartdataCollection<T>; public static collection: SmartdataCollection<any>;
public collection: SmartdataCollection<any>;
/** /**
* how the Doc in memory was created, may prove useful later. * how the Doc in memory was created, may prove useful later.
@ -75,30 +76,12 @@ export class SmartDataDbDoc<T, TImplements> {
/** /**
* class constructor * class constructor
*/ */
constructor() { constructor() {}
this.name = this.constructor['name'];
if (this.constructor['smartdataCollection']) {
// tslint:disable-next-line: no-string-literal
this.collection = this.constructor['smartdataCollection'];
// tslint:disable-next-line: no-string-literal
} else if (typeof this.constructor['smartdataDelayedCollection'] === 'function') {
// tslint:disable-next-line: no-string-literal
this.collection = this.constructor['smartdataDelayedCollection']();
} else {
console.error('Could not determine collection for DbDoc');
}
}
public static async getInstances<T>(filterArg: Partial<T>): Promise<T[]> { public static async getInstances<T>(
const self: any = this; // fool typesystem filterArg: plugins.tsclass.typeFest.PartialDeep<T>
let referenceMongoDBCollection: SmartdataCollection<T>; ): Promise<T[]> {
const foundDocs = await this.collection.find(filterArg);
if (self.smartdataCollection) {
referenceMongoDBCollection = self.smartdataCollection;
} else if (self.smartdataDelayedCollection) {
referenceMongoDBCollection = self.smartdataDelayedCollection();
}
const foundDocs = await referenceMongoDBCollection.find(filterArg);
const returnArray = []; const returnArray = [];
for (const item of foundDocs) { for (const item of foundDocs) {
const newInstance = new this(); const newInstance = new this();
@ -111,7 +94,9 @@ export class SmartDataDbDoc<T, TImplements> {
return returnArray; return returnArray;
} }
public static async getInstance<T>(filterArg: Partial<T>): Promise<T> { public static async getInstance<T>(
filterArg: plugins.tsclass.typeFest.PartialDeep<T>
): Promise<T> {
const result = await this.getInstances<T>(filterArg); const result = await this.getInstances<T>(filterArg);
if (result && result.length > 0) { if (result && result.length > 0) {
return result[0]; return result[0];
@ -142,8 +127,7 @@ export class SmartDataDbDoc<T, TImplements> {
* deletes a document from the database * deletes a document from the database
*/ */
public async delete() { public async delete() {
const self: any = this; await this.collection.delete(this);
await this.collection.delete(self);
} }
/** /**

View File

@ -1,4 +1,9 @@
import * as assert from 'assert'; // tsclass scope
import * as tsclass from '@tsclass/tsclass';
export { tsclass };
// @pushrocks scope
import * as smartlog from '@pushrocks/smartlog'; import * as smartlog from '@pushrocks/smartlog';
import * as lodash from 'lodash'; import * as lodash from 'lodash';
import * as mongodb from 'mongodb'; import * as mongodb from 'mongodb';
@ -6,4 +11,4 @@ import * as smartq from '@pushrocks/smartpromise';
import * as smartstring from '@pushrocks/smartstring'; import * as smartstring from '@pushrocks/smartstring';
import * as smartunique from '@pushrocks/smartunique'; import * as smartunique from '@pushrocks/smartunique';
export { assert, smartlog, lodash, smartq, mongodb, smartstring, smartunique }; export { smartlog, lodash, smartq, mongodb, smartstring, smartunique };