foundationdb/bindings/nodejs/tests/util.js

277 lines
6.6 KiB
JavaScript

/*
* util.js
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
"use strict";
var fdb = require('../lib/fdb.js').apiVersion(parseInt(process.argv[3]));
var util = require('../lib/fdbUtil.js');
function Stack() {
this.stack = [];
}
Stack.prototype.length = function() {
return this.stack.length;
};
Stack.prototype.get = function(index) {
return this.stack[index];
};
Stack.prototype.set = function(index, val) {
this.stack[index] = val;
};
Stack.prototype.push = function(instructionIndex, item, isFuture) {
if(typeof isFuture === 'undefined')
isFuture = false;
this.pushEntry({ instructionIndex: instructionIndex, item: item, isFuture: isFuture });
};
Stack.prototype.pushEntry = function(entry) {
this.stack.push(entry);
};
Stack.prototype.pop = function(options, callback) {
var self = this;
return fdb.future.create(function(futureCb) {
if(typeof options === 'undefined')
options = {};
var count = options.count;
if(typeof count === 'undefined')
count = 1;
var params = self.stack.slice(self.stack.length-count).reverse();
self.stack = self.stack.slice(0, self.stack.length-count);
var index = 0;
var itemCallback = function(err, val) {
if(err) {
//console.log(err);
params[index].item = fdb.tuple.pack([fdb.buffer('ERROR'), fdb.buffer(err.code.toString())]);
}
else if(val)
params[index].item = val;
else
params[index].item = fdb.buffer('RESULT_NOT_PRESENT');
params[index].isFuture = false;
if(!options.withMetadata)
params[index] = params[index].item;
index++;
processNext();
};
var processNext = function() {
while(true) {
if(index >= params.length) {
if(typeof options.count === 'undefined')
futureCb(undefined, params[0]);
else
futureCb(undefined, params);
return;
}
if(params[index].isFuture) {
params[index].item(itemCallback);
return;
}
if(!options.withMetadata)
params[index] = params[index].item;
index++;
}
};
processNext();
})(callback);
};
Stack.prototype.popEntry = function() {
return this.stack.pop();
};
function Context(db, prefix, processInstruction, directoryExtension) {
var range = fdb.tuple.range([fdb.buffer(prefix)]);
this.prefix = prefix;
this.stack = new Stack();
this.db = db;
this.next = range.begin;
this.end = range.end;
this.processInstruction = processInstruction;
this.instructionIndex = -1;
this.directoryExtension = directoryExtension;
this.trName = prefix;
}
Context.trMap = {}
Context.prototype.newTransaction = function() {
Context.trMap[this.trName] = this.db.createTransaction();
};
Context.prototype.switchTransaction = function(name) {
this.trName = name;
if(typeof Context.trMap[this.trName] === 'undefined') {
this.newTransaction();
}
};
Context.prototype.updateResults = function(results) {
this.ops = results;
this.current = 0;
this.next = fdb.KeySelector.firstGreaterThan(results[results.length-1].key);
};
var issueInstruction = function(context, cb) {
try {
var tokens = fdb.tuple.unpack(context.ops[context.current].value);
var op = tokens[0].toString();
var snapshotStr = '_SNAPSHOT';
var databaseStr = '_DATABASE';
var isSnapshot = endsWith(op, snapshotStr);
var isDatabase = endsWith(op, databaseStr);
var tr = Context.trMap[context.trName];
if(isSnapshot) {
op = op.substr(0, op.length - snapshotStr.length);
tr = tr.snapshot;
}
else if(isDatabase) {
op = op.substr(0, op.length - databaseStr.length);
tr = context.db;
}
var inst = new Instruction(context, tr, op, tokens, isDatabase, isSnapshot);
context.processInstruction(context, inst, cb);
}
catch(e) {
cb(e);
}
};
Context.prototype.run = function(cb) {
var self = this;
function getInstructions(instCb) {
self.db.doTransaction(function(tr, trCb) {
tr.getRange(self.next, self.end, { limit: 1000 } ).toArray(function(rangeErr, rangeRes) {
if(rangeErr) return trCb(rangeErr);
trCb(undefined, rangeRes);
});
}, function(err, rangeRes) {
if(err) return instCb(err);
if(rangeRes.length > 0)
self.updateResults(rangeRes);
instCb();
});
}
function readAndExecuteInstructions(loopCb) {
++self.instructionIndex;
if(!self.ops || ++self.current === self.ops.length) {
getInstructions(function(err) {
if(err) return loopCb(err);
if(self.current < self.ops.length)
issueInstruction(self, loopCb);
else
loopCb(undefined, null); // terminate the loop
});
}
else
issueInstruction(self, loopCb);
}
util.whileLoop(readAndExecuteInstructions, function(err) {
if(err) {
if(self.ops && self.current < self.ops.length)
console.error('ERROR during operation \'' + self.ops[self.current].value.toString() + '\':');
else
console.error('ERROR getting operations:');
if(err.stack)
console.error(err.stack);
else
console.error(err);
}
cb(err);
});
};
function Instruction(context, tr, op, tokens, isDatabase, isSnapshot) {
this.context = context;
this.tr = tr;
this.op = op;
this.tokens = tokens;
this.isDatabase = isDatabase;
this.isSnapshot = isSnapshot;
}
Instruction.prototype.pop = function(options, callback) {
return this.context.stack.pop(options, callback);
};
Instruction.prototype.push = function(item, isFuture) {
this.context.stack.push(this.context.instructionIndex, item, isFuture);
};
function toJavaScriptName(name) {
name = name.toString().toLowerCase();
var start = 0;
while(start < name.length) {
start = name.indexOf('_', start);
if(start === -1)
break;
name = name.slice(0, start) + name[start+1].toUpperCase() + name.slice(start+2);
}
return name.replace(/_/g, '');
}
function startsWith(str, prefixStr) {
return str.length >= prefixStr.length && str.substr(0, prefixStr.length) === prefixStr;
}
function endsWith(str, endStr) {
return str.length >= endStr.length && str.substr(str.length - endStr.length) === endStr;
}
module.exports = {
Stack: Stack,
Context: Context,
Instruction: Instruction,
toJavaScriptName: toJavaScriptName,
startsWith: startsWith,
endsWith: endsWith
};