Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(disk): use local mongdb instead of disk #87

Merged
merged 4 commits into from
Nov 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 6 additions & 6 deletions packages/core/src/__tests__/user.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,25 @@ describe('user', () => {
beforeAll(async () => {
this.henri = new Henri({ runlevel: 4 });
await this.henri.init();
});
}, 60000);

afterAll(async () => {
await this.henri.stop();
});
}, 60000);

test('should be defined', () => {
expect(this.henri.user).toBeDefined();
});
}, 15000);

test('should extend BaseModule', () => {
expect(this.henri.user).toBeInstanceOf(BaseModule);
});
}, 15000);

test('should match snapshot', () => {
const controllers = new User();

expect(controllers).toMatchSnapshot();
});
}, 15000);

test('encryption', async () => {
const { encrypt } = this.henri.user;
Expand All @@ -40,7 +40,7 @@ describe('user', () => {
await expect(encrypt()).rejects.toBeDefined();
await expect(encrypt('lydia')).rejects.toBeDefined();
await expect(encrypt(password)).resolves.toBeDefined();
});
}, 15000);

test('compare', async () => {
const { encrypt, compare } = this.henri.user;
Expand Down
186 changes: 36 additions & 150 deletions packages/disk/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
const Waterline = require('waterline');
const disk = require('sails-disk');
const HenriMongoose = require('@usehenri/mongoose');
const { MongoMemoryServer } = require('mongodb-memory-server');
const md5 = require('md5');
const debug = require('debug')('henri:disk');
const path = require('path');
const os = require('os');
const fs = require('fs');

/**
* Disk database adapter
*
* @class Disk
*/
class Disk {
class Disk extends HenriMongoose {
/**
* Creates an instance of Disk.
*
Expand All @@ -16,159 +21,49 @@ class Disk {
* @memberof Disk
*/
constructor(name, config, thisHenri) {
super(name, { url: 'soon' }, thisHenri);

this.adapterName = 'disk';
this.name = name;
this.config = config;
this.models = {};
this.user = null;
this.waterline = new Waterline();
this.instance = null;
this.sessionPath = '.tmp/nedb-sessions.db';
this.name = name;
this.mongod = null;
this.mongoUri = '';
this.henri = thisHenri;

this.addModel = this.addModel.bind(this);
this.overload = this.overload.bind(this);
this.getModels = this.getModels.bind(this);
this.getSessionConnector = this.getSessionConnector.bind(this);
this.start = this.start.bind(this);
this.stop = this.stop.bind(this);
debug('constructor => done');
}

/**
* Add a model to the store
* Starts the store
*
* @param {object} model The model object
* @param {string} user The user object name
* @returns {object} The model instance (initialized)
* @returns {Promise} Resolves or not
* @memberof Disk
*/
addModel(model, user) {
let obj = Object.assign({}, model);
let isUser = false;

if (obj.identity === user) {
obj = this.overload(obj, user);
this.user = obj.globalId;
isUser = true;
}

obj.schema._id = {
autoMigrations: { autoIncrement: true },
type: 'number',
};
obj.primaryKey = '_id';

obj.attributes = model.schema;
obj.datastore = this.name;

delete obj.schema;
delete obj.graphql;
async start() {
debug('starting %s', this.name);

const instance = Waterline.Model.extend(obj);
const dataPath = path.join(
os.tmpdir(),
`henri-mongo-${md5(process.cwd())}`
);

this.waterline.registerModel(instance);
if (isUser) {
this.user = obj.globalId;
if (!fs.existsSync(dataPath)) {
fs.mkdirSync(dataPath);
}
this.models[obj.globalId] = instance;

return this.models[obj.globalId];
}

/**
* Overload the user entity
*
* @param {any} model Current model
* @returns {object} the model
* @memberof Disk
*/
overload(model) {
this.henri.pen.info('disk', `user model`, model.globalId, `overloading...`);

model.schema.email = { required: true, type: 'string' };
model.schema.password = { required: true, type: 'string' };

model.beforeCreate = async (values, cb) => {
values.password = await this.henri.user.encrypt(values.password);
cb();
};
model.beforeUpdate = async (values, cb) => {
if (values.hasOwnProperty('password')) {
values.password = await this.henri.user.encrypt(values.password);
}
cb();
};
model.hasRole = async function(roles = []) {
let given = Array.isArray(roles) ? roles : [roles];

return given.every(element => this.roles.includes(element));
};

return model;
}

/**
* Returns the models of this store
*
* @returns {object} the models
* @memberof Disk
*/
getModels() {
return this.models || {};
}

/**
* Returns the session connector (for connect styles session storage)
*
* @param {function} session session-store function
* @returns {object} a store
* @memberof Disk
*/
getSessionConnector(session) {
// eslint-disable-next-line global-require
const NedbStore = require('nedb-session-store')(session);

return new NedbStore({
filename: this.sessionPath,
this.mongod = new MongoMemoryServer({
instance: {
dbName: 'henri',
dbPath: this.henri.isTest ? null : dataPath,
storageEngine: this.henri.isTest ? 'ephemeralForTest' : 'wiredTiger',
},
});
}

/**
* Start the store
*
* @returns {Promise} Resolves or not
* @memberof Disk
*/
async start() {
return new Promise(resolve => {
var config = {
adapters: {
disk: disk,
},

datastores: {
[this.name]: {
adapter: 'disk',
},
},
};
this.config.url = await this.mongod.getConnectionString();

this.waterline.initialize(config, (err, orm) => {
if (err) {
throw err;
}
this.instance = orm;
for (let name in this.models) {
if (typeof this.models[name] !== 'undefined') {
if (name === this.user) {
this.henri._user = orm.collections[name.toLowerCase()];
}
global[name] = orm.collections[name.toLowerCase()];
}
}
resolve();
});
});
return super.start();
}

/**
Expand All @@ -178,20 +73,11 @@ class Disk {
* @memberof Disk
*/
async stop() {
return new Promise(resolve => {
this.waterline.teardown(err => {
if (err) {
this.henri.pen.error(
'disk',
'something went wrong while stopping the orm',
err
);
debug('stopping %s', this.name);

return resolve(err);
}
setTimeout(() => resolve(), 250);
});
});
await super.stop();

await this.mongod.stop();
}
}

Expand Down
7 changes: 4 additions & 3 deletions packages/disk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
"author": "Felix-Antoine Paradis",
"license": "MIT",
"dependencies": {
"nedb-session-store": "^1.1.2",
"sails-disk": "^1.1.2",
"waterline": "^0.13.6"
"@usehenri/mongoose": "^0.37.0",
"debug": "^4.1.1",
"md5": "^2.2.1",
"mongodb-memory-server": "^6.0.1"
}
}
90 changes: 45 additions & 45 deletions packages/disk/types.js
Original file line number Diff line number Diff line change
@@ -1,51 +1,51 @@
/* eslint-disable sort-keys */
const types = require('mongoose').SchemaTypes;

module.exports = {
// Mongoose specific
Array: 'json',
Bool: 'boolean',
Boolean: 'boolean',
Buffer: 'string',
Date: 'number',
Decimal128: 'string',
DocumentArray: 'json',
Embedded: 'ref',
Mixed: 'string',
Number: 'number',
Object: 'json',
ObjectId: 'ref',
String: 'string',
String: types.String,
Number: types.Number,
Boolean: types.Boolean,
DocumentArray: types.DocumentArray,
Embedded: types.Embedded,
Array: types.Array,
Buffer: types.Buffer,
Date: types.Date,
ObjectId: types.ObjectId,
Mixed: types.Mixed,
Decimal128: types.Decimal128,
Object: types.Mixed,
Bool: types.Boolean,

// Sequelize specific
STRING: 'string',
CHAR: 'string',
TEXT: 'string',
TINYINT: 'number',
SMALLINT: 'number',
MEDIUMINT: 'number',
INTEGER: 'number',
BIGINT: 'number',
NUMBER: 'number',
FLOAT: 'number',
DOUBLE: 'number',
DECIMAL: 'number',
REAL: 'number',
BOOLEAN: 'boolean',
BLOB: 'string',
ENUM: 'json',
DATE: 'number',
DATEONLY: 'number',
TIME: 'number',
NOW: 'number',
UUID: 'string',
UUIDV1: 'string',
UUIDV4: 'string',
HSTORE: 'string',
JSON: 'json',
JSONB: 'json',
ARRAY: 'json',
RANGE: 'string',
GEOMETRY: 'string',
GEOGRAPHY: 'string',
VIRTUAL: 'json',
STRING: types.String,
CHAR: types.String,
TEXT: types.String,
TINYINT: types.Number,
SMALLINT: types.Number,
MEDIUMINT: types.Number,
INTEGER: types.Number,
BIGINT: types.Number,
NUMBER: types.Number,
FLOAT: types.Number,
DOUBLE: types.Number,
DECIMAL: types.Number,
REAL: types.Number,
BOOLEAN: types.Boolean,
BLOB: types.String,
ENUM: types.Array,
DATE: types.Date,
DATEONLY: types.Date,
TIME: types.Date,
NOW: types.Date,
UUID: types.ObjectId,
UUIDV1: types.ObjectId,
UUIDV4: types.ObjectId,
HSTORE: types.Mixed,
JSON: types.Mixed,
JSONB: types.Mixed,
ARRAY: types.Array,
RANGE: types.mixed,
GEOMETRY: types.Mixed,
GEOGRAPHY: types.Mixed,
VIRTUAL: types.Mixed,
};