Compare commits
No commits in common. "d58ea1863a8a88739b0b1053f2dffddadbf7b793" and "7c1bd453ee6b167b21777222afeda17b51c4b0c8" have entirely different histories.
d58ea1863a
...
7c1bd453ee
|
@ -2,41 +2,21 @@
|
||||||
|
|
||||||
const Extension = require('./Extension').default;
|
const Extension = require('./Extension').default;
|
||||||
const { inc, listingParamsMasterKeysV0ToV1,
|
const { inc, listingParamsMasterKeysV0ToV1,
|
||||||
FILTER_END, FILTER_ACCEPT, FILTER_SKIP, SKIP_NONE } = require('./tools');
|
FILTER_END, FILTER_ACCEPT, FILTER_SKIP } = require('./tools');
|
||||||
const VSConst = require('../../versioning/constants').VersioningConstants;
|
const VSConst = require('../../versioning/constants').VersioningConstants;
|
||||||
const { DbPrefixes, BucketVersioningKeyFormat } = VSConst;
|
const { DbPrefixes, BucketVersioningKeyFormat } = VSConst;
|
||||||
|
|
||||||
export interface FilterState {
|
/**
|
||||||
id: number,
|
* Find the common prefix in the path
|
||||||
};
|
*
|
||||||
|
* @param {String} key - path of the object
|
||||||
export const enum DelimiterFilterStateId {
|
* @param {String} delimiter - separator
|
||||||
NotSkipping = 1,
|
* @param {Number} delimiterIndex - 'folder' index in the path
|
||||||
SkippingPrefix = 2,
|
* @return {String} - CommonPrefix
|
||||||
};
|
*/
|
||||||
|
function getCommonPrefix(key, delimiter, delimiterIndex) {
|
||||||
export interface DelimiterFilterState_NotSkipping extends FilterState {
|
return key.substring(0, delimiterIndex + delimiter.length);
|
||||||
id: DelimiterFilterStateId.NotSkipping,
|
}
|
||||||
};
|
|
||||||
|
|
||||||
export interface DelimiterFilterState_SkippingPrefix extends FilterState {
|
|
||||||
id: DelimiterFilterStateId.SkippingPrefix,
|
|
||||||
prefix: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type KeyHandler = (key: string, value: string) => number;
|
|
||||||
|
|
||||||
type ResultObject = {
|
|
||||||
CommonPrefixes: string[];
|
|
||||||
Contents: {
|
|
||||||
key: string;
|
|
||||||
value: string;
|
|
||||||
}[];
|
|
||||||
IsTruncated: boolean;
|
|
||||||
Delimiter ?: string;
|
|
||||||
NextMarker ?: string;
|
|
||||||
NextContinuationToken ?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle object listing with parameters
|
* Handle object listing with parameters
|
||||||
|
@ -50,11 +30,7 @@ type ResultObject = {
|
||||||
* @prop {String|undefined} prefix - prefix per amazon format
|
* @prop {String|undefined} prefix - prefix per amazon format
|
||||||
* @prop {Number} maxKeys - number of keys to list
|
* @prop {Number} maxKeys - number of keys to list
|
||||||
*/
|
*/
|
||||||
export class Delimiter extends Extension {
|
class Delimiter extends Extension {
|
||||||
|
|
||||||
state: FilterState;
|
|
||||||
keyHandlers: { [id: number]: KeyHandler };
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new Delimiter instance
|
* Create a new Delimiter instance
|
||||||
* @constructor
|
* @constructor
|
||||||
|
@ -72,6 +48,9 @@ export class Delimiter extends Extension {
|
||||||
* format
|
* format
|
||||||
* @param {String} [parameters.continuationToken] - obfuscated amazon
|
* @param {String} [parameters.continuationToken] - obfuscated amazon
|
||||||
* token
|
* token
|
||||||
|
* @param {Boolean} [parameters.alphabeticalOrder] - Either the result is
|
||||||
|
* alphabetically ordered
|
||||||
|
* or not
|
||||||
* @param {RequestLogger} logger - The logger of the
|
* @param {RequestLogger} logger - The logger of the
|
||||||
* request
|
* request
|
||||||
* @param {String} [vFormat] - versioning key format
|
* @param {String} [vFormat] - versioning key format
|
||||||
|
@ -81,21 +60,38 @@ export class Delimiter extends Extension {
|
||||||
// original listing parameters
|
// original listing parameters
|
||||||
this.delimiter = parameters.delimiter;
|
this.delimiter = parameters.delimiter;
|
||||||
this.prefix = parameters.prefix;
|
this.prefix = parameters.prefix;
|
||||||
|
this.marker = parameters.marker;
|
||||||
this.maxKeys = parameters.maxKeys || 1000;
|
this.maxKeys = parameters.maxKeys || 1000;
|
||||||
|
this.startAfter = parameters.startAfter;
|
||||||
if (parameters.v2) {
|
this.continuationToken = parameters.continuationToken;
|
||||||
this.marker = parameters.continuationToken || parameters.startAfter;
|
this.alphabeticalOrder =
|
||||||
} else {
|
typeof parameters.alphabeticalOrder !== 'undefined' ?
|
||||||
this.marker = parameters.marker;
|
parameters.alphabeticalOrder : true;
|
||||||
}
|
|
||||||
this.nextMarker = this.marker;
|
|
||||||
|
|
||||||
this.vFormat = vFormat || BucketVersioningKeyFormat.v0;
|
this.vFormat = vFormat || BucketVersioningKeyFormat.v0;
|
||||||
// results
|
// results
|
||||||
this.CommonPrefixes = [];
|
this.CommonPrefixes = [];
|
||||||
this.Contents = [];
|
this.Contents = [];
|
||||||
this.IsTruncated = false;
|
this.IsTruncated = false;
|
||||||
this.keyHandlers = {};
|
this.NextMarker = parameters.marker;
|
||||||
|
this.NextContinuationToken =
|
||||||
|
parameters.continuationToken || parameters.startAfter;
|
||||||
|
|
||||||
|
this.startMarker = parameters.v2 ? 'startAfter' : 'marker';
|
||||||
|
this.continueMarker = parameters.v2 ? 'continuationToken' : 'marker';
|
||||||
|
this.nextContinueMarker = parameters.v2 ?
|
||||||
|
'NextContinuationToken' : 'NextMarker';
|
||||||
|
|
||||||
|
if (this.delimiter !== undefined &&
|
||||||
|
this[this.nextContinueMarker] !== undefined &&
|
||||||
|
this[this.nextContinueMarker].startsWith(this.prefix || '')) {
|
||||||
|
const nextDelimiterIndex =
|
||||||
|
this[this.nextContinueMarker].indexOf(this.delimiter,
|
||||||
|
this.prefix ? this.prefix.length : 0);
|
||||||
|
this[this.nextContinueMarker] =
|
||||||
|
this[this.nextContinueMarker].slice(0, nextDelimiterIndex +
|
||||||
|
this.delimiter.length);
|
||||||
|
}
|
||||||
|
|
||||||
Object.assign(this, {
|
Object.assign(this, {
|
||||||
[BucketVersioningKeyFormat.v0]: {
|
[BucketVersioningKeyFormat.v0]: {
|
||||||
|
@ -109,49 +105,21 @@ export class Delimiter extends Extension {
|
||||||
skipping: this.skippingV1,
|
skipping: this.skippingV1,
|
||||||
},
|
},
|
||||||
}[this.vFormat]);
|
}[this.vFormat]);
|
||||||
|
|
||||||
// if there is a delimiter, we may skip ranges by prefix,
|
|
||||||
// hence using the NotSkippingPrefix flavor that checks the
|
|
||||||
// subprefix up to the delimiter for the NotSkipping state
|
|
||||||
if (this.delimiter) {
|
|
||||||
this.setKeyHandler(
|
|
||||||
DelimiterFilterStateId.NotSkipping,
|
|
||||||
this.keyHandler_NotSkippingPrefix.bind(this));
|
|
||||||
|
|
||||||
this.setKeyHandler(
|
|
||||||
DelimiterFilterStateId.SkippingPrefix,
|
|
||||||
this.keyHandler_SkippingPrefix.bind(this));
|
|
||||||
} else {
|
|
||||||
// listing without a delimiter never has to skip over any
|
|
||||||
// prefix -> use NeverSkipping flavor for the NotSkipping
|
|
||||||
// state
|
|
||||||
this.setKeyHandler(
|
|
||||||
DelimiterFilterStateId.NotSkipping,
|
|
||||||
this.keyHandler_NeverSkipping.bind(this));
|
|
||||||
}
|
|
||||||
this.state = <DelimiterFilterState_NotSkipping> {
|
|
||||||
id: DelimiterFilterStateId.NotSkipping,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
genMDParamsV0() {
|
genMDParamsV0() {
|
||||||
const params: { gt ?: string, gte ?: string, lt ?: string } = {};
|
const params = {};
|
||||||
if (this.prefix) {
|
if (this.prefix) {
|
||||||
params.gte = this.prefix;
|
params.gte = this.prefix;
|
||||||
params.lt = inc(this.prefix);
|
params.lt = inc(this.prefix);
|
||||||
}
|
}
|
||||||
if (this.marker && this.delimiter) {
|
const startVal = this[this.continueMarker] || this[this.startMarker];
|
||||||
const commonPrefix = this.getCommonPrefix(this.marker);
|
if (startVal) {
|
||||||
if (commonPrefix) {
|
if (params.gte && params.gte > startVal) {
|
||||||
const afterPrefix = inc(commonPrefix);
|
return params;
|
||||||
if (!params.gte || afterPrefix > params.gte) {
|
|
||||||
params.gte = afterPrefix;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (this.marker && (!params.gte || this.marker >= params.gte)) {
|
|
||||||
delete params.gte;
|
delete params.gte;
|
||||||
params.gt = this.marker;
|
params.gt = startVal;
|
||||||
}
|
}
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
@ -183,62 +151,21 @@ export class Delimiter extends Extension {
|
||||||
* @param {String} value - The value of the key
|
* @param {String} value - The value of the key
|
||||||
* @return {number} - indicates if iteration should continue
|
* @return {number} - indicates if iteration should continue
|
||||||
*/
|
*/
|
||||||
addContents(key: string, value: string): number {
|
addContents(key, value) {
|
||||||
if (this._reachedMaxKeys()) {
|
if (this._reachedMaxKeys()) {
|
||||||
return FILTER_END;
|
return FILTER_END;
|
||||||
}
|
}
|
||||||
this.Contents.push({ key, value: this.trimMetadata(value) });
|
this.Contents.push({ key, value: this.trimMetadata(value) });
|
||||||
|
this[this.nextContinueMarker] = key;
|
||||||
++this.keys;
|
++this.keys;
|
||||||
this.nextMarker = key;
|
|
||||||
return FILTER_ACCEPT;
|
return FILTER_ACCEPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCommonPrefix(key: string): string | undefined {
|
getObjectKeyV0(obj) {
|
||||||
const baseIndex = this.prefix ? this.prefix.length : 0;
|
|
||||||
const delimiterIndex = key.indexOf(this.delimiter, baseIndex);
|
|
||||||
if (delimiterIndex === -1) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return key.substring(0, delimiterIndex + this.delimiter.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a Common Prefix in the list
|
|
||||||
* @param {String} commonPrefix - common prefix to add
|
|
||||||
* @param {String} key - full key starting with commonPrefix
|
|
||||||
* @return {Boolean} - indicates if iteration should continue
|
|
||||||
*/
|
|
||||||
addCommonPrefix(commonPrefix: string, key: string): number {
|
|
||||||
if (this._reachedMaxKeys()) {
|
|
||||||
return FILTER_END;
|
|
||||||
}
|
|
||||||
// add the new prefix to the list
|
|
||||||
this.CommonPrefixes.push(commonPrefix);
|
|
||||||
++this.keys;
|
|
||||||
this.nextMarker = key;
|
|
||||||
// transition into SkippingPrefix state to skip all following keys
|
|
||||||
// while they start with the same prefix
|
|
||||||
this.setState(<DelimiterFilterState_SkippingPrefix> {
|
|
||||||
id: DelimiterFilterStateId.SkippingPrefix,
|
|
||||||
prefix: commonPrefix,
|
|
||||||
});
|
|
||||||
return FILTER_ACCEPT;
|
|
||||||
}
|
|
||||||
|
|
||||||
addCommonPrefixOrContents(key: string, value: string): number {
|
|
||||||
// add the subprefix to the common prefixes if the key has the delimiter
|
|
||||||
const commonPrefix = this.getCommonPrefix(key);
|
|
||||||
if (commonPrefix) {
|
|
||||||
return this.addCommonPrefix(commonPrefix, key);
|
|
||||||
}
|
|
||||||
return this.addContents(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
getObjectKeyV0(obj: { key: string }): string {
|
|
||||||
return obj.key;
|
return obj.key;
|
||||||
}
|
}
|
||||||
|
|
||||||
getObjectKeyV1(obj: { key: string }): string {
|
getObjectKeyV1(obj) {
|
||||||
return obj.key.slice(DbPrefixes.Master.length);
|
return obj.key.slice(DbPrefixes.Master.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,65 +180,67 @@ export class Delimiter extends Extension {
|
||||||
* @param {String} obj.value - The value of the element
|
* @param {String} obj.value - The value of the element
|
||||||
* @return {number} - indicates if iteration should continue
|
* @return {number} - indicates if iteration should continue
|
||||||
*/
|
*/
|
||||||
filter(obj: { key: string, value: string }): number {
|
filter(obj) {
|
||||||
const key = this.getObjectKey(obj);
|
const key = this.getObjectKey(obj);
|
||||||
const value = obj.value;
|
const value = obj.value;
|
||||||
|
if ((this.prefix && !key.startsWith(this.prefix))
|
||||||
return this.handleKey(key, value);
|
|| (this.alphabeticalOrder
|
||||||
}
|
&& typeof this[this.nextContinueMarker] === 'string'
|
||||||
|
&& key <= this[this.nextContinueMarker])) {
|
||||||
setState(state: FilterState): void {
|
return FILTER_SKIP;
|
||||||
this.state = state;
|
}
|
||||||
}
|
if (this.delimiter) {
|
||||||
|
const baseIndex = this.prefix ? this.prefix.length : 0;
|
||||||
setKeyHandler(stateId: number, keyHandler: KeyHandler): void {
|
const delimiterIndex = key.indexOf(this.delimiter, baseIndex);
|
||||||
this.keyHandlers[stateId] = keyHandler;
|
if (delimiterIndex === -1) {
|
||||||
}
|
return this.addContents(key, value);
|
||||||
|
}
|
||||||
handleKey(key: string, value: string): number {
|
return this.addCommonPrefix(key, delimiterIndex);
|
||||||
return this.keyHandlers[this.state.id](key, value);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
keyHandler_NeverSkipping(key, value) {
|
|
||||||
return this.addContents(key, value);
|
return this.addContents(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
keyHandler_NotSkippingPrefix(key, value) {
|
/**
|
||||||
return this.addCommonPrefixOrContents(key, value);
|
* Add a Common Prefix in the list
|
||||||
}
|
* @param {String} key - object name
|
||||||
|
* @param {Number} index - after prefix starting point
|
||||||
keyHandler_SkippingPrefix(key, value) {
|
* @return {Boolean} - indicates if iteration should continue
|
||||||
const { prefix } = <DelimiterFilterState_SkippingPrefix> this.state;
|
*/
|
||||||
if (key.startsWith(prefix)) {
|
addCommonPrefix(key, index) {
|
||||||
return FILTER_SKIP;
|
const commonPrefix = getCommonPrefix(key, this.delimiter, index);
|
||||||
}
|
if (this.CommonPrefixes.indexOf(commonPrefix) === -1
|
||||||
this.setState(<DelimiterFilterState_NotSkipping> {
|
&& this[this.nextContinueMarker] !== commonPrefix) {
|
||||||
id: DelimiterFilterStateId.NotSkipping,
|
if (this._reachedMaxKeys()) {
|
||||||
});
|
return FILTER_END;
|
||||||
return this.handleKey(key, value);
|
}
|
||||||
}
|
this.CommonPrefixes.push(commonPrefix);
|
||||||
|
this[this.nextContinueMarker] = commonPrefix;
|
||||||
skippingBase(): string | undefined {
|
++this.keys;
|
||||||
switch (this.state.id) {
|
return FILTER_ACCEPT;
|
||||||
case DelimiterFilterStateId.SkippingPrefix:
|
|
||||||
const { prefix } = <DelimiterFilterState_SkippingPrefix> this.state;
|
|
||||||
return prefix;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return SKIP_NONE;
|
|
||||||
}
|
}
|
||||||
|
return FILTER_SKIP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If repd happens to want to skip listing on a bucket in v0
|
||||||
|
* versioning key format, here is an idea.
|
||||||
|
*
|
||||||
|
* @return {string} - the present range (NextMarker) if repd believes
|
||||||
|
* that it's enough and should move on
|
||||||
|
*/
|
||||||
skippingV0() {
|
skippingV0() {
|
||||||
return this.skippingBase();
|
return this[this.nextContinueMarker];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If repd happens to want to skip listing on a bucket in v1
|
||||||
|
* versioning key format, here is an idea.
|
||||||
|
*
|
||||||
|
* @return {string} - the present range (NextMarker) if repd believes
|
||||||
|
* that it's enough and should move on
|
||||||
|
*/
|
||||||
skippingV1() {
|
skippingV1() {
|
||||||
const skipTo = this.skippingBase();
|
return DbPrefixes.Master + this[this.nextContinueMarker];
|
||||||
if (skipTo === SKIP_NONE) {
|
|
||||||
return SKIP_NONE;
|
|
||||||
}
|
|
||||||
return DbPrefixes.Master + skipTo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -320,12 +249,12 @@ export class Delimiter extends Extension {
|
||||||
* isn't truncated
|
* isn't truncated
|
||||||
* @return {Object} - following amazon format
|
* @return {Object} - following amazon format
|
||||||
*/
|
*/
|
||||||
result(): ResultObject {
|
result() {
|
||||||
/* NextMarker is only provided when delimiter is used.
|
/* NextMarker is only provided when delimiter is used.
|
||||||
* specified in v1 listing documentation
|
* specified in v1 listing documentation
|
||||||
* http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGET.html
|
* http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGET.html
|
||||||
*/
|
*/
|
||||||
const result: ResultObject = {
|
const result = {
|
||||||
CommonPrefixes: this.CommonPrefixes,
|
CommonPrefixes: this.CommonPrefixes,
|
||||||
Contents: this.Contents,
|
Contents: this.Contents,
|
||||||
IsTruncated: this.IsTruncated,
|
IsTruncated: this.IsTruncated,
|
||||||
|
@ -333,11 +262,13 @@ export class Delimiter extends Extension {
|
||||||
};
|
};
|
||||||
if (this.parameters.v2) {
|
if (this.parameters.v2) {
|
||||||
result.NextContinuationToken = this.IsTruncated
|
result.NextContinuationToken = this.IsTruncated
|
||||||
? this.nextMarker : undefined;
|
? this.NextContinuationToken : undefined;
|
||||||
} else {
|
} else {
|
||||||
result.NextMarker = (this.IsTruncated && this.delimiter)
|
result.NextMarker = (this.IsTruncated && this.delimiter)
|
||||||
? this.nextMarker : undefined;
|
? this.NextMarker : undefined;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = { Delimiter, getCommonPrefix };
|
|
@ -0,0 +1,230 @@
|
||||||
|
'use strict'; // eslint-disable-line strict
|
||||||
|
|
||||||
|
const Delimiter = require('./delimiter').Delimiter;
|
||||||
|
const Version = require('../../versioning/Version').Version;
|
||||||
|
const VSConst = require('../../versioning/constants').VersioningConstants;
|
||||||
|
const { BucketVersioningKeyFormat } = VSConst;
|
||||||
|
const { FILTER_ACCEPT, FILTER_SKIP, SKIP_NONE } = require('./tools');
|
||||||
|
|
||||||
|
const VID_SEP = VSConst.VersionId.Separator;
|
||||||
|
const { DbPrefixes } = VSConst;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle object listing with parameters. This extends the base class Delimiter
|
||||||
|
* to return the raw master versions of existing objects.
|
||||||
|
*/
|
||||||
|
class DelimiterMaster extends Delimiter {
|
||||||
|
/**
|
||||||
|
* Delimiter listing of master versions.
|
||||||
|
* @param {Object} parameters - listing parameters
|
||||||
|
* @param {String} parameters.delimiter - delimiter per amazon format
|
||||||
|
* @param {String} parameters.prefix - prefix per amazon format
|
||||||
|
* @param {String} parameters.marker - marker per amazon format
|
||||||
|
* @param {Number} parameters.maxKeys - number of keys to list
|
||||||
|
* @param {Boolean} parameters.v2 - indicates whether v2 format
|
||||||
|
* @param {String} parameters.startAfter - marker per amazon v2 format
|
||||||
|
* @param {String} parameters.continuationToken - obfuscated amazon token
|
||||||
|
* @param {RequestLogger} logger - The logger of the request
|
||||||
|
* @param {String} [vFormat] - versioning key format
|
||||||
|
*/
|
||||||
|
constructor(parameters, logger, vFormat) {
|
||||||
|
super(parameters, logger, vFormat);
|
||||||
|
// non-PHD master version or a version whose master is a PHD version
|
||||||
|
this.prvKey = undefined;
|
||||||
|
this.prvPHDKey = undefined;
|
||||||
|
this.inReplayPrefix = false;
|
||||||
|
this.prefixKeySeen = false;
|
||||||
|
this.prefixEndsWithDelim = this.prefix && this.prefix.endsWith(this.delimiter);
|
||||||
|
|
||||||
|
Object.assign(this, {
|
||||||
|
[BucketVersioningKeyFormat.v0]: {
|
||||||
|
filter: this.filterV0,
|
||||||
|
skipping: this.skippingV0,
|
||||||
|
},
|
||||||
|
[BucketVersioningKeyFormat.v1]: {
|
||||||
|
filter: this.filterV1,
|
||||||
|
skipping: this.skippingV1,
|
||||||
|
},
|
||||||
|
}[this.vFormat]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter to apply on each iteration for buckets in v0 format,
|
||||||
|
* based on:
|
||||||
|
* - prefix
|
||||||
|
* - delimiter
|
||||||
|
* - maxKeys
|
||||||
|
* The marker is being handled directly by levelDB
|
||||||
|
* @param {Object} obj - The key and value of the element
|
||||||
|
* @param {String} obj.key - The key of the element
|
||||||
|
* @param {String} obj.value - The value of the element
|
||||||
|
* @return {number} - indicates if iteration should continue
|
||||||
|
*/
|
||||||
|
filterV0(obj) {
|
||||||
|
let key = obj.key;
|
||||||
|
const value = obj.value;
|
||||||
|
|
||||||
|
if (key === this.prefix) {
|
||||||
|
this.prefixKeySeen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.startsWith(DbPrefixes.Replay)) {
|
||||||
|
this.inReplayPrefix = true;
|
||||||
|
return FILTER_SKIP;
|
||||||
|
}
|
||||||
|
this.inReplayPrefix = false;
|
||||||
|
|
||||||
|
/* Skip keys not starting with the prefix or not alphabetically
|
||||||
|
* ordered. */
|
||||||
|
if ((this.prefix && !key.startsWith(this.prefix))
|
||||||
|
|| (typeof this[this.nextContinueMarker] === 'string' &&
|
||||||
|
key <= this[this.nextContinueMarker])) {
|
||||||
|
return FILTER_SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Skip version keys (<key><versionIdSeparator><version>) if we already
|
||||||
|
* have a master version. */
|
||||||
|
const versionIdIndex = key.indexOf(VID_SEP);
|
||||||
|
if (versionIdIndex >= 0) {
|
||||||
|
key = key.slice(0, versionIdIndex);
|
||||||
|
/* - key === this.prvKey is triggered when a master version has
|
||||||
|
* been accepted for this key,
|
||||||
|
* - key === this.NextMarker or this.NextContinueToken is triggered
|
||||||
|
* when a listing page ends on an accepted obj and the next page
|
||||||
|
* starts with a version of this object.
|
||||||
|
* In that case prvKey is default set to undefined
|
||||||
|
* in the constructor and comparing to NextMarker is the only
|
||||||
|
* way to know we should not accept this version. This test is
|
||||||
|
* not redundant with the one at the beginning of this function,
|
||||||
|
* we are comparing here the key without the version suffix,
|
||||||
|
* - key startsWith the previous NextMarker happens because we set
|
||||||
|
* NextMarker to the common prefix instead of the whole key
|
||||||
|
* value. (TODO: remove this test once ZENKO-1048 is fixed)
|
||||||
|
* */
|
||||||
|
if (key === this.prvKey || key === this[this.nextContinueMarker]
|
||||||
|
|| (this.delimiter && key.startsWith(this[this.nextContinueMarker]))) {
|
||||||
|
/* master version already filtered */
|
||||||
|
return FILTER_SKIP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Version.isPHD(value)) {
|
||||||
|
/* master version is a PHD version, we want to wait for the next
|
||||||
|
* one:
|
||||||
|
* - Set the prvKey to undefined to not skip the next version,
|
||||||
|
* - return accept to avoid users to skip the next values in range
|
||||||
|
* (skip scan mechanism in metadata backend like Metadata or
|
||||||
|
* MongoClient). */
|
||||||
|
this.prvKey = undefined;
|
||||||
|
this.prvPHDKey = key;
|
||||||
|
return FILTER_ACCEPT;
|
||||||
|
}
|
||||||
|
if (Version.isDeleteMarker(value)) {
|
||||||
|
/* This entry is a deleteMarker which has not been filtered by the
|
||||||
|
* version test. Either :
|
||||||
|
* - it is a deleteMarker on the master version, we want to SKIP
|
||||||
|
* all the following entries with this key (no master version),
|
||||||
|
* - or a deleteMarker following a PHD (setting prvKey to undefined
|
||||||
|
* when an entry is a PHD avoids the skip on version for the
|
||||||
|
* next entry). In that case we expect the master version to
|
||||||
|
* follow. */
|
||||||
|
if (key === this.prvPHDKey) {
|
||||||
|
this.prvKey = undefined;
|
||||||
|
return FILTER_ACCEPT;
|
||||||
|
}
|
||||||
|
this.prvKey = key;
|
||||||
|
if (this.prefixEndsWithDelim) {
|
||||||
|
/* When the prefix ends with a delimiter, update nextContinueMarker
|
||||||
|
* to be able to skip ranges of the form prefix/subprefix/ as an optimization.
|
||||||
|
* The marker may also end up being prefix/, in which case .skipping will determine
|
||||||
|
* if a skip over the full range is allowed or a smaller skipping range of prefix/{VID_SEP}
|
||||||
|
* must be used. */
|
||||||
|
this[this.nextContinueMarker] = key.slice(0, key.lastIndexOf(this.delimiter) + 1);
|
||||||
|
}
|
||||||
|
return FILTER_SKIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.prvKey = key;
|
||||||
|
if (this.delimiter) {
|
||||||
|
// check if the key has the delimiter
|
||||||
|
const baseIndex = this.prefix ? this.prefix.length : 0;
|
||||||
|
const delimiterIndex = key.indexOf(this.delimiter, baseIndex);
|
||||||
|
if (delimiterIndex >= 0) {
|
||||||
|
// try to add the prefix to the list
|
||||||
|
return this.addCommonPrefix(key, delimiterIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.addContents(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter to apply on each iteration for buckets in v1 format,
|
||||||
|
* based on:
|
||||||
|
* - prefix
|
||||||
|
* - delimiter
|
||||||
|
* - maxKeys
|
||||||
|
* The marker is being handled directly by levelDB
|
||||||
|
* @param {Object} obj - The key and value of the element
|
||||||
|
* @param {String} obj.key - The key of the element
|
||||||
|
* @param {String} obj.value - The value of the element
|
||||||
|
* @return {number} - indicates if iteration should continue
|
||||||
|
*/
|
||||||
|
filterV1(obj) {
|
||||||
|
// Filtering master keys in v1 is simply listing the master
|
||||||
|
// keys, as the state of version keys do not change the
|
||||||
|
// result, so we can use Delimiter method directly.
|
||||||
|
return super.filter(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if a nextContinueMarker ending with a delimiter
|
||||||
|
* can be skipped.
|
||||||
|
* @returns {bool}
|
||||||
|
*/
|
||||||
|
|
||||||
|
allowDelimiterRangeSkip() {
|
||||||
|
if (!this.prefixKeySeen) {
|
||||||
|
// A prefix key is a master key equal to the prefix. If it has
|
||||||
|
// not been encountered, can skip.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const marker = this[this.nextContinueMarker];
|
||||||
|
// prefix = prefix, key = prefix/. Can skip since key will be part of commonPrefixes.
|
||||||
|
if (marker.length > this.prefix.length && marker.startsWith(this.prefix)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const lastIdx = marker.lastIndexOf(this.delimiter); // prefix/foo is a masterKey following a prefix key.
|
||||||
|
return marker.slice(0, lastIdx + 1) !== this.prefix; // cannot skip the full range prefix/ range.
|
||||||
|
}
|
||||||
|
|
||||||
|
skippingBase() {
|
||||||
|
if (this[this.nextContinueMarker]) {
|
||||||
|
// next marker or next continuation token:
|
||||||
|
// - foo/ : skipping foo/
|
||||||
|
// - foo : skipping foo.
|
||||||
|
const index = this[this.nextContinueMarker].
|
||||||
|
lastIndexOf(this.delimiter);
|
||||||
|
if (index === this[this.nextContinueMarker].length - 1 && this.allowDelimiterRangeSkip()) {
|
||||||
|
return this[this.nextContinueMarker];
|
||||||
|
}
|
||||||
|
return this[this.nextContinueMarker] + VID_SEP;
|
||||||
|
}
|
||||||
|
return SKIP_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
skippingV0() {
|
||||||
|
if (this.inReplayPrefix) {
|
||||||
|
return DbPrefixes.Replay;
|
||||||
|
}
|
||||||
|
return this.skippingBase();
|
||||||
|
}
|
||||||
|
|
||||||
|
skippingV1() {
|
||||||
|
const skipTo = this.skippingBase();
|
||||||
|
if (skipTo === SKIP_NONE) {
|
||||||
|
return SKIP_NONE;
|
||||||
|
}
|
||||||
|
return DbPrefixes.Master + skipTo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { DelimiterMaster };
|
|
@ -1,171 +0,0 @@
|
||||||
import {
|
|
||||||
Delimiter,
|
|
||||||
FilterState,
|
|
||||||
DelimiterFilterStateId,
|
|
||||||
DelimiterFilterState_NotSkipping,
|
|
||||||
DelimiterFilterState_SkippingPrefix,
|
|
||||||
} from './delimiter';
|
|
||||||
const Version = require('../../versioning/Version').Version;
|
|
||||||
const VSConst = require('../../versioning/constants').VersioningConstants;
|
|
||||||
const { BucketVersioningKeyFormat } = VSConst;
|
|
||||||
const { FILTER_ACCEPT, FILTER_SKIP } = require('./tools');
|
|
||||||
|
|
||||||
const VID_SEP = VSConst.VersionId.Separator;
|
|
||||||
const { DbPrefixes } = VSConst;
|
|
||||||
|
|
||||||
const enum DelimiterMasterFilterStateId {
|
|
||||||
SkippingVersionsV0 = 101,
|
|
||||||
WaitVersionAfterPHDV0 = 102,
|
|
||||||
};
|
|
||||||
|
|
||||||
interface DelimiterMasterFilterState_SkippingVersionsV0 extends FilterState {
|
|
||||||
id: DelimiterMasterFilterStateId.SkippingVersionsV0,
|
|
||||||
masterKey: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
interface DelimiterMasterFilterState_WaitVersionAfterPHDV0 extends FilterState {
|
|
||||||
id: DelimiterMasterFilterStateId.WaitVersionAfterPHDV0,
|
|
||||||
masterKey: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle object listing with parameters. This extends the base class Delimiter
|
|
||||||
* to return the raw master versions of existing objects.
|
|
||||||
*/
|
|
||||||
export class DelimiterMaster extends Delimiter {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delimiter listing of master versions.
|
|
||||||
* @param {Object} parameters - listing parameters
|
|
||||||
* @param {String} parameters.delimiter - delimiter per amazon format
|
|
||||||
* @param {String} parameters.prefix - prefix per amazon format
|
|
||||||
* @param {String} parameters.marker - marker per amazon format
|
|
||||||
* @param {Number} parameters.maxKeys - number of keys to list
|
|
||||||
* @param {Boolean} parameters.v2 - indicates whether v2 format
|
|
||||||
* @param {String} parameters.startAfter - marker per amazon v2 format
|
|
||||||
* @param {String} parameters.continuationToken - obfuscated amazon token
|
|
||||||
* @param {RequestLogger} logger - The logger of the request
|
|
||||||
* @param {String} [vFormat] - versioning key format
|
|
||||||
*/
|
|
||||||
constructor(parameters, logger, vFormat) {
|
|
||||||
super(parameters, logger, vFormat);
|
|
||||||
|
|
||||||
Object.assign(this, {
|
|
||||||
[BucketVersioningKeyFormat.v0]: {
|
|
||||||
skipping: this.skippingV0,
|
|
||||||
},
|
|
||||||
[BucketVersioningKeyFormat.v1]: {
|
|
||||||
skipping: this.skippingV1,
|
|
||||||
},
|
|
||||||
}[this.vFormat]);
|
|
||||||
|
|
||||||
if (vFormat === BucketVersioningKeyFormat.v0) {
|
|
||||||
// override Delimiter's implementation of NotSkipping for
|
|
||||||
// DelimiterMaster logic (skipping versions and special
|
|
||||||
// handling of delete markers and PHDs)
|
|
||||||
this.setKeyHandler(
|
|
||||||
DelimiterFilterStateId.NotSkipping,
|
|
||||||
this.keyHandler_NotSkippingPrefixNorVersionsV0.bind(this));
|
|
||||||
|
|
||||||
// add extra state handlers specific to DelimiterMaster with v0 format
|
|
||||||
this.setKeyHandler(
|
|
||||||
DelimiterMasterFilterStateId.SkippingVersionsV0,
|
|
||||||
this.keyHandler_SkippingVersionsV0.bind(this));
|
|
||||||
|
|
||||||
this.setKeyHandler(
|
|
||||||
DelimiterMasterFilterStateId.WaitVersionAfterPHDV0,
|
|
||||||
this.keyHandler_WaitVersionAfterPHDV0.bind(this));
|
|
||||||
|
|
||||||
if (this.marker) {
|
|
||||||
// distinct initial state to include some special logic
|
|
||||||
// before the first master key is found that does not have
|
|
||||||
// to be checked afterwards
|
|
||||||
this.state = <DelimiterMasterFilterState_SkippingVersionsV0> {
|
|
||||||
id: DelimiterMasterFilterStateId.SkippingVersionsV0,
|
|
||||||
masterKey: this.marker,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
this.state = <DelimiterFilterState_NotSkipping> {
|
|
||||||
id: DelimiterFilterStateId.NotSkipping,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// in v1, we can directly use Delimiter's implementation,
|
|
||||||
// which is already set to the proper state
|
|
||||||
}
|
|
||||||
|
|
||||||
filter_onNewMasterKeyV0(key, value) {
|
|
||||||
// update the state to start skipping versions of the new master key
|
|
||||||
this.setState(<DelimiterMasterFilterState_SkippingVersionsV0> {
|
|
||||||
id: DelimiterMasterFilterStateId.SkippingVersionsV0,
|
|
||||||
masterKey: key,
|
|
||||||
});
|
|
||||||
// if this master key is a delete marker, accept it without
|
|
||||||
// adding the version to the contents
|
|
||||||
if (Version.isDeleteMarker(value)) {
|
|
||||||
return FILTER_ACCEPT;
|
|
||||||
}
|
|
||||||
if (Version.isPHD(value)) {
|
|
||||||
// master version is a PHD version: wait for the first
|
|
||||||
// following version that will be considered as the actual
|
|
||||||
// master key
|
|
||||||
this.setState(<DelimiterMasterFilterState_WaitVersionAfterPHDV0> {
|
|
||||||
id: DelimiterMasterFilterStateId.WaitVersionAfterPHDV0,
|
|
||||||
masterKey: key,
|
|
||||||
});
|
|
||||||
return FILTER_ACCEPT;
|
|
||||||
}
|
|
||||||
if (key.startsWith(DbPrefixes.Replay)) {
|
|
||||||
// skip internal replay prefix entirely
|
|
||||||
this.setState(<DelimiterFilterState_SkippingPrefix> {
|
|
||||||
id: DelimiterFilterStateId.SkippingPrefix,
|
|
||||||
prefix: DbPrefixes.Replay,
|
|
||||||
});
|
|
||||||
return FILTER_SKIP;
|
|
||||||
}
|
|
||||||
return this.addCommonPrefixOrContents(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
keyHandler_NotSkippingPrefixNorVersionsV0(key, value) {
|
|
||||||
return this.filter_onNewMasterKeyV0(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
keyHandler_SkippingVersionsV0(key, value) {
|
|
||||||
/* In the SkippingVersionsV0 state, skip all version keys
|
|
||||||
* (<key><versionIdSeparator><version>) */
|
|
||||||
const versionIdIndex = key.indexOf(VID_SEP);
|
|
||||||
if (versionIdIndex !== -1) {
|
|
||||||
return FILTER_SKIP;
|
|
||||||
}
|
|
||||||
return this.filter_onNewMasterKeyV0(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
keyHandler_WaitVersionAfterPHDV0(key, value) {
|
|
||||||
// After a PHD key is encountered, the next version key of the
|
|
||||||
// same object if it exists is the new master key, hence
|
|
||||||
// consider it as such and call 'onNewMasterKeyV0' (the test
|
|
||||||
// 'masterKey == phdKey' is probably redundant when we already
|
|
||||||
// know we have a versioned key, since all objects in v0 have
|
|
||||||
// a master key, but keeping it in doubt)
|
|
||||||
const { masterKey: phdKey } = <DelimiterMasterFilterState_WaitVersionAfterPHDV0> this.state;
|
|
||||||
const versionIdIndex = key.indexOf(VID_SEP);
|
|
||||||
if (versionIdIndex !== -1) {
|
|
||||||
const masterKey = key.slice(0, versionIdIndex);
|
|
||||||
if (masterKey === phdKey) {
|
|
||||||
return this.filter_onNewMasterKeyV0(masterKey, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.filter_onNewMasterKeyV0(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
skippingBase(): string | undefined {
|
|
||||||
switch (this.state.id) {
|
|
||||||
case DelimiterMasterFilterStateId.SkippingVersionsV0:
|
|
||||||
const { masterKey } = <DelimiterMasterFilterState_SkippingVersionsV0> this.state;
|
|
||||||
return masterKey + VID_SEP;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return super.skippingBase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -151,27 +151,6 @@ class DelimiterVersions extends Delimiter {
|
||||||
return FILTER_ACCEPT;
|
return FILTER_ACCEPT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a Common Prefix in the list
|
|
||||||
* @param {String} key - object name
|
|
||||||
* @param {Number} index - after prefix starting point
|
|
||||||
* @return {Boolean} - indicates if iteration should continue
|
|
||||||
*/
|
|
||||||
addCommonPrefix(key, index) {
|
|
||||||
const commonPrefix = key.substring(0, index + this.delimiter.length);
|
|
||||||
if (this.CommonPrefixes.indexOf(commonPrefix) === -1
|
|
||||||
&& this.NextMarker !== commonPrefix) {
|
|
||||||
if (this._reachedMaxKeys()) {
|
|
||||||
return FILTER_END;
|
|
||||||
}
|
|
||||||
this.CommonPrefixes.push(commonPrefix);
|
|
||||||
this.NextMarker = commonPrefix;
|
|
||||||
++this.keys;
|
|
||||||
return FILTER_ACCEPT;
|
|
||||||
}
|
|
||||||
return FILTER_SKIP;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter to apply on each iteration if bucket is in v0
|
* Filter to apply on each iteration if bucket is in v0
|
||||||
* versioning key format, based on:
|
* versioning key format, based on:
|
||||||
|
|
|
@ -62,6 +62,7 @@
|
||||||
"@types/jest": "^27.4.1",
|
"@types/jest": "^27.4.1",
|
||||||
"@types/node": "^17.0.21",
|
"@types/node": "^17.0.21",
|
||||||
"@types/xml2js": "^0.4.11",
|
"@types/xml2js": "^0.4.11",
|
||||||
|
"chance": "^1.1.8",
|
||||||
"eslint": "^8.12.0",
|
"eslint": "^8.12.0",
|
||||||
"eslint-config-airbnb": "6.2.0",
|
"eslint-config-airbnb": "6.2.0",
|
||||||
"eslint-config-scality": "scality/Guidelines#7.10.2",
|
"eslint-config-scality": "scality/Guidelines#7.10.2",
|
||||||
|
|
|
@ -7,17 +7,23 @@ const DelimiterMaster =
|
||||||
require('../../../../lib/algos/list/delimiterMaster').DelimiterMaster;
|
require('../../../../lib/algos/list/delimiterMaster').DelimiterMaster;
|
||||||
const Werelogs = require('werelogs').Logger;
|
const Werelogs = require('werelogs').Logger;
|
||||||
const logger = new Werelogs('listTest');
|
const logger = new Werelogs('listTest');
|
||||||
|
const performListing = require('../../../utils/performListing');
|
||||||
const zpad = require('../../helpers').zpad;
|
const zpad = require('../../helpers').zpad;
|
||||||
const { inc } = require('../../../../lib/algos/list/tools');
|
const { inc } = require('../../../../lib/algos/list/tools');
|
||||||
const VSConst = require('../../../../lib/versioning/constants').VersioningConstants;
|
const VSConst = require('../../../../lib/versioning/constants').VersioningConstants;
|
||||||
const { DbPrefixes } = VSConst;
|
const { DbPrefixes } = VSConst;
|
||||||
|
|
||||||
class Test {
|
class Test {
|
||||||
constructor(name, input, genMDParams, output) {
|
constructor(name, input, genMDParams, output, filter) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.input = input;
|
this.input = input;
|
||||||
this.genMDParams = genMDParams;
|
this.genMDParams = genMDParams;
|
||||||
this.output = output;
|
this.output = output;
|
||||||
|
this.filter = filter || this._defaultFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
_defaultFilter() {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +33,7 @@ const valueDeleteMarker = '{"hello":"world","isDeleteMarker":"true"}';
|
||||||
const data = [
|
const data = [
|
||||||
{ key: 'Pâtisserie=中文-español-English', value },
|
{ key: 'Pâtisserie=中文-español-English', value },
|
||||||
{ key: 'notes/spring/1.txt', value },
|
{ key: 'notes/spring/1.txt', value },
|
||||||
{ key: 'notes/spring/4.txt', value },
|
{ key: 'notes/spring/2.txt', value },
|
||||||
{ key: 'notes/spring/march/1.txt', value },
|
{ key: 'notes/spring/march/1.txt', value },
|
||||||
{ key: 'notes/summer/1.txt', value },
|
{ key: 'notes/summer/1.txt', value },
|
||||||
{ key: 'notes/summer/2.txt', value },
|
{ key: 'notes/summer/2.txt', value },
|
||||||
|
@ -50,9 +56,6 @@ const dataVersioned = [
|
||||||
{ key: 'notes/spring/2.txt\0foo', value },
|
{ key: 'notes/spring/2.txt\0foo', value },
|
||||||
{ key: 'notes/spring/3.txt', value: valueDeleteMarker },
|
{ key: 'notes/spring/3.txt', value: valueDeleteMarker },
|
||||||
{ key: 'notes/spring/3.txt\0foo', value },
|
{ key: 'notes/spring/3.txt\0foo', value },
|
||||||
{ key: 'notes/spring/4.txt', value: valuePHD },
|
|
||||||
{ key: 'notes/spring/4.txt\0bar', value },
|
|
||||||
{ key: 'notes/spring/4.txt\0foo', value },
|
|
||||||
{ key: 'notes/spring/march/1.txt', value },
|
{ key: 'notes/spring/march/1.txt', value },
|
||||||
{ key: 'notes/spring/march/1.txt\0bar', value },
|
{ key: 'notes/spring/march/1.txt\0bar', value },
|
||||||
{ key: 'notes/spring/march/1.txt\0foo', value },
|
{ key: 'notes/spring/march/1.txt\0foo', value },
|
||||||
|
@ -75,8 +78,15 @@ const dataVersioned = [
|
||||||
{ key: 'notes/yore.rs', value },
|
{ key: 'notes/yore.rs', value },
|
||||||
{ key: 'notes/zaphod/Beeblebrox.txt', value },
|
{ key: 'notes/zaphod/Beeblebrox.txt', value },
|
||||||
];
|
];
|
||||||
|
const nonAlphabeticalData = [
|
||||||
|
{ key: 'zzz', value },
|
||||||
|
{ key: 'aaa', value },
|
||||||
|
];
|
||||||
|
|
||||||
const receivedData = data.map(item => ({ key: item.key, value: item.value }));
|
const receivedData = data.map(item => ({ key: item.key, value: item.value }));
|
||||||
|
const receivedNonAlphaData = nonAlphabeticalData.map(
|
||||||
|
item => ({ key: item.key, value: item.value }),
|
||||||
|
);
|
||||||
|
|
||||||
const tests = [
|
const tests = [
|
||||||
new Test('all elements', {}, {
|
new Test('all elements', {}, {
|
||||||
|
@ -114,7 +124,7 @@ const tests = [
|
||||||
Delimiter: undefined,
|
Delimiter: undefined,
|
||||||
IsTruncated: false,
|
IsTruncated: false,
|
||||||
NextMarker: undefined,
|
NextMarker: undefined,
|
||||||
}),
|
}, (e, input) => e.key > input.marker),
|
||||||
new Test('with bad marker', {
|
new Test('with bad marker', {
|
||||||
marker: 'zzzz',
|
marker: 'zzzz',
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
|
@ -132,7 +142,7 @@ const tests = [
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: false,
|
IsTruncated: false,
|
||||||
NextMarker: undefined,
|
NextMarker: undefined,
|
||||||
}),
|
}, (e, input) => e.key > input.marker),
|
||||||
new Test('with makKeys', {
|
new Test('with makKeys', {
|
||||||
maxKeys: 3,
|
maxKeys: 3,
|
||||||
}, {
|
}, {
|
||||||
|
@ -209,12 +219,12 @@ const tests = [
|
||||||
marker: 'notes/summer0',
|
marker: 'notes/summer0',
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/summer0',
|
gt: `notes/summer${inc('/')}`,
|
||||||
lt: 'notes/summer0',
|
lt: `notes/summer${inc('/')}`,
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/summer0`,
|
gt: `${DbPrefixes.Master}notes/summer${inc('/')}`,
|
||||||
lt: `${DbPrefixes.Master}notes/summer0`,
|
lt: `${DbPrefixes.Master}notes/summer${inc('/')}`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [],
|
Contents: [],
|
||||||
|
@ -222,18 +232,18 @@ const tests = [
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: false,
|
IsTruncated: false,
|
||||||
NextMarker: undefined,
|
NextMarker: undefined,
|
||||||
}),
|
}, (e, input) => e.key > input.marker),
|
||||||
new Test('delimiter and prefix (related to #147)', {
|
new Test('delimiter and prefix (related to #147)', {
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
prefix: 'notes/',
|
prefix: 'notes/',
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gte: 'notes/',
|
gte: 'notes/',
|
||||||
lt: 'notes0',
|
lt: `notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gte: `${DbPrefixes.Master}notes/`,
|
gte: `${DbPrefixes.Master}notes/`,
|
||||||
lt: `${DbPrefixes.Master}notes0`,
|
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [
|
Contents: [
|
||||||
|
@ -256,11 +266,11 @@ const tests = [
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/year.txt',
|
gt: 'notes/year.txt',
|
||||||
lt: 'notes0',
|
lt: `notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/year.txt`,
|
gt: `${DbPrefixes.Master}notes/year.txt`,
|
||||||
lt: `${DbPrefixes.Master}notes0`,
|
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [
|
Contents: [
|
||||||
|
@ -272,8 +282,8 @@ const tests = [
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: false,
|
IsTruncated: false,
|
||||||
NextMarker: undefined,
|
NextMarker: undefined,
|
||||||
}),
|
}, (e, input) => e.key > input.marker),
|
||||||
new Test('all parameters 1/5', {
|
new Test('all parameters 1/3', {
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
prefix: 'notes/',
|
prefix: 'notes/',
|
||||||
marker: 'notes/',
|
marker: 'notes/',
|
||||||
|
@ -281,55 +291,55 @@ const tests = [
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/',
|
gt: 'notes/',
|
||||||
lt: 'notes0',
|
lt: `notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/`,
|
gt: `${DbPrefixes.Master}notes/`,
|
||||||
lt: `${DbPrefixes.Master}notes0`,
|
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [],
|
Contents: [],
|
||||||
CommonPrefixes: ['notes/spring/'],
|
CommonPrefixes: ['notes/spring/'],
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: true,
|
IsTruncated: true,
|
||||||
NextMarker: 'notes/spring/1.txt',
|
NextMarker: 'notes/spring/',
|
||||||
}),
|
}, (e, input) => e.key > input.marker),
|
||||||
|
|
||||||
new Test('all parameters 2/5', {
|
new Test('all parameters 2/3', {
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
prefix: 'notes/',
|
prefix: 'notes/', // prefix
|
||||||
marker: 'notes/spring/1.txt',
|
marker: 'notes/spring/',
|
||||||
maxKeys: 1,
|
maxKeys: 1,
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gte: 'notes/spring0',
|
gt: 'notes/spring/',
|
||||||
lt: 'notes0',
|
lt: `notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gte: `${DbPrefixes.Master}notes/spring0`,
|
gt: `${DbPrefixes.Master}notes/spring/`,
|
||||||
lt: `${DbPrefixes.Master}notes0`,
|
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [],
|
Contents: [],
|
||||||
CommonPrefixes: ['notes/summer/'],
|
CommonPrefixes: ['notes/summer/'],
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: true,
|
IsTruncated: true,
|
||||||
NextMarker: 'notes/summer/1.txt',
|
NextMarker: 'notes/summer/',
|
||||||
}),
|
}, (e, input) => e.key > input.marker),
|
||||||
|
|
||||||
new Test('all parameters 3/5', {
|
new Test('all parameters 3/3', {
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
prefix: 'notes/',
|
prefix: 'notes/', // prefix
|
||||||
marker: 'notes/summer/1.txt',
|
marker: 'notes/summer/',
|
||||||
maxKeys: 1,
|
maxKeys: 1,
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gte: 'notes/summer0',
|
gt: 'notes/summer/',
|
||||||
lt: 'notes0',
|
lt: `notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gte: `${DbPrefixes.Master}notes/summer0`,
|
gt: `${DbPrefixes.Master}notes/summer/`,
|
||||||
lt: `${DbPrefixes.Master}notes0`,
|
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [
|
Contents: [
|
||||||
|
@ -339,21 +349,21 @@ const tests = [
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: true,
|
IsTruncated: true,
|
||||||
NextMarker: 'notes/year.txt',
|
NextMarker: 'notes/year.txt',
|
||||||
}),
|
}, (e, input) => e.key > input.marker),
|
||||||
|
|
||||||
new Test('all parameters 4/5', {
|
new Test('all parameters 4/3', {
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
prefix: 'notes/',
|
prefix: 'notes/', // prefix
|
||||||
marker: 'notes/year.txt',
|
marker: 'notes/year.txt',
|
||||||
maxKeys: 1,
|
maxKeys: 1,
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/year.txt',
|
gt: 'notes/year.txt',
|
||||||
lt: 'notes0',
|
lt: `notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/year.txt`,
|
gt: `${DbPrefixes.Master}notes/year.txt`,
|
||||||
lt: `${DbPrefixes.Master}notes0`,
|
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [
|
Contents: [
|
||||||
|
@ -363,9 +373,9 @@ const tests = [
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: true,
|
IsTruncated: true,
|
||||||
NextMarker: 'notes/yore.rs',
|
NextMarker: 'notes/yore.rs',
|
||||||
}),
|
}, (e, input) => e.key > input.marker),
|
||||||
|
|
||||||
new Test('all parameters 5/5', {
|
new Test('all parameters 5/3', {
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
prefix: 'notes/',
|
prefix: 'notes/',
|
||||||
marker: 'notes/yore.rs',
|
marker: 'notes/yore.rs',
|
||||||
|
@ -373,11 +383,11 @@ const tests = [
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/yore.rs',
|
gt: 'notes/yore.rs',
|
||||||
lt: 'notes0',
|
lt: `notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/yore.rs`,
|
gt: `${DbPrefixes.Master}notes/yore.rs`,
|
||||||
lt: `${DbPrefixes.Master}notes0`,
|
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [],
|
Contents: [],
|
||||||
|
@ -385,7 +395,7 @@ const tests = [
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: false,
|
IsTruncated: false,
|
||||||
NextMarker: undefined,
|
NextMarker: undefined,
|
||||||
}),
|
}, (e, input) => e.key > input.marker),
|
||||||
|
|
||||||
new Test('all elements v2', {
|
new Test('all elements v2', {
|
||||||
v2: true,
|
v2: true,
|
||||||
|
@ -425,7 +435,7 @@ const tests = [
|
||||||
Delimiter: undefined,
|
Delimiter: undefined,
|
||||||
IsTruncated: false,
|
IsTruncated: false,
|
||||||
NextContinuationToken: undefined,
|
NextContinuationToken: undefined,
|
||||||
}),
|
}, (e, input) => e.key > input.startAfter),
|
||||||
new Test('with bad startAfter', {
|
new Test('with bad startAfter', {
|
||||||
startAfter: 'zzzz',
|
startAfter: 'zzzz',
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
|
@ -444,7 +454,7 @@ const tests = [
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: false,
|
IsTruncated: false,
|
||||||
NextContinuationToken: undefined,
|
NextContinuationToken: undefined,
|
||||||
}),
|
}, (e, input) => e.key > input.startAfter),
|
||||||
new Test('with valid continuationToken', {
|
new Test('with valid continuationToken', {
|
||||||
continuationToken: receivedData[4].key,
|
continuationToken: receivedData[4].key,
|
||||||
v2: true,
|
v2: true,
|
||||||
|
@ -468,7 +478,7 @@ const tests = [
|
||||||
Delimiter: undefined,
|
Delimiter: undefined,
|
||||||
IsTruncated: false,
|
IsTruncated: false,
|
||||||
NextContinuationToken: undefined,
|
NextContinuationToken: undefined,
|
||||||
}),
|
}, (e, input) => e.key > input.continuationToken),
|
||||||
new Test('with bad continuationToken', {
|
new Test('with bad continuationToken', {
|
||||||
continuationToken: 'zzzz',
|
continuationToken: 'zzzz',
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
|
@ -487,49 +497,47 @@ const tests = [
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: false,
|
IsTruncated: false,
|
||||||
NextContinuationToken: undefined,
|
NextContinuationToken: undefined,
|
||||||
}),
|
}, (e, input) => e.key > input.continuationToken),
|
||||||
new Test('bad startAfter and good prefix', {
|
new Test('bad startAfter and good prefix', {
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
prefix: 'notes/summer/',
|
prefix: 'notes/summer/',
|
||||||
startAfter: 'notes/summer0',
|
startAfter: 'notes/summer0',
|
||||||
v2: true,
|
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/summer0',
|
gte: 'notes/summer/',
|
||||||
lt: 'notes/summer0',
|
lt: `notes/summer${inc('/')}`,
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/summer0`,
|
gte: `${DbPrefixes.Master}notes/summer/`,
|
||||||
lt: `${DbPrefixes.Master}notes/summer0`,
|
lt: `${DbPrefixes.Master}notes/summer${inc('/')}`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [],
|
Contents: [],
|
||||||
CommonPrefixes: [],
|
CommonPrefixes: [],
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: false,
|
IsTruncated: false,
|
||||||
NextContinuationToken: undefined,
|
NextMarker: undefined,
|
||||||
}),
|
}, (e, input) => e.key > input.startAfter),
|
||||||
new Test('bad continuation token and good prefix', {
|
new Test('bad continuation token and good prefix', {
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
prefix: 'notes/summer/',
|
prefix: 'notes/summer/',
|
||||||
continuationToken: 'notes/summer0',
|
continuationToken: 'notes/summer0',
|
||||||
v2: true,
|
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/summer0',
|
gte: 'notes/summer/',
|
||||||
lt: 'notes/summer0',
|
lt: `notes/summer${inc('/')}`,
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/summer0`,
|
gte: `${DbPrefixes.Master}notes/summer/`,
|
||||||
lt: `${DbPrefixes.Master}notes/summer0`,
|
lt: `${DbPrefixes.Master}notes/summer${inc('/')}`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [],
|
Contents: [],
|
||||||
CommonPrefixes: [],
|
CommonPrefixes: [],
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: false,
|
IsTruncated: false,
|
||||||
NextContinuationToken: undefined,
|
NextMarker: undefined,
|
||||||
}),
|
}, (e, input) => e.key > input.continuationToken),
|
||||||
|
|
||||||
new Test('no delimiter v2', {
|
new Test('no delimiter v2', {
|
||||||
startAfter: 'notes/year.txt',
|
startAfter: 'notes/year.txt',
|
||||||
|
@ -551,9 +559,9 @@ const tests = [
|
||||||
Delimiter: undefined,
|
Delimiter: undefined,
|
||||||
IsTruncated: true,
|
IsTruncated: true,
|
||||||
NextContinuationToken: 'notes/yore.rs',
|
NextContinuationToken: 'notes/yore.rs',
|
||||||
}),
|
}, (e, input) => e.key > input.startAfter),
|
||||||
|
|
||||||
new Test('all parameters v2 1/5', {
|
new Test('all parameters v2 1/6', {
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
prefix: 'notes/',
|
prefix: 'notes/',
|
||||||
startAfter: 'notes/',
|
startAfter: 'notes/',
|
||||||
|
@ -562,57 +570,57 @@ const tests = [
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/',
|
gt: 'notes/',
|
||||||
lt: 'notes0',
|
lt: `notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/`,
|
gt: `${DbPrefixes.Master}notes/`,
|
||||||
lt: `${DbPrefixes.Master}notes0`,
|
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [],
|
Contents: [],
|
||||||
CommonPrefixes: ['notes/spring/'],
|
CommonPrefixes: ['notes/spring/'],
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: true,
|
IsTruncated: true,
|
||||||
NextContinuationToken: 'notes/spring/1.txt',
|
NextContinuationToken: 'notes/spring/',
|
||||||
}),
|
}, (e, input) => e.key > input.startAfter),
|
||||||
|
|
||||||
new Test('all parameters v2 2/5', {
|
new Test('all parameters v2 2/6', {
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
prefix: 'notes/',
|
prefix: 'notes/',
|
||||||
continuationToken: 'notes/spring/1.txt',
|
continuationToken: 'notes/spring/',
|
||||||
maxKeys: 1,
|
maxKeys: 1,
|
||||||
v2: true,
|
v2: true,
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gte: 'notes/spring0',
|
gt: 'notes/spring/',
|
||||||
lt: 'notes0',
|
lt: `notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gte: `${DbPrefixes.Master}notes/spring0`,
|
gt: `${DbPrefixes.Master}notes/spring/`,
|
||||||
lt: `${DbPrefixes.Master}notes0`,
|
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [],
|
Contents: [],
|
||||||
CommonPrefixes: ['notes/summer/'],
|
CommonPrefixes: ['notes/summer/'],
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: true,
|
IsTruncated: true,
|
||||||
NextContinuationToken: 'notes/summer/1.txt',
|
NextContinuationToken: 'notes/summer/',
|
||||||
}),
|
}, (e, input) => e.key > input.continuationToken),
|
||||||
|
|
||||||
new Test('all parameters v2 3/5', {
|
new Test('all parameters v2 3/5', {
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
prefix: 'notes/',
|
prefix: 'notes/',
|
||||||
continuationToken: 'notes/summer/1.txt',
|
continuationToken: 'notes/summer/',
|
||||||
maxKeys: 1,
|
maxKeys: 1,
|
||||||
v2: true,
|
v2: true,
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gte: 'notes/summer0',
|
gt: 'notes/summer/',
|
||||||
lt: 'notes0',
|
lt: `notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gte: `${DbPrefixes.Master}notes/summer0`,
|
gt: `${DbPrefixes.Master}notes/summer/`,
|
||||||
lt: `${DbPrefixes.Master}notes0`,
|
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [
|
Contents: [
|
||||||
|
@ -622,7 +630,7 @@ const tests = [
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: true,
|
IsTruncated: true,
|
||||||
NextContinuationToken: 'notes/year.txt',
|
NextContinuationToken: 'notes/year.txt',
|
||||||
}),
|
}, (e, input) => e.key > input.continuationToken),
|
||||||
|
|
||||||
new Test('all parameters v2 4/5', {
|
new Test('all parameters v2 4/5', {
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
|
@ -633,11 +641,11 @@ const tests = [
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/year.txt',
|
gt: 'notes/year.txt',
|
||||||
lt: 'notes0',
|
lt: `notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/year.txt`,
|
gt: `${DbPrefixes.Master}notes/year.txt`,
|
||||||
lt: `${DbPrefixes.Master}notes0`,
|
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [
|
Contents: [
|
||||||
|
@ -647,7 +655,7 @@ const tests = [
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: true,
|
IsTruncated: true,
|
||||||
NextContinuationToken: 'notes/yore.rs',
|
NextContinuationToken: 'notes/yore.rs',
|
||||||
}),
|
}, (e, input) => e.key > input.startAfter),
|
||||||
|
|
||||||
new Test('all parameters v2 5/5', {
|
new Test('all parameters v2 5/5', {
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
|
@ -658,11 +666,11 @@ const tests = [
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/yore.rs',
|
gt: 'notes/yore.rs',
|
||||||
lt: 'notes0',
|
lt: `notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/yore.rs`,
|
gt: `${DbPrefixes.Master}notes/yore.rs`,
|
||||||
lt: `${DbPrefixes.Master}notes0`,
|
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [],
|
Contents: [],
|
||||||
|
@ -670,11 +678,35 @@ const tests = [
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: false,
|
IsTruncated: false,
|
||||||
NextContinuationToken: undefined,
|
NextContinuationToken: undefined,
|
||||||
}),
|
}, (e, input) => e.key > input.startAfter),
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function getTestListing(mdParams, data, vFormat) {
|
const alphabeticalOrderTests = [
|
||||||
|
{
|
||||||
|
params: {},
|
||||||
|
expectedValue: true,
|
||||||
|
}, {
|
||||||
|
params: {
|
||||||
|
alphabeticalOrder: undefined,
|
||||||
|
},
|
||||||
|
expectedValue: true,
|
||||||
|
}, {
|
||||||
|
params: {
|
||||||
|
alphabeticalOrder: true,
|
||||||
|
},
|
||||||
|
expectedValue: true,
|
||||||
|
}, {
|
||||||
|
params: {
|
||||||
|
alphabeticalOrder: false,
|
||||||
|
},
|
||||||
|
expectedValue: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function getTestListing(test, data, vFormat) {
|
||||||
return data
|
return data
|
||||||
|
.filter(e => test.filter(e, test.input))
|
||||||
.map(obj => {
|
.map(obj => {
|
||||||
if (vFormat === 'v0') {
|
if (vFormat === 'v0') {
|
||||||
return obj;
|
return obj;
|
||||||
|
@ -686,12 +718,7 @@ function getTestListing(mdParams, data, vFormat) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return assert.fail(`bad format ${vFormat}`);
|
return assert.fail(`bad format ${vFormat}`);
|
||||||
})
|
});
|
||||||
.filter(e =>
|
|
||||||
(!mdParams.gt || e.key > mdParams.gt) &&
|
|
||||||
(!mdParams.gte || e.key >= mdParams.gte) &&
|
|
||||||
(!mdParams.lt || e.key < mdParams.lt),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
['v0', 'v1'].forEach(vFormat => {
|
['v0', 'v1'].forEach(vFormat => {
|
||||||
|
@ -708,6 +735,15 @@ function getTestListing(mdParams, data, vFormat) {
|
||||||
`${vFormat === 'v1' ? DbPrefixes.Master : ''}foo/`);
|
`${vFormat === 'v1' ? DbPrefixes.Master : ''}foo/`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Should set Delimiter alphabeticalOrder field to the expected value', () => {
|
||||||
|
alphabeticalOrderTests.forEach(test => {
|
||||||
|
const delimiter = new Delimiter(test.params);
|
||||||
|
assert.strictEqual(delimiter.alphabeticalOrder,
|
||||||
|
test.expectedValue,
|
||||||
|
`${JSON.stringify(test.params)}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
tests.forEach(test => {
|
tests.forEach(test => {
|
||||||
it(`Should return metadata listing params to list ${test.name}`, () => {
|
it(`Should return metadata listing params to list ${test.name}`, () => {
|
||||||
const listing = new Delimiter(test.input, logger, vFormat);
|
const listing = new Delimiter(test.input, logger, vFormat);
|
||||||
|
@ -715,13 +751,9 @@ function getTestListing(mdParams, data, vFormat) {
|
||||||
assert.deepStrictEqual(params, test.genMDParams[vFormat]);
|
assert.deepStrictEqual(params, test.genMDParams[vFormat]);
|
||||||
});
|
});
|
||||||
it(`Should list ${test.name}`, () => {
|
it(`Should list ${test.name}`, () => {
|
||||||
const listing = new Delimiter(test.input, logger, vFormat);
|
// Simulate skip scan done by LevelDB
|
||||||
const mdParams = listing.genMDParams();
|
const d = getTestListing(test, data, vFormat);
|
||||||
const rawEntries = getTestListing(mdParams, data, vFormat);
|
const res = performListing(d, Delimiter, test.input, logger, vFormat);
|
||||||
for (const entry of rawEntries) {
|
|
||||||
listing.filter(entry);
|
|
||||||
}
|
|
||||||
const res = listing.result();
|
|
||||||
assert.deepStrictEqual(res, test.output);
|
assert.deepStrictEqual(res, test.output);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -730,16 +762,49 @@ function getTestListing(mdParams, data, vFormat) {
|
||||||
if (vFormat === 'v0') {
|
if (vFormat === 'v0') {
|
||||||
tests.forEach(test => {
|
tests.forEach(test => {
|
||||||
it(`Should list master versions ${test.name}`, () => {
|
it(`Should list master versions ${test.name}`, () => {
|
||||||
const listing = new DelimiterMaster(test.input, logger, vFormat);
|
// Simulate skip scan done by LevelDB
|
||||||
const mdParams = listing.genMDParams();
|
const d = dataVersioned.filter(e => test.filter(e, test.input));
|
||||||
const rawEntries = getTestListing(mdParams, dataVersioned, vFormat);
|
const res = performListing(d, DelimiterMaster, test.input, logger, vFormat);
|
||||||
for (const entry of rawEntries) {
|
|
||||||
listing.filter(entry);
|
|
||||||
}
|
|
||||||
const res = listing.result();
|
|
||||||
assert.deepStrictEqual(res, test.output);
|
assert.deepStrictEqual(res, test.output);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it('Should filter values according to alphabeticalOrder parameter', () => {
|
||||||
|
let test = new Test('alphabeticalOrder parameter set', {
|
||||||
|
delimiter: '/',
|
||||||
|
alphabeticalOrder: true,
|
||||||
|
}, {
|
||||||
|
}, {
|
||||||
|
Contents: [
|
||||||
|
receivedNonAlphaData[0],
|
||||||
|
],
|
||||||
|
Delimiter: '/',
|
||||||
|
CommonPrefixes: [],
|
||||||
|
IsTruncated: false,
|
||||||
|
NextMarker: undefined,
|
||||||
|
});
|
||||||
|
let d = getTestListing(test, nonAlphabeticalData, vFormat);
|
||||||
|
let res = performListing(d, Delimiter, test.input, logger, vFormat);
|
||||||
|
assert.deepStrictEqual(res, test.output);
|
||||||
|
|
||||||
|
test = new Test('alphabeticalOrder parameter set', {
|
||||||
|
delimiter: '/',
|
||||||
|
alphabeticalOrder: false,
|
||||||
|
}, {
|
||||||
|
}, {
|
||||||
|
Contents: [
|
||||||
|
receivedNonAlphaData[0],
|
||||||
|
receivedNonAlphaData[1],
|
||||||
|
],
|
||||||
|
Delimiter: '/',
|
||||||
|
CommonPrefixes: [],
|
||||||
|
IsTruncated: false,
|
||||||
|
NextMarker: undefined,
|
||||||
|
});
|
||||||
|
d = getTestListing(test, nonAlphabeticalData, vFormat);
|
||||||
|
res = performListing(d, Delimiter, test.input, logger, vFormat);
|
||||||
|
assert.deepStrictEqual(res, test.output);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2144,6 +2144,11 @@ chalk@^4.0.0:
|
||||||
ansi-styles "^4.1.0"
|
ansi-styles "^4.1.0"
|
||||||
supports-color "^7.1.0"
|
supports-color "^7.1.0"
|
||||||
|
|
||||||
|
chance@^1.1.8:
|
||||||
|
version "1.1.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/chance/-/chance-1.1.8.tgz#5d6c2b78c9170bf6eb9df7acdda04363085be909"
|
||||||
|
integrity sha512-v7fi5Hj2VbR6dJEGRWLmJBA83LJMS47pkAbmROFxHWd9qmE1esHRZW8Clf1Fhzr3rjxnNZVCjOEv/ivFxeIMtg==
|
||||||
|
|
||||||
char-regex@^1.0.2:
|
char-regex@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf"
|
resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf"
|
||||||
|
|
Loading…
Reference in New Issue