Compare commits

...

63 Commits

Author SHA1 Message Date
81e39d09e4 4.0.4 2021-06-09 14:10:08 +02:00
8e51b518b1 fix(core): update 2021-06-09 14:10:08 +02:00
8308d8d03b 4.0.3 2021-06-09 13:40:23 +02:00
97365ddf29 fix(core): update 2021-06-09 13:40:23 +02:00
55d96fa68d 4.0.2 2021-06-09 12:40:55 +02:00
54ec6accdf fix(core): update 2021-06-09 12:40:55 +02:00
fc5d092b25 4.0.1 2021-06-06 17:54:48 +02:00
dfba057562 fix(core): update 2021-06-06 17:54:47 +02:00
1ad05943b5 4.0.0 2021-06-06 17:48:37 +02:00
302e51a77f BREAKING CHANGE(filter design): filters now are congruent with their data types. Static extensions of doc base class now are fully typed with automatic reference to their child classes. 2021-06-06 17:48:37 +02:00
1330d03af2 3.1.56 2021-05-16 23:26:05 +00:00
a298e9d190 fix(core): update 2021-05-16 23:26:05 +00:00
e571ef347b 3.1.55 2021-02-05 21:16:46 +00:00
39a4c7ef3f fix(core): update 2021-02-05 21:16:45 +00:00
0d3518d990 3.1.54 2020-10-19 16:44:28 +00:00
fbdde2268c fix(core): update 2020-10-19 16:44:28 +00:00
8b6c26f45a 3.1.53 2020-10-19 16:37:45 +00:00
97e82ed75a fix(core): update 2020-10-19 16:37:44 +00:00
03baffd9fd 3.1.52 2020-09-25 21:05:22 +00:00
f29ae67580 fix(core): update 2020-09-25 21:05:21 +00:00
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
0984a1ade4 3.1.38 2020-08-18 12:53:45 +00:00
804701c96a fix(core): update 2020-08-18 12:53:44 +00:00
a3759fa484 3.1.37 2020-08-18 12:52:01 +00:00
ef38df62be fix(core): update 2020-08-18 12:52:00 +00:00
c17789e92e 3.1.36 2020-08-18 12:46:14 +00:00
0bf2ba554d fix(core): update 2020-08-18 12:46:14 +00:00
5cbf1a222a 3.1.35 2020-08-18 12:36:41 +00:00
f075530838 fix(core): update 2020-08-18 12:36:41 +00:00
efb83853fb 3.1.34 2020-08-18 12:35:16 +00:00
73300ca4d3 fix(core): update 2020-08-18 12:35:16 +00:00
1e946cdceb 3.1.33 2020-08-18 12:33:17 +00:00
608ff93a41 fix(core): update 2020-08-18 12:33:16 +00:00
6211953f14 3.1.32 2020-08-18 12:32:55 +00:00
99e520b776 fix(core): update 2020-08-18 12:32:54 +00:00
eda8297356 3.1.31 2020-08-18 12:05:08 +00:00
ffa52a5883 fix(core): update 2020-08-18 12:05:07 +00:00
1e83f0a0ef 3.1.30 2020-08-18 12:01:47 +00:00
0203eabdfd fix(core): update 2020-08-18 12:01:46 +00:00
13 changed files with 23463 additions and 2422 deletions

View File

@ -19,23 +19,35 @@ mirror:
stage: security
script:
- npmci git mirror
only:
- tags
tags:
- lossless
- docker
- notpriv
audit:
auditProductionDependencies:
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
stage: security
script:
- npmci npm prepare
- npmci command npm install --production --ignore-scripts
- npmci command npm config set registry https://registry.npmjs.org
- npmci command npm audit --audit-level=high --only=prod --production
tags:
- docker
auditDevDependencies:
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
stage: security
script:
- npmci npm prepare
- npmci command npm install --ignore-scripts
- npmci command npm config set registry https://registry.npmjs.org
- npmci command npm audit --audit-level=high
- npmci command npm audit --audit-level=high --only=dev
tags:
- lossless
- docker
- notpriv
allow_failure: true
# ====================
# test stage
@ -50,9 +62,7 @@ testStable:
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- lossless
- docker
- priv
testBuild:
stage: test
@ -63,9 +73,7 @@ testBuild:
- npmci command npm run build
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- lossless
- docker
- notpriv
release:
stage: release
@ -85,6 +93,8 @@ release:
codequality:
stage: metadata
allow_failure: true
only:
- tags
script:
- npmci command npm install -g tslint typescript
- npmci npm prepare

