BREAKING CHANGE(scope/db driver): switched to pushrocks scope and from rethinkdb to mongodb as db

This commit is contained in:
2018-07-08 23:48:14 +02:00
parent 2a1c45608f
commit 8b4f7169ff
16 changed files with 1176 additions and 1106 deletions

View File

@ -1,3 +1,3 @@
export * from "./smartdata.classes.db";
export * from "./smartdata.classes.dbtable";
export * from "./smartdata.classes.dbdoc";
export * from './smartdata.classes.db';
export * from './smartdata.classes.dbtable';
export * from './smartdata.classes.dbdoc';

View File

@ -1,51 +1,42 @@
import * as plugins from "./smartdata.plugins";
import { Objectmap } from "lik";
import * as plugins from './smartdata.plugins';
import { Objectmap } from 'lik';
import { DbTable } from "./smartdata.classes.dbtable";
import { SmartdataCollection } from './smartdata.classes.dbtable';
import { Connection as dbConnection, ConnectionOptions as IConnectionOptions } from "rethinkdb";
export {
IConnectionOptions
}
import * as mongoHelpers from './smartdata.mongohelpers';
/**
* interface - indicates the connection status of the db
*/
export type TConnectionStatus =
| "initial"
| "disconnected"
| "connected"
| "failed";
export type TConnectionStatus = 'initial' | 'disconnected' | 'connected' | 'failed';
export class Db {
dbName: string;
connectionOptions: plugins.rethinkDb.ConnectionOptions;
dbConnection: plugins.rethinkDb.Connection;
status: TConnectionStatus;
dbTablesMap = new Objectmap<DbTable<any>>();
constructor(connectionOptionsArg: IConnectionOptions) {
this.dbName = connectionOptionsArg.db;
this.connectionOptions = connectionOptionsArg;
this.status = "initial";
}
export interface ISmartdataOptions {
/**
* the URL to connect to
*/
mongoDbUrl: string;
/**
* supply additional SSl options needed to connect to certain Rethink DB servers (e.g. compose.io)
* the db to use for the project
*/
setSsl(certificateStringArg: string, formatArg: "base64" | "clearText") {
let certificateString: string;
if ((formatArg = "base64")) {
certificateString = plugins.smartstring.base64.decode(
certificateStringArg
);
} else {
certificateString = certificateStringArg;
}
this.connectionOptions["ssl"] = {
ca: Buffer.from(certificateString)
};
mongoDbName: string;
/**
* an optional password that will be replace <PASSWORD> in the connection string
*/
mongoDbPass?: string;
}
export class SmartdataDb {
smartdataOptions: ISmartdataOptions;
mongoDbClient: plugins.mongodb.MongoClient;
mongoDb: plugins.mongodb.Db;
status: TConnectionStatus;
smartdataCollectionMap = new Objectmap<SmartdataCollection<any>>();
constructor(smartdataOptions: ISmartdataOptions) {
this.smartdataOptions = smartdataOptions;
this.status = 'initial';
}
// basic connection stuff ----------------------------------------------
@ -54,35 +45,49 @@ export class Db {
* connects to the database that was specified during instance creation
*/
async connect(): Promise<any> {
this.dbConnection = await plugins.rethinkDb.connect(this.connectionOptions);
this.dbConnection.use(this.dbName);
this.status = "connected";
plugins.beautylog.ok(`Connected to database ${this.dbName}`);
let finalConnectionUrl = this.smartdataOptions.mongoDbUrl;
if (this.smartdataOptions.mongoDbPass) {
finalConnectionUrl = mongoHelpers.addPassword(
this.smartdataOptions.mongoDbName,
this.smartdataOptions.mongoDbPass
);
}
this.mongoDbClient = await plugins.mongodb.MongoClient.connect(
finalConnectionUrl,
{}
);
this.mongoDb = this.mongoDbClient.db(this.smartdataOptions.mongoDbName);
this.status = 'connected';
plugins.smartlog
.getDefaultLogger()
.info(`Connected to database ${this.smartdataOptions.mongoDbName}`);
}
/**
* closes the connection to the databse
*/
async close(): Promise<any> {
await this.dbConnection.close();
this.status = "disconnected";
plugins.beautylog.ok(`disconnected from database ${this.dbName}`);
await this.mongoDbClient.close();
this.status = 'disconnected';
plugins.smartlog
.getDefaultLogger()
.info(`disconnected from database ${this.smartdataOptions.mongoDbName}`);
}
// handle table to class distribution
addTable(dbTableArg: DbTable<any>) {
this.dbTablesMap.add(dbTableArg);
addTable(SmartdataCollectionArg: SmartdataCollection<any>) {
this.smartdataCollectionMap.add(SmartdataCollectionArg);
}
/**
* Gets a table's name and returns smartdata's DbTable class
* Gets a collection's name and returns a SmartdataCollection instance
* @param nameArg
* @returns DbTable
*/
async getDbTableByName<T>(nameArg: string): Promise<DbTable<T>> {
let resultCollection = this.dbTablesMap.find(dbTableArg => {
return dbTableArg.tableName === nameArg;
async getSmartdataCollectionByName<T>(nameArg: string): Promise<SmartdataCollection<T>> {
let resultCollection = this.smartdataCollectionMap.find(dbTableArg => {
return dbTableArg.collectionName === nameArg;
});
return resultCollection;
}

View File

@ -1,18 +1,18 @@
import * as plugins from "./smartdata.plugins";
import * as plugins from './smartdata.plugins';
import { Objectmap } from "lik";
import { Objectmap } from 'lik';
import { Db } from "./smartdata.classes.db";
import { DbTable } from "./smartdata.classes.dbtable";
import { SmartdataDb } from './smartdata.classes.db';
import { SmartdataCollection } from './smartdata.classes.dbtable';
export type TDocCreation = "db" | "new" | "mixed";
export type TDocCreation = 'db' | 'new' | 'mixed';
/**
* saveable - saveable decorator to be used on class properties
*/
export function svDb() {
return (target: DbDoc<any>, key: string) => {
console.log("called sva");
return (target: smartDataDbDoc<any>, key: string) => {
console.log('called sva');
if (!target.saveableProperties) {
target.saveableProperties = [];
}
@ -20,16 +20,16 @@ export function svDb() {
};
}
export class DbDoc<T> {
export class smartDataDbDoc<T> {
/**
* the collection object an Doc belongs to
*/
collection: DbTable<T>;
collection: SmartdataCollection<T>;
/**
* how the Doc in memory was created, may prove useful later.
*/
creationStatus: TDocCreation = "new";
creationStatus: TDocCreation = 'new';
/**
* an array of saveable properties of a doc
@ -44,25 +44,25 @@ export class DbDoc<T> {
/**
* primary id in the database
*/
dbId: string;
dbDocUniqueId: string;
/**
* class constructor
*/
constructor() {
this.name = this.constructor["name"];
this.collection = this.constructor["dbTable"];
this.name = this.constructor['name'];
this.collection = this.constructor['dbTable'];
}
static async getInstances<T>(filterArg): Promise<T[]> {
let self: any = this; // fool typesystem
let referenceTable: DbTable<T> = self.dbTable;
let referenceTable: SmartdataCollection<T> = self.dbTable;
const foundDocs = await referenceTable.find(filterArg);
const returnArray = [];
for (let item of foundDocs) {
let newInstance = new this();
for (let key in item) {
if(key !== 'id') {
if (key !== 'id') {
newInstance[key] = item[key];
}
}
@ -72,9 +72,9 @@ export class DbDoc<T> {
}
static async getInstance<T>(filterArg): Promise<T> {
let result = await this.getInstances<T>(filterArg)
if(result && result.length > 0) {
return result[0]
let result = await this.getInstances<T>(filterArg);
if (result && result.length > 0) {
return result[0];
}
}
@ -85,15 +85,15 @@ export class DbDoc<T> {
async save() {
let self: any = this;
switch (this.creationStatus) {
case "db":
case 'db':
await this.collection.update(self);
break;
case "new":
case 'new':
let writeResult = await this.collection.insert(self);
this.creationStatus = "db";
this.creationStatus = 'db';
break;
default:
console.error("neither new nor in db?");
console.error('neither new nor in db?');
}
}
@ -101,21 +101,21 @@ export class DbDoc<T> {
* also store any referenced objects to DB
* better for data consistency
*/
saveDeep(savedMapArg: Objectmap<DbDoc<any>> = null) {
saveDeep(savedMapArg: Objectmap<smartDataDbDoc<any>> = null) {
if (!savedMapArg) {
savedMapArg = new Objectmap<DbDoc<any>>();
savedMapArg = new Objectmap<smartDataDbDoc<any>>();
}
savedMapArg.add(this);
this.save();
for (let propertyKey in this) {
let property: any = this[propertyKey];
if (property instanceof DbDoc && !savedMapArg.checkForObject(property)) {
if (property instanceof smartDataDbDoc && !savedMapArg.checkForObject(property)) {
property.saveDeep(savedMapArg);
}
}
}
createSavableObject() {
async createSavableObject() {
let saveableObject: any = {}; // is not exposed to outside, so any is ok here
for (let propertyNameString of this.saveableProperties) {
saveableObject[propertyNameString] = this[propertyNameString];

View File

@ -1,9 +1,6 @@
import * as plugins from "./smartdata.plugins";
import { Db } from "./smartdata.classes.db";
import { DbDoc } from "./smartdata.classes.dbdoc";
// RethinkDb Interfaces
import { WriteResult, Cursor } from "rethinkdb";
import * as plugins from './smartdata.plugins';
import { SmartdataDb } from './smartdata.classes.db';
import { smartDataDbDoc } from './smartdata.classes.dbdoc';
export interface IFindOptions {
limit?: number;
@ -20,45 +17,45 @@ export interface IDocValidationFunc<T> {
* This is a decorator that will tell the decorated class what dbTable to use
* @param db
*/
export function Table(db: Db) {
export function Table(db: SmartdataDb) {
return function(constructor) {
constructor["dbTable"] = new DbTable(constructor, db);
constructor['mongoDbCollection'] = new SmartdataCollection(constructor, db);
};
}
export class DbTable<T> {
export class SmartdataCollection<T> {
/**
* the collection that is used
*/
table: plugins.rethinkDb.Table;
mongoDbCollection: plugins.mongodb.Collection;
objectValidation: IDocValidationFunc<T> = null;
tableName: string;
db: Db;
collectionName: string;
smartdataDb: SmartdataDb;
constructor(collectedClassArg: T & DbDoc<T>, dbArg: Db) {
constructor(collectedClassArg: T & smartDataDbDoc<T>, smartDataDbArg: SmartdataDb) {
// tell the collection where it belongs
this.tableName = collectedClassArg.name;
this.db = dbArg;
this.collectionName = collectedClassArg.name;
this.smartdataDb = smartDataDbArg;
// tell the db class about it (important since Db uses different systems under the hood)
this.db.addTable(this);
this.smartdataDb.addTable(this);
}
/**
* makes sure a collection exists within MongoDb that maps to the SmartdataCollection
*/
async init() {
if (!this.table) {
// connect this instance to a RethinkDB table
const availableTables = await plugins.rethinkDb
.db(this.db.dbName)
.tableList()
.run(this.db.dbConnection);
if (availableTables.indexOf(this.tableName)) {
await plugins.rethinkDb
.db(this.db.dbName)
.tableCreate(this.tableName)
.run(this.db.dbConnection);
if (!this.mongoDbCollection) {
// connect this instance to a MongoDB collection
const availableMongoDbCollections = await this.smartdataDb.mongoDb.collections();
const wantedCollection = availableMongoDbCollections.find(collection => {
return collection.collectionName === this.collectionName;
});
if (!wantedCollection) {
await this.smartdataDb.mongoDb.createCollection(this.collectionName);
}
this.mongoDbCollection = await this.smartdataDb.mongoDb.collection(this.collectionName);
}
this.table = plugins.rethinkDb.table(this.tableName);
}
/**
@ -73,36 +70,27 @@ export class DbTable<T> {
*/
async find(filterObject: any): Promise<any> {
await this.init();
let cursor = await plugins.rethinkDb
.table(this.tableName)
.filter(filterObject)
.run(this.db.dbConnection);
return await cursor.toArray();
}
/**
* create an object in the database
*/
async insert(dbDocArg: T & DbDoc<T>): Promise<WriteResult> {
async insert(dbDocArg: T & smartDataDbDoc<T>): Promise<any> {
await this.init();
await this.checkDoc(dbDocArg);
return await plugins.rethinkDb
.table(this.tableName)
.insert(dbDocArg.createSavableObject())
.run(this.db.dbConnection);
const saveableObject = await dbDocArg.createSavableObject();
const result = await this.mongoDbCollection.insertOne(saveableObject);
return result;
}
/**
* inserts object into the DbCollection
*/
async update(dbDocArg: T & DbDoc<T>): Promise<WriteResult> {
async update(dbDocArg: T & smartDataDbDoc<T>): Promise<any> {
await this.init();
await this.checkDoc(dbDocArg);
console.log(this.tableName, dbDocArg.createSavableObject());
return await plugins.rethinkDb
.table(this.tableName)
.update(dbDocArg.createSavableObject())
.run(this.db.dbConnection);
const saveableObject = await dbDocArg.createSavableObject();
this.mongoDbCollection.updateOne(saveableObject.dbDocUniqueId, saveableObject);
}
/**
@ -118,10 +106,8 @@ export class DbTable<T> {
if (validationResult) {
done.resolve();
} else {
done.reject("validation of object did not pass");
done.reject('validation of object did not pass');
}
return done.promise;
}
extractKey(writeResult: WriteResult) {}
}

View File

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

View File

@ -1,8 +1,8 @@
import * as assert from "assert";
import * as beautylog from "beautylog";
import * as lodash from "lodash";
import * as rethinkDb from "rethinkdb";
import * as smartq from "smartq";
import * as smartstring from "smartstring";
import * as assert from 'assert';
import * as smartlog from '@pushrocks/smartlog';
import * as lodash from 'lodash';
import * as mongodb from 'mongodb';
import * as smartq from 'smartq';
import * as smartstring from 'smartstring';
export { assert, beautylog, lodash, smartq, rethinkDb, smartstring };
export { assert, smartlog, lodash, smartq, mongodb, smartstring };