Archived
2
This repository has been archived on 2023-03-02. You can view files and clone it, but cannot push or open issues or pull requests.
definma-api/src/db.ts

162 lines
6.3 KiB
TypeScript
Raw Normal View History

import mongoose from 'mongoose';
import cfenv from 'cfenv';
2020-06-05 08:50:06 +02:00
import _ from 'lodash';
import ChangelogModel from './models/changelog';
2020-05-04 15:48:07 +02:00
// database urls, prod db url is retrieved automatically
const TESTING_URL = 'mongodb://localhost/dfopdb_test';
const DEV_URL = 'mongodb://localhost/dfopdb';
const debugging = true;
2020-07-09 16:30:10 +02:00
if (process.env.NODE_ENV !== 'production' && debugging) {
mongoose.set('debug', true); // enable mongoose debug
}
export default class db {
private static state = { // db object and current mode (test, dev, prod)
db: null,
mode: null,
};
2020-05-18 14:47:22 +02:00
static connect (mode = '', done: Function = () => {}) { // set mode to test for unit/integration tests, otherwise skip parameters. done is also only needed for testing
if (this.state.db) return done(); // db is already connected
// find right connection url
let connectionString: string = "";
if (mode === 'test') { // testing
connectionString = TESTING_URL;
this.state.mode = 'test';
}
else if(process.env.NODE_ENV === 'production') {
let services = cfenv.getAppEnv().getServices();
for (let service in services) {
if(services[service].tags.indexOf("mongodb") >= 0) {
connectionString = services[service]["credentials"].uri;
}
}
this.state.mode = 'prod';
}
else {
connectionString = DEV_URL;
this.state.mode = 'dev';
}
// connect to db
2020-05-06 14:39:04 +02:00
mongoose.connect(connectionString, {useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true, connectTimeoutMS: 10000}, err => {
if (err) done(err);
});
mongoose.connection.on('error', console.error.bind(console, 'connection error:'));
mongoose.connection.on('connected', () => { // evaluation connection behaviour on prod
if (process.env.NODE_ENV !== 'test') { // Do not interfere with testing
console.info('Database connected');
}
});
2020-04-23 13:59:45 +02:00
mongoose.connection.on('disconnected', () => { // reset state on disconnect
2020-05-28 11:47:51 +02:00
if (process.env.NODE_ENV !== 'test') { // Do not interfere with testing
console.info('Database disconnected');
// this.state.db = 0; // prod database connects and disconnects automatically
2020-05-28 11:47:51 +02:00
}
2020-04-23 13:59:45 +02:00
});
process.on('SIGINT', () => { // close connection when app is terminated
2020-05-28 11:47:51 +02:00
if (!this.state.db) { // database still connected
mongoose.connection.close(() => {
console.info('Mongoose default connection disconnected through app termination');
process.exit(0);
});
}
2020-04-23 13:59:45 +02:00
});
mongoose.connection.once('open', () => {
2020-04-23 17:46:00 +02:00
mongoose.set('useFindAndModify', false);
2020-05-07 21:55:29 +02:00
console.info(process.env.NODE_ENV === 'test' ? '' : `Connected to ${connectionString}`);
this.state.db = mongoose.connection;
done();
});
}
2020-05-28 11:47:51 +02:00
static disconnect (done) {
mongoose.connection.close(() => {
console.info(process.env.NODE_ENV === 'test' ? '' : `Disconnected from database`);
this.state.db = 0;
done();
});
}
static getState () {
return this.state;
}
static drop (done: Function = () => {}) { // drop all collections of connected db (only dev and test for safety reasons ;)
if (!this.state.db || this.state.mode === 'prod') return done(); // no db connection or prod db
this.state.db.db.listCollections().toArray((err, collections) => { // get list of all collections
if (collections.length === 0) { // there are no collections to drop
return done();
}
else {
let dropCounter = 0; // count number of dropped collections to know when to return done()
collections.forEach(collection => { // drop each collection
this.state.db.dropCollection(collection.name, () => {
if (++ dropCounter >= collections.length) { // all collections dropped
done();
}
});
});
}
});
}
static loadJson (json, done: Function = () => {}) { // insert given JSON data into db, uses core mongodb methods
2020-05-18 14:47:22 +02:00
if (!this.state.db || !json.hasOwnProperty('collections') || json.collections.length === 0) { // no db connection or nothing to load
return done();
2020-05-18 14:47:22 +02:00
}
let loadCounter = 0; // count number of loaded collections to know when to return done()
Object.keys(json.collections).forEach(collectionName => { // create each collection
2020-05-07 21:55:29 +02:00
json.collections[collectionName] = this.oidResolve(json.collections[collectionName]);
this.state.db.createCollection(collectionName, (err, collection) => {
2020-08-04 13:54:14 +02:00
if (err) {
console.error(err);
}
collection.insertMany(json.collections[collectionName], () => { // insert JSON data
if (++ loadCounter >= Object.keys(json.collections).length) { // all collections loaded
done();
}
});
});
});
}
2020-05-07 21:55:29 +02:00
2020-06-05 08:50:06 +02:00
// changelog entry
static log(req, thisOrCollection, conditions = null, data = null) { // expects (req, this (from query helper)) or (req, collection, conditions, data)
if (! (conditions || data)) { // (req, this)
data = thisOrCollection._update ? _.cloneDeep(thisOrCollection._update) : {}; // replace undefined with {}
Object.keys(data).forEach(key => {
if (key[0] === '$') {
data[key.substr(1)] = data[key];
delete data[key];
}
});
new ChangelogModel({action: req.method + ' ' + req.url, collectionName: thisOrCollection._collection.collectionName, conditions: thisOrCollection._conditions, data: data, user_id: req.authDetails.id ? req.authDetails.id : null}).save(err => {
if (err) console.error(err);
});
}
else { // (req, collection, conditions, data)
new ChangelogModel({action: req.method + ' ' + req.url, collectionName: thisOrCollection, conditions: conditions, data: data, user_id: req.authDetails.id ? req.authDetails.id : null}).save(err => {
if (err) console.error(err);
});
}
}
2020-05-07 21:55:29 +02:00
private static oidResolve (object: any) { // resolve $oid fields to actual ObjectIds recursively
Object.keys(object).forEach(key => {
2020-05-18 14:47:22 +02:00
if (object[key] !== null && object[key].hasOwnProperty('$oid')) { // found oid, replace
2020-05-07 21:55:29 +02:00
object[key] = mongoose.Types.ObjectId(object[key].$oid);
}
2020-05-18 14:47:22 +02:00
else if (typeof object[key] === 'object' && object[key] !== null) { // deeper into recursion
2020-05-07 21:55:29 +02:00
object[key] = this.oidResolve(object[key]);
}
});
return object;
}
2020-06-05 08:50:06 +02:00
};