// Copyright IBM Corp. 2015,2016. All Rights Reserved. // Node module: loopback-datasource-juggler // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT /* * Describe context objects of operation hooks in comprehensive HTML table. * Usage: * $ node support/describe-operation-hooks.js > hooks.html * $ open hooks.hml * */ var Promise = global.Promise = require('bluebird'); var DataSource = require('../').DataSource; var Memory = require('../lib/connectors/memory').Memory; var HOOK_NAMES = [ 'access', 'before save', 'persist', 'loaded', 'after save', 'before delete', 'after delete', ]; var dataSources = [ createOptimizedDataSource(), createUnoptimizedDataSource(), ]; var observedContexts = []; var lastId = 0; Promise.onPossiblyUnhandledRejection(function(err) { console.error('POSSIBLY UNHANDLED REJECTION', err.stack); }); var operations = [ function find(ds) { return ds.TestModel.find({ where: { id: '1' }}); }, function count(ds) { return ds.TestModel.count({ id: ds.existingInstance.id }); }, function create(ds) { return ds.TestModel.create({ name: 'created' }); }, function findOrCreate_found(ds) { return ds.TestModel.findOrCreate( { where: { name: ds.existingInstance.name }}, { name: ds.existingInstance.name }); }, function findOrCreate_create(ds) { return ds.TestModel.findOrCreate( { where: { name: 'new-record' }}, { name: 'new-record' }); }, function updateOrCreate_create(ds) { return ds.TestModel.updateOrCreate({ id: 'not-found', name: 'not found' }); }, function updateOrCreate_update(ds) { return ds.TestModel.updateOrCreate( { id: ds.existingInstance.id, name: 'new name' }); }, function replaceOrCreate_create(ds) { return ds.TestModel.replaceOrCreate({ id: 'not-found', name: 'not found' }); }, function replaceOrCreate_update(ds) { return ds.TestModel.replaceOrCreate( { id: ds.existingInstance.id, name: 'new name' }); }, function replaceById(ds) { return ds.TestModel.replaceById( ds.existingInstance.id, { name: 'new name' }); }, function updateAll(ds) { return ds.TestModel.updateAll({ name: 'searched' }, { name: 'updated' }); }, function prototypeSave(ds) { ds.existingInstance.name = 'changed'; return ds.existingInstance.save(); }, function prototypeUpdateAttributes(ds) { return ds.existingInstance.updateAttributes({ name: 'changed' }); }, function prototypeDelete(ds) { return ds.existingInstance.delete(); }, function deleteAll(ds) { return ds.TestModel.deleteAll({ name: ds.existingInstance.name }); }, ]; var p = setupTestModels(); operations.forEach(function(op) { p = p.then(runner(op)); }); p.then(report, function(err) { console.error(err.stack); }); function createOptimizedDataSource() { var ds = new DataSource({ connector: Memory }); ds.name = 'Optimized'; return ds; } function createUnoptimizedDataSource() { var ds = new DataSource({ connector: Memory }); ds.name = 'Unoptimized'; // disable optimized methods ds.connector.updateOrCreate = false; ds.connector.findOrCreate = false; ds.connector.replaceOrCreate = false; return ds; } function setupTestModels() { dataSources.forEach(function setupOnDataSource(ds) { var TestModel = ds.TestModel = ds.createModel('TestModel', { id: { type: String, id: true, default: uid }, name: { type: String, required: true }, extra: { type: String, required: false }, }); }); return Promise.resolve(); } function uid() { lastId += 1; return '' + lastId; } function runner(fn) { return function() { var res = Promise.resolve(); dataSources.forEach(function(ds) { res = res.then(function() { return resetStorage(ds); }).then(function() { observedContexts.push({ operation: fn.name, connector: ds.name, hooks: {}, }); return fn(ds); }); }); return res; }; } function resetStorage(ds) { var TestModel = ds.TestModel; HOOK_NAMES.forEach(function(hook) { TestModel.clearObservers(hook); }); return TestModel.deleteAll() .then(function() { return TestModel.create({ name: 'first' }); }) .then(function(instance) { // Look it up from DB so that default values are retrieved return TestModel.findById(instance.id); }) .then(function(instance) { ds.existingInstance = instance; return TestModel.create({ name: 'second' }); }) .then(function() { HOOK_NAMES.forEach(function(hook) { TestModel.observe(hook, function(ctx, next) { var row = observedContexts[observedContexts.length - 1]; row.hooks[hook] = Object.keys(ctx); next(); }); }); }); } function report() { console.log(''); // merge rows where Optimized and Unoptimized produce the same context observedContexts.forEach(function(row, ix) { if (!ix) return; var last = observedContexts[ix - 1]; if (row.operation != last.operation) return; if (JSON.stringify(row.hooks) !== JSON.stringify(last.hooks)) return; last.merge = true; row.skip = true; }); console.log('\n'); console.log('
'); HOOK_NAMES.forEach(function(h) { console.log(' | ' + h + ' | '); }); console.log('
---|---|
' + caption + ' | '); HOOK_NAMES.forEach(function(h) { var text = row.hooks[h] ? row.hooks[h].join('' + text + ' | '); }); console.log('