View File

@ -15,7 +15,7 @@
"properties": {
"projectType": {
"type": "string",
"enum": ["website", "element", "service", "npm"]
"enum": ["website", "element", "service", "npm", "wcc"]
}
}
}

25390
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "@pushrocks/smartdata",
"version": "3.1.29",
"version": "4.0.4",
"private": false,
"description": "do more with data",
"main": "dist_ts/index.js",
@ -21,27 +21,28 @@
},
"homepage": "https://gitlab.com/pushrocks/smartdata#README",
"dependencies": {
"@pushrocks/lik": "^4.0.13",
"@pushrocks/smartlog": "^2.0.35",
"@pushrocks/smartpromise": "^3.0.6",
"@pushrocks/smartstring": "^3.0.18",
"@pushrocks/smartunique": "^3.0.3",
"@types/lodash": "^4.14.155",
"@types/mongodb": "^3.5.20",
"lodash": "^4.17.15",
"mongodb": "^3.5.8",
"@pushrocks/lik": "^4.0.20",
"@pushrocks/smartlog": "^2.0.39",
"@pushrocks/smartpromise": "^3.1.5",
"@pushrocks/smartstring": "^3.0.24",
"@tsclass/tsclass": "^3.0.33",
"@types/lodash": "^4.14.169",
"@types/mongodb": "^3.6.12",
"lodash": "^4.17.21",
"mongodb": "^3.6.6",
"runtime-type-checks": "0.0.4"
},
"devDependencies": {
"@gitzone/tsbuild": "^2.1.24",
"@gitzone/tstest": "^1.0.33",
"@gitzone/tsbuild": "^2.1.25",
"@gitzone/tstest": "^1.0.54",
"@pushrocks/qenv": "^4.0.10",
"@pushrocks/tapbundle": "^3.2.1",
"@pushrocks/smartunique": "^3.0.3",
"@pushrocks/tapbundle": "^3.2.14",
"@types/mongodb-memory-server": "^2.3.0",
"@types/node": "^14.0.13",
"@types/node": "^15.3.0",
"@types/shortid": "0.0.29",
"mongodb-memory-server": "^6.6.1",
"tslint": "^6.1.2",
"mongodb-memory-server": "^6.9.6",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0"
},
"files": [
@ -55,5 +56,8 @@
"cli.js",
"npmextra.json",
"readme.md"
],
"browserslist": [
"last 1 chrome versions"
]
}

View File

