lib/waterline/methods/archive-one.js
/**
* Module dependencies
*/
var util = require('util');
var _ = require('@sailshq/lodash');
var flaverr = require('flaverr');
var parley = require('parley');
var forgeStageTwoQuery = require('../utils/query/forge-stage-two-query');
var getQueryModifierMethods = require('../utils/query/get-query-modifier-methods');
var verifyModelMethodContext = require('../utils/query/verify-model-method-context');
/**
* Module constants
*/
var DEFERRED_METHODS = getQueryModifierMethods('archiveOne');
/**
* archiveOne()
*
* Archive (s.k.a. "soft-delete") a record that matches the specified criteria,
* saving it as a new records in the built-in Archive model, then destroying
* the original. (Returns the original, now-destroyed record.)
*
* @experimental
*
* TODO: document further
*/
module.exports = function archiveOne(criteria, explicitCbMaybe, metaContainer){
// Verify `this` refers to an actual Sails/Waterline model.
verifyModelMethodContext(this);
// Set up a few, common local vars for convenience / familiarity.
var WLModel = this;
var orm = this.waterline;
var modelIdentity = this.identity;
// Potentially build an omen for use below.
var omenMaybe = flaverr.omen(archiveOne);
// Build initial query.
var query = {
method: 'archiveOne',
using: modelIdentity,
criteria: criteria,
meta: metaContainer
};
// ██╗ ██╗ █████╗ ██████╗ ██╗ █████╗ ██████╗ ██╗ ██████╗███████╗
// ██║ ██║██╔══██╗██╔══██╗██║██╔══██╗██╔══██╗██║██╔════╝██╔════╝
// ██║ ██║███████║██████╔╝██║███████║██║ ██║██║██║ ███████╗
// ╚██╗ ██╔╝██╔══██║██╔══██╗██║██╔══██║██║ ██║██║██║ ╚════██║
// ╚████╔╝ ██║ ██║██║ ██║██║██║ ██║██████╔╝██║╚██████╗███████║
// ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═════╝╚══════╝
//
// N/A
// (there are no out-of-order, optional arguments)
// ██████╗ ███████╗███████╗███████╗██████╗
// ██╔══██╗██╔════╝██╔════╝██╔════╝██╔══██╗
// ██║ ██║█████╗ █████╗ █████╗ ██████╔╝
// ██║ ██║██╔══╝ ██╔══╝ ██╔══╝ ██╔══██╗
// ██████╔╝███████╗██║ ███████╗██║ ██║
// ╚═════╝ ╚══════╝╚═╝ ╚══════╝╚═╝ ╚═╝
//
// ██╗███╗ ███╗ █████╗ ██╗ ██╗██████╗ ███████╗██╗
// ██╔╝████╗ ████║██╔══██╗╚██╗ ██╔╝██╔══██╗██╔════╝╚██╗
// ██║ ██╔████╔██║███████║ ╚████╔╝ ██████╔╝█████╗ ██║
// ██║ ██║╚██╔╝██║██╔══██║ ╚██╔╝ ██╔══██╗██╔══╝ ██║
// ╚██╗██║ ╚═╝ ██║██║ ██║ ██║ ██████╔╝███████╗██╔╝
// ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝
//
// ┌┐ ┬ ┬┬┬ ┌┬┐ ┬ ┬─┐┌─┐┌┬┐┬ ┬┬─┐┌┐┌ ┌┐┌┌─┐┬ ┬ ┌┬┐┌─┐┌─┐┌─┐┬─┐┬─┐┌─┐┌┬┐
// ├┴┐│ │││ ││ ┌┼─ ├┬┘├┤ │ │ │├┬┘│││ │││├┤ │││ ││├┤ ├┤ ├┤ ├┬┘├┬┘├┤ ││
// └─┘└─┘┴┴─┘─┴┘ └┘ ┴└─└─┘ ┴ └─┘┴└─┘└┘ ┘└┘└─┘└┴┘ ─┴┘└─┘└ └─┘┴└─┴└─└─┘─┴┘
// ┌─ ┬┌─┐ ┬─┐┌─┐┬ ┌─┐┬ ┬┌─┐┌┐┌┌┬┐ ─┐
// │─── │├┤ ├┬┘├┤ │ ├┤ └┐┌┘├─┤│││ │ ───│
// └─ ┴└ ┴└─└─┘┴─┘└─┘ └┘ ┴ ┴┘└┘ ┴ ─┘
// If a callback function was not specified, then build a new Deferred and bail now.
//
// > This method will be called AGAIN automatically when the Deferred is executed.
// > and next time, it'll have a callback.
return parley(
function (done){
// Otherwise, IWMIH, we know that a callback was specified.
// So...
// ███████╗██╗ ██╗███████╗ ██████╗██╗ ██╗████████╗███████╗
// ██╔════╝╚██╗██╔╝██╔════╝██╔════╝██║ ██║╚══██╔══╝██╔════╝
// █████╗ ╚███╔╝ █████╗ ██║ ██║ ██║ ██║ █████╗
// ██╔══╝ ██╔██╗ ██╔══╝ ██║ ██║ ██║ ██║ ██╔══╝
// ███████╗██╔╝ ██╗███████╗╚██████╗╚██████╔╝ ██║ ███████╗
// ╚══════╝╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚══════╝
// ╔═╗╔═╗╦═╗╔═╗╔═╗ ┌─┐┌┬┐┌─┐┌─┐┌─┐ ┌┬┐┬ ┬┌─┐ ┌─┐ ┬ ┬┌─┐┬─┐┬ ┬
// ╠╣ ║ ║╠╦╝║ ╦║╣ └─┐ │ ├─┤│ ┬├┤ │ ││││ │ │─┼┐│ │├┤ ├┬┘└┬┘
// ╚ ╚═╝╩╚═╚═╝╚═╝ └─┘ ┴ ┴ ┴└─┘└─┘ ┴ └┴┘└─┘ └─┘└└─┘└─┘┴└─ ┴
//
// Forge a stage 2 query (aka logical protostatement)
// This ensures a normalized format.
try {
forgeStageTwoQuery(query, orm);
} catch (err) {
switch (err.code) {
case 'E_INVALID_CRITERIA':
return done(
flaverr({
name: 'UsageError',
code: err.code,
details: err.details,
message:
'Invalid criteria.\n'+
'Details:\n'+
' '+err.details+'\n'
}, omenMaybe)
);
case 'E_NOOP':
// Determine the appropriate no-op result.
// If `fetch` meta key is set, use `[]`-- otherwise use `undefined`.
var noopResult = undefined;
return done(undefined, noopResult);
default:
return done(err);
}
}
// Do a .count() to ensure that there are ≤1 matching records.
// FUTURE: Make this transactional, if supported by the underlying adapter.
var modifiedCriteriaForCount = _.omit(query.criteria, ['select', 'omit', 'limit', 'skip', 'sort']);
WLModel.count(modifiedCriteriaForCount, function _afterCounting(err, total) {
if (err) {
return done(err);
}
// If more than one matching record was found, then consider this an error.
if (total > 1) {
return done(flaverr({
message:
'Preventing `.'+query.method+'()`: found too many ('+total+') matching records.\n'+
'\n'+
'Criteria used:\n'+
'···\n'+
util.inspect(modifiedCriteriaForCount,{depth:5})+'\n'+
'···'
}, omenMaybe));
}//-•
// Build a modified shallow clone of the originally-provided `meta` from
// userland, but that also has `fetch: true`.
var modifiedMetaForArchive = _.extend({}, query.meta || {}, {
fetch: true,
});
var modifiedCriteriaForArchive = _.omit(query.criteria, ['select', 'omit', 'limit', 'skip', 'sort']);
WLModel.archive(modifiedCriteriaForArchive, function _afterArchiving(err, affectedRecords) {
if (err) {
return done(err);
}
// Note that we always get `affectedRecords` here because "fetch" is enabled.
return done(undefined, affectedRecords[0]);
}, modifiedMetaForArchive);//_∏_ </.archive()>
}, query.meta);//_∏_ </.count()>
},
explicitCbMaybe,
_.extend(DEFERRED_METHODS, {
// Provide access to this model for use in query modifier methods.
_WLModel: WLModel,
// Set up initial query metadata.
_wlQueryInfo: query,
})
);//</parley>
};