@ -49,15 +49,16 @@ How RethinkDB's terms map to the ones of smartdata:
represents a Database. Naturally it has .connect() etc. methods on it.
```typescript
// Assuming toplevel await
import * as smartdata from 'smartdata';
const smartdataDb = new smartdata.SmartdataDb({
mongoDbUrl: '//someurl',
mongoDbName: 'myDatabase',
mongoDbPass: 'mypassword'
mongoDbPass: 'mypassword',
});
smartdataDb.connect();
await smartdataDb.connect();
```
### class DbCollection
@ -68,10 +69,11 @@ A collection is defined by the object class (that is extending smartdata.dbdoc)
So to get to get access to a specific collection you document
```typescript
// Assuming toplevel await
// continues from the block before...
@smartdata.Collection(smartdataDb)
class MyObject extends smartdata.DbDoc<MyObject> {
class MyObject extends smartdata.DbDoc<MyObject /* ,[an optional interface to implement] */> {
// read the next block about DbDoc
@smartdata.svDb()
property1: string; // @smartdata.svDb() marks the property for db save
@ -87,14 +89,22 @@ class MyObject extends smartdata.DbDoc<MyObject> {
const localObject = new MyObject({
property1: 'hi',
property2: 2
property2: {
deep: 3
},
});
localObject.save(); // saves the object to the database
await localObject.save(); // saves the object to the database
// start retrieving instances
MyObject.getInstance<MyObject>({
property: 'hi'
// .getInstance is staticly inheritied, yet fully typed static function to get instances with fully typed filters
const myInstance = await MyObject.getInstance({
property1: 'hi',
property2: {
deep: {
$gt: 2
} as any
}
}); // outputs a new instance of MyObject with the values from db assigned
```
@ -134,7 +144,6 @@ Since you define your classes in TypeScript and types flow through smartdata in
you should get all the Intellisense and type checking you love when using smartdata.
smartdata itself also bundles typings. You don't need to install any additional types for smartdata.
## Contribution
We are always happy for code contributions. If you are not the code contributing type that is ok. Still, maintaining Open Source repositories takes considerable time and thought. If you like the quality of what we do and our modules are useful to you we would appreciate a little monthly contribution: You can [contribute one time](https://lossless.link/contribute-onetime) or [contribute monthly](https://lossless.link/contribute). :)

View File

@ -3,33 +3,49 @@ import { Qenv } from '@pushrocks/qenv';
const testQenv = new Qenv(process.cwd(), process.cwd() + '/.nogit/');
console.log(process.memoryUsage());
// the tested module
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 { smartunique } from '../ts/smartdata.plugins';
// =======================================
// Connecting to the database server
// =======================================
let testDb: smartdata.SmartdataDb;
let smartdataOptions: smartdata.ISmartdataOptions;
let smartdataOptions: smartdata.IMongoDescriptor;
let mongod: mongoPlugin.MongoMemoryServer;
tap.test('should create a testinstance as database', async () => {
mongod = new mongoPlugin.MongoMemoryServer();
const totalCars = 2000;
tap.skip.test('should create a testinstance as database', async () => {
mongod = new mongoPlugin.MongoMemoryServer({});
console.log('created mongod instance');
await mongod._startUpInstance().catch((err) => {
console.log(err);
});
console.log('mongod started');
smartdataOptions = {
mongoDbName: await mongod.getDbName(),
mongoDbPass: '',
mongoDbUrl: await mongod.getConnectionString()
mongoDbUrl: await mongod.getUri(),
};
console.log(smartdataOptions);
testDb = new smartdata.SmartdataDb(smartdataOptions);
});
tap.test('should establish a connection to the rethink Db cluster', async () => {
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 () => {
await testDb.init();
});
@ -54,6 +70,11 @@ class Car extends smartdata.SmartDataDbDoc<Car, Car> {
@smartdata.svDb()
public brand: string;
@smartdata.svDb()
deepData = {
sodeep: 'yes',
};
constructor(colorArg: string, brandArg: string) {
super();
this.color = colorArg;
@ -68,20 +89,70 @@ tap.test('should save the car to the db', async () => {
const myCar2 = new Car('red', 'Volvo');
await myCar2.save();
const myCar3 = new Car('red', 'Renault');
await myCar3.save();
let counter = 0;
process.memoryUsage();
do {
const myCar3 = new Car('red', 'Renault');
await myCar3.save();
counter++;
if (counter % 100 === 0) {
console.log(
`Filled database with ${counter} of ${totalCars} Cars and memory usage ${
process.memoryUsage().rss / 1e6
} MB`
);
}
} while (counter < totalCars);
console.log(process.memoryUsage());
});
tap.test('expect to get instance of Car', async () => {
const myCars = await Car.getInstances<Car>({
brand: 'Volvo'
});
expect(myCars[0].color).to.equal('red');
tap.test('expect to get instance of Car with shallow match', async () => {
const totalQueryCycles = totalCars / 4;
let counter = 0;
do {
const timeStart = Date.now();
const myCars = await Car.getInstances({
brand: 'Renault',
});
if (counter % 10 === 0) {
console.log(
`performed ${counter} of ${totalQueryCycles} total query cycles: took ${
Date.now() - timeStart
}ms to query a set of 2000 with memory footprint ${process.memoryUsage().rss / 1e6} MB`
);
}
expect(myCars[0].deepData.sodeep).to.equal('yes');
expect(myCars[0].brand).to.equal('Renault');
counter++;
} while (counter < totalQueryCycles);
});
tap.test('expect to get instance of Car with deep match', async () => {
const totalQueryCycles = totalCars / 4;
let counter = 0;
do {
const timeStart = Date.now();
const myCars2 = await Car.getInstances({
deepData: {
sodeep: 'yes'
},
});
if (counter % 10 === 0) {
console.log(
`performed ${counter} of ${totalQueryCycles} total query cycles: took ${
Date.now() - timeStart
}ms to deep query a set of 2000 with memory footprint ${process.memoryUsage().rss / 1e6} MB`
);
}
expect(myCars2[0].deepData.sodeep).to.equal('yes');
expect(myCars2[0].brand).to.equal('Volvo');
counter++;
} while (counter < totalQueryCycles);
});
tap.test('expect to get instance of Car and update it', async () => {
const myCar = await Car.getInstance<Car>({
brand: 'Volvo'
brand: 'Volvo',
});
expect(myCar.color).to.equal('red');
myCar.color = 'blue';
@ -89,14 +160,18 @@ tap.test('expect to get instance of Car and update it', async () => {
});
tap.test('should be able to delete an instance of car', async () => {
const myCar = await Car.getInstance<Car>({
brand: 'Volvo'
const myCars = await Car.getInstances({
brand: 'Volvo',
color: 'blue',
});
expect(myCar.color).to.equal('blue');
await myCar.delete();
console.log(myCars);
expect(myCars[0].color).to.equal('blue');
for (const myCar of myCars) {
await myCar.delete();
}
const myCar2 = await Car.getInstance<Car>({
brand: 'Volvo'
brand: 'Volvo',
});
expect(myCar2.color).to.equal('red');
});
@ -125,19 +200,23 @@ class Truck extends smartdata.SmartDataDbDoc<Car, Car> {
tap.test('should store a new Truck', async () => {
const truck = new Truck('blue', 'MAN');
await truck.save();
const myTruck = await Truck.getInstance<Truck>({ color: 'blue' });
const myTruck = await Truck.getInstance({ color: 'blue' });
myTruck.id = 'foo';
await myTruck.save();
const myTruck2 = await Truck.getInstance<Truck>({ color: 'blue' });
const myTruck2 = await Truck.getInstance({ color: 'blue' });
console.log(myTruck2);
});
tap.test('should ', async () => {})
// =======================================
// close the database connection
// =======================================
tap.test('should close the database connection', async tools => {
tap.test('should close the database connection', async (tools) => {
await testDb.close();
await mongod.stop();
try {
await mongod.stop();
} catch (e) {}
});
tap.start({ throwOnError: true });

View File

@ -1,5 +1,22 @@
export interface IMongoDescriptor {
mongoDbName: string;
/**
* the URL to connect to
*/
mongoDbUrl: string;
mongoDbPass: string;
/**
* the db to use for the project
*/
mongoDbName?: string;
/**
* a username to use to connect to the database
*/
mongoDbUser?: string;
/**
* an optional password that will be replace <PASSWORD> in the connection string
*/
mongoDbPass?: string;
}

View File

@ -1,6 +1,7 @@
import * as plugins from './smartdata.plugins';
import { SmartdataDb } from './smartdata.classes.db';
import { SmartDataDbDoc } from './smartdata.classes.doc';
import { CollectionFactory } from './smartdata.classes.collectionfactory';
export interface IFindOptions {
limit?: number;
@ -13,25 +14,76 @@ export interface IDocValidationFunc<T> {
(doc: T): boolean;
}
export type TDelayedDbCreation = () => SmartdataDb;
export type TDelayed<TDelayedArg> = () => TDelayedArg;
const collectionFactory = new CollectionFactory();
/**
* This is a decorator that will tell the decorated class what dbTable to use
* @param dbArg
*/
export function Collection(dbArg: SmartdataDb | TDelayedDbCreation) {
return function(constructor) {
if (dbArg instanceof SmartdataDb) {
// tslint:disable-next-line: no-string-literal
constructor['smartdataCollection'] = new SmartdataCollection(constructor, dbArg);
} else {
constructor['smartdataDelayedCollection'] = () => {
return new SmartdataCollection(constructor, dbArg());
};
}
export function Collection(dbArg: SmartdataDb | TDelayed<SmartdataDb>) {
return function classDecorator<T extends { new (...args: any[]): {} }>(constructor: T) {
return class extends constructor {
public static get collection() {
if (!(dbArg instanceof SmartdataDb)) {
dbArg = dbArg();
}
return collectionFactory.getCollection(constructor.name, dbArg);
}
public get collection() {
if (!(dbArg instanceof SmartdataDb)) {
dbArg = dbArg();
}
return collectionFactory.getCollection(constructor.name, dbArg);
}
};
};
}
export interface IManager {
db: SmartdataDb
}
/**
* This is a decorator that will tell the decorated class what dbTable to use
* @param dbArg
*/
export function Manager<TManager extends IManager>(managerArg?: TManager | TDelayed<TManager>) {
return function classDecorator<T extends { new (...args: any[]): {} }>(constructor: T) {
return class extends constructor {
public static get collection() {
let dbArg: SmartdataDb;
if (managerArg['db']) {
dbArg = (managerArg as TManager).db
} else {
dbArg = (managerArg as TDelayed<TManager>)().db;
}
return collectionFactory.getCollection(constructor.name, dbArg);
}
public get collection() {
let dbArg: SmartdataDb;
if (managerArg['db']) {
dbArg = (managerArg as TManager).db
} else {
dbArg = (managerArg as TDelayed<TManager>)().db;
}
return collectionFactory.getCollection(constructor.name, dbArg);
}
public get manager() {
let manager: TManager;
if (managerArg['db']) {
manager = (managerArg as TManager);
} else {
manager = (managerArg as TDelayed<TManager>)();
}
return manager;
}
};
};
}
// tslint:disable-next-line: max-classes-per-file
export class SmartdataCollection<T> {
/**
* the collection that is used
@ -42,13 +94,13 @@ export class SmartdataCollection<T> {
public smartdataDb: SmartdataDb;
public uniqueIndexes: string[] = [];
constructor(collectedClassArg: T & SmartDataDbDoc<T, unknown>, smartDataDbArg: SmartdataDb) {
constructor(classNameArg: string, smartDataDbArg: SmartdataDb) {
// tell the collection where it belongs
this.collectionName = collectedClassArg.name;
this.collectionName = classNameArg;
this.smartdataDb = smartDataDbArg;
// tell the db class about it (important since Db uses different systems under the hood)
this.smartdataDb.addTable(this);
this.smartdataDb.addCollection(this);
}
/**
@ -58,14 +110,14 @@ export class SmartdataCollection<T> {
if (!this.mongoDbCollection) {
// connect this instance to a MongoDB collection
const availableMongoDbCollections = await this.smartdataDb.mongoDb.collections();
const wantedCollection = availableMongoDbCollections.find(collection => {
const wantedCollection = availableMongoDbCollections.find((collection) => {
return collection.collectionName === this.collectionName;
});
if (!wantedCollection) {
await this.smartdataDb.mongoDb.createCollection(this.collectionName);
console.log(`Successfully initiated Collection ${this.collectionName}`);
}
this.mongoDbCollection = await this.smartdataDb.mongoDb.collection(this.collectionName);
// console.log(`Successfully initiated Collection ${this.collectionName}`);
this.mongoDbCollection = this.smartdataDb.mongoDb.collection(this.collectionName);
}
}
@ -76,7 +128,7 @@ export class SmartdataCollection<T> {
for (const key of keyArrayArg) {
if (!this.uniqueIndexes.includes(key)) {
this.mongoDbCollection.createIndex(key, {
unique: true
unique: true,
});
// make sure we only call this once and not for every doc we create
this.uniqueIndexes.push(key);
@ -127,11 +179,12 @@ export class SmartdataCollection<T> {
}
updateableObject[key] = saveableObject[key];
}
this.mongoDbCollection.updateOne(
const result = await this.mongoDbCollection.updateOne(
identifiableObject,
{ $set: updateableObject },
{ upsert: true }
);
return result;
}
public async delete(dbDocArg: T & SmartDataDbDoc<T, unknown>): Promise<any> {
@ -139,7 +192,7 @@ export class SmartdataCollection<T> {
await this.checkDoc(dbDocArg);
const identifiableObject = await dbDocArg.createIdentifiableObject();
await this.mongoDbCollection.deleteOne(identifiableObject, {
w: 1
w: 1,
});
}

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
): 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);
}
})();
}
return this.collections[nameArg];
};
}

View File

@ -3,39 +3,22 @@ import { ObjectMap } from '@pushrocks/lik';
import { SmartdataCollection } from './smartdata.classes.collection';
import * as mongoHelpers from './smartdata.mongohelpers';
import { logger } from './smartdata.logging';
import { IMongoDescriptor } from './interfaces';
/**
* interface - indicates the connection status of the db
*/
export type TConnectionStatus = 'initial' | 'disconnected' | 'connected' | 'failed';
export interface ISmartdataOptions {
/**
* the URL to connect to
*/
mongoDbUrl: string;
/**
* the db to use for the project
*/
mongoDbName: string;
/**
* an optional password that will be replace <PASSWORD> in the connection string
*/
mongoDbPass?: string;
}
export class SmartdataDb {
smartdataOptions: ISmartdataOptions;
smartdataOptions: IMongoDescriptor;
mongoDbClient: plugins.mongodb.MongoClient;
mongoDb: plugins.mongodb.Db;
status: TConnectionStatus;
smartdataCollectionMap = new ObjectMap<SmartdataCollection<any>>();
constructor(smartdataOptions: ISmartdataOptions) {
constructor(smartdataOptions: IMongoDescriptor) {
this.smartdataOptions = smartdataOptions;
this.status = 'initial';
}
@ -46,17 +29,21 @@ export class SmartdataDb {
* connects to the database that was specified during instance creation
*/
public async init(): Promise<any> {
let finalConnectionUrl = this.smartdataOptions.mongoDbUrl;
if (this.smartdataOptions.mongoDbPass) {
finalConnectionUrl = mongoHelpers.addPassword(
this.smartdataOptions.mongoDbUrl,
this.smartdataOptions.mongoDbPass
);
}
console.log(`connection Url: ${finalConnectionUrl}`);
const finalConnectionUrl = this.smartdataOptions.mongoDbUrl
.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('<DBNAME>', this.smartdataOptions.mongoDbName)
.replace('<dbname>', this.smartdataOptions.mongoDbName);
this.mongoDbClient = await plugins.mongodb.MongoClient.connect(finalConnectionUrl, {
useNewUrlParser: true,
useUnifiedTopology: true
useUnifiedTopology: true,
maxPoolSize: 100,
maxIdleTimeMS: 10,
});
this.mongoDb = this.mongoDbClient.db(this.smartdataOptions.mongoDbName);
this.status = 'connected';
@ -74,7 +61,7 @@ export class SmartdataDb {
// handle table to class distribution
public addTable(SmartdataCollectionArg: SmartdataCollection<any>) {
public addCollection(SmartdataCollectionArg: SmartdataCollection<any>) {
this.smartdataCollectionMap.add(SmartdataCollectionArg);
}
@ -84,7 +71,7 @@ export class SmartdataDb {
* @returns DbTable
*/
public async getSmartdataCollectionByName<T>(nameArg: string): Promise<SmartdataCollection<T>> {
const resultCollection = this.smartdataCollectionMap.find(dbTableArg => {
const resultCollection = this.smartdataCollectionMap.find((dbTableArg) => {
return dbTableArg.collectionName === nameArg;
});
return resultCollection;

View File

@ -3,7 +3,7 @@ import * as plugins from './smartdata.plugins';
import { ObjectMap } from '@pushrocks/lik';
import { SmartdataDb } from './smartdata.classes.db';
import { SmartdataCollection } from './smartdata.classes.collection';
import { IManager, SmartdataCollection } from './smartdata.classes.collection';
export type TDocCreation = 'db' | 'new' | 'mixed';
@ -12,7 +12,7 @@ export type TDocCreation = 'db' | 'new' | 'mixed';
*/
export function svDb() {
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) {
target.saveableProperties = [];
}
@ -25,7 +25,7 @@ export function svDb() {
*/
export function unI() {
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
if (!target.uniqueIndexes) {
@ -41,11 +41,13 @@ export function unI() {
};
}
export class SmartDataDbDoc<T, TImplements> {
export class SmartDataDbDoc<T extends TImplements, TImplements, TManager extends IManager = any> {
/**
* the collection object an Doc belongs to
*/
public collection: SmartdataCollection<T>;
public static collection: SmartdataCollection<any>;
public collection: SmartdataCollection<any>;
public manager: TManager;
/**
* how the Doc in memory was created, may prove useful later.
@ -75,34 +77,38 @@ export class SmartDataDbDoc<T, TImplements> {
/**
* class 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');
}
}
constructor() {}
public static async getInstances<T>(filterArg): Promise<T[]> {
const self: any = this; // fool typesystem
let referenceMongoDBCollection: SmartdataCollection<T>;
if (self.smartdataCollection) {
referenceMongoDBCollection = self.smartdataCollection;
} else if (self.smartdataDelayedCollection) {
referenceMongoDBCollection = self.smartdataDelayedCollection();
public static async getInstances<T>(
this: plugins.tsclass.typeFest.Class<T>,
filterArg: plugins.tsclass.typeFest.PartialDeep<T>
): Promise<T[]> {
const convertedFilter: any = {};
const convertFilterArgument = (keyPathArg: string, filterArg2: any) => {
if (typeof filterArg2 === 'object') {
for (const key of Object.keys(filterArg2)) {
if (key.startsWith('$')) {
convertedFilter[keyPathArg] = filterArg2;
return;
} else if (key.includes('.')) {
throw new Error('keys cannot contain dots');
}
}
for (const key of Object.keys(filterArg2)) {
convertFilterArgument(`${keyPathArg}.${key}`, filterArg2[key]);
}
} else {
convertedFilter[keyPathArg] = filterArg2
}
}
const foundDocs = await referenceMongoDBCollection.find(filterArg);
for (const key of Object.keys(filterArg)) {
convertFilterArgument(key, filterArg[key]);
}
const foundDocs = await (this as any).collection.find(convertedFilter);
const returnArray = [];
for (const item of foundDocs) {
const newInstance = new this();
newInstance.creationStatus = 'db';
(newInstance as any).creationStatus = 'db';
for (const key of Object.keys(item)) {
newInstance[key] = item[key];
}
@ -111,8 +117,11 @@ export class SmartDataDbDoc<T, TImplements> {
return returnArray;
}
public static async getInstance<T>(filterArg): Promise<T> {
const result = await this.getInstances<T>(filterArg);
public static async getInstance<T>(
this: plugins.tsclass.typeFest.Class<T>,
filterArg: plugins.tsclass.typeFest.PartialDeep<T>
): Promise<T> {
const result = await (this as any).getInstances(filterArg);
if (result && result.length > 0) {
return result[0];
}
@ -125,25 +134,26 @@ export class SmartDataDbDoc<T, TImplements> {
public async save() {
// tslint:disable-next-line: no-this-assignment
const self: any = this;
let dbResult: any;
switch (this.creationStatus) {
case 'db':
await this.collection.update(self);
dbResult = await this.collection.update(self);
break;
case 'new':
const writeResult = await this.collection.insert(self);
dbResult = await this.collection.insert(self);
this.creationStatus = 'db';
break;
default:
console.error('neither new nor in db?');
}
return dbResult;
}
/**
* deletes a document from the database
*/
public async delete() {
const self: any = this;
await this.collection.delete(self);
await this.collection.delete(this);
}
/**

View File

@ -1,7 +0,0 @@
export const addUsername = (mongoUrlArg: string, usernameArg: string): string => {
return mongoUrlArg.replace('<USERNAME>', usernameArg);
};
export const addPassword = (mongoUrlArg: string, passwordArg: string): string => {
return mongoUrlArg.replace('<PASSWORD>', passwordArg);
};

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 lodash from 'lodash';
import * as mongodb from 'mongodb';
@ -6,4 +11,4 @@ import * as smartq from '@pushrocks/smartpromise';
import * as smartstring from '@pushrocks/smartstring';
import * as smartunique from '@pushrocks/smartunique';
export { assert, smartlog, lodash, smartq, mongodb, smartstring, smartunique };
export { smartlog, lodash, smartq, mongodb, smartstring, smartunique };