Compare commits
8 Commits
developmen
...
user/jonat
Author | SHA1 | Date |
---|---|---|
Jonathan Gramain | d58ea1863a | |
Jonathan Gramain | 62e68cd541 | |
Jonathan Gramain | 07559f5bca | |
Jonathan Gramain | 8ae597049c | |
Jonathan Gramain | 8e75bbd696 | |
Jonathan Gramain | f73513ed25 | |
Jonathan Gramain | 565eddfc35 | |
Jonathan Gramain | b567120ce5 |
|
@ -2,21 +2,41 @@
|
||||||
|
|
||||||
const Extension = require('./Extension').default;
|
const Extension = require('./Extension').default;
|
||||||
const { inc, listingParamsMasterKeysV0ToV1,
|
const { inc, listingParamsMasterKeysV0ToV1,
|
||||||
FILTER_END, FILTER_ACCEPT, FILTER_SKIP } = require('./tools');
|
FILTER_END, FILTER_ACCEPT, FILTER_SKIP, SKIP_NONE } = 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 {
|
||||||
* Find the common prefix in the path
|
id: number,
|
||||||
*
|
};
|
||||||
* @param {String} key - path of the object
|
|
||||||
* @param {String} delimiter - separator
|
export const enum DelimiterFilterStateId {
|
||||||
* @param {Number} delimiterIndex - 'folder' index in the path
|
NotSkipping = 1,
|
||||||
* @return {String} - CommonPrefix
|
SkippingPrefix = 2,
|
||||||
*/
|
};
|
||||||
function getCommonPrefix(key, delimiter, delimiterIndex) {
|
|
||||||
return key.substring(0, delimiterIndex + delimiter.length);
|
export interface DelimiterFilterState_NotSkipping extends FilterState {
|
||||||
}
|
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
|
||||||
|
@ -30,7 +50,11 @@ function getCommonPrefix(key, delimiter, delimiterIndex) {
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
class Delimiter extends Extension {
|
export class Delimiter extends Extension {
|
||||||
|
|
||||||
|
state: FilterState;
|
||||||
|
keyHandlers: { [id: number]: KeyHandler };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new Delimiter instance
|
* Create a new Delimiter instance
|
||||||
* @constructor
|
* @constructor
|
||||||
|
@ -48,9 +72,6 @@ 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
|
||||||
|
@ -60,38 +81,21 @@ 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;
|
|
||||||
this.continuationToken = parameters.continuationToken;
|
if (parameters.v2) {
|
||||||
this.alphabeticalOrder =
|
this.marker = parameters.continuationToken || parameters.startAfter;
|
||||||
typeof parameters.alphabeticalOrder !== 'undefined' ?
|
} else {
|
||||||
parameters.alphabeticalOrder : true;
|
this.marker = parameters.marker;
|
||||||
|
}
|
||||||
|
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.NextMarker = parameters.marker;
|
this.keyHandlers = {};
|
||||||
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]: {
|
||||||
|
@ -105,21 +109,49 @@ 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 = {};
|
const params: { gt ?: string, gte ?: string, lt ?: string } = {};
|
||||||
if (this.prefix) {
|
if (this.prefix) {
|
||||||
params.gte = this.prefix;
|
params.gte = this.prefix;
|
||||||
params.lt = inc(this.prefix);
|
params.lt = inc(this.prefix);
|
||||||
}
|
}
|
||||||
const startVal = this[this.continueMarker] || this[this.startMarker];
|
if (this.marker && this.delimiter) {
|
||||||
if (startVal) {
|
const commonPrefix = this.getCommonPrefix(this.marker);
|
||||||
if (params.gte && params.gte > startVal) {
|
if (commonPrefix) {
|
||||||
return params;
|
const afterPrefix = inc(commonPrefix);
|
||||||
|
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 = startVal;
|
params.gt = this.marker;
|
||||||
}
|
}
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
@ -151,21 +183,62 @@ 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, value) {
|
addContents(key: string, value: string): number {
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
getObjectKeyV0(obj) {
|
getCommonPrefix(key: string): string | undefined {
|
||||||
|
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) {
|
getObjectKeyV1(obj: { key: string }): string {
|
||||||
return obj.key.slice(DbPrefixes.Master.length);
|
return obj.key.slice(DbPrefixes.Master.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,67 +253,65 @@ 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) {
|
filter(obj: { key: string, value: string }): number {
|
||||||
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))
|
|
||||||
|| (this.alphabeticalOrder
|
return this.handleKey(key, value);
|
||||||
&& typeof this[this.nextContinueMarker] === 'string'
|
|
||||||
&& key <= this[this.nextContinueMarker])) {
|
|
||||||
return FILTER_SKIP;
|
|
||||||
}
|
}
|
||||||
if (this.delimiter) {
|
|
||||||
const baseIndex = this.prefix ? this.prefix.length : 0;
|
setState(state: FilterState): void {
|
||||||
const delimiterIndex = key.indexOf(this.delimiter, baseIndex);
|
this.state = state;
|
||||||
if (delimiterIndex === -1) {
|
|
||||||
return this.addContents(key, value);
|
|
||||||
}
|
}
|
||||||
return this.addCommonPrefix(key, delimiterIndex);
|
|
||||||
|
setKeyHandler(stateId: number, keyHandler: KeyHandler): void {
|
||||||
|
this.keyHandlers[stateId] = keyHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleKey(key: string, value: string): number {
|
||||||
|
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) {
|
||||||
* Add a Common Prefix in the list
|
return this.addCommonPrefixOrContents(key, value);
|
||||||
* @param {String} key - object name
|
}
|
||||||
* @param {Number} index - after prefix starting point
|
|
||||||
* @return {Boolean} - indicates if iteration should continue
|
keyHandler_SkippingPrefix(key, value) {
|
||||||
*/
|
const { prefix } = <DelimiterFilterState_SkippingPrefix> this.state;
|
||||||
addCommonPrefix(key, index) {
|
if (key.startsWith(prefix)) {
|
||||||
const commonPrefix = getCommonPrefix(key, this.delimiter, index);
|
return FILTER_SKIP;
|
||||||
if (this.CommonPrefixes.indexOf(commonPrefix) === -1
|
}
|
||||||
&& this[this.nextContinueMarker] !== commonPrefix) {
|
this.setState(<DelimiterFilterState_NotSkipping> {
|
||||||
if (this._reachedMaxKeys()) {
|
id: DelimiterFilterStateId.NotSkipping,
|
||||||
return FILTER_END;
|
});
|
||||||
}
|
return this.handleKey(key, value);
|
||||||
this.CommonPrefixes.push(commonPrefix);
|
}
|
||||||
this[this.nextContinueMarker] = commonPrefix;
|
|
||||||
++this.keys;
|
skippingBase(): string | undefined {
|
||||||
return FILTER_ACCEPT;
|
switch (this.state.id) {
|
||||||
}
|
case DelimiterFilterStateId.SkippingPrefix:
|
||||||
return FILTER_SKIP;
|
const { prefix } = <DelimiterFilterState_SkippingPrefix> this.state;
|
||||||
|
return prefix;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return SKIP_NONE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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[this.nextContinueMarker];
|
return this.skippingBase();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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() {
|
||||||
return DbPrefixes.Master + this[this.nextContinueMarker];
|
const skipTo = this.skippingBase();
|
||||||
|
if (skipTo === SKIP_NONE) {
|
||||||
|
return SKIP_NONE;
|
||||||
|
}
|
||||||
|
return DbPrefixes.Master + skipTo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -249,12 +320,12 @@ class Delimiter extends Extension {
|
||||||
* isn't truncated
|
* isn't truncated
|
||||||
* @return {Object} - following amazon format
|
* @return {Object} - following amazon format
|
||||||
*/
|
*/
|
||||||
result() {
|
result(): ResultObject {
|
||||||
/* 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 = {
|
const result: ResultObject = {
|
||||||
CommonPrefixes: this.CommonPrefixes,
|
CommonPrefixes: this.CommonPrefixes,
|
||||||
Contents: this.Contents,
|
Contents: this.Contents,
|
||||||
IsTruncated: this.IsTruncated,
|
IsTruncated: this.IsTruncated,
|
||||||
|
@ -262,13 +333,11 @@ class Delimiter extends Extension {
|
||||||
};
|
};
|
||||||
if (this.parameters.v2) {
|
if (this.parameters.v2) {
|
||||||
result.NextContinuationToken = this.IsTruncated
|
result.NextContinuationToken = this.IsTruncated
|
||||||
? this.NextContinuationToken : undefined;
|
? this.nextMarker : 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 };
|
|
|
@ -1,230 +0,0 @@
|
||||||
'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 };
|
|
|
@ -0,0 +1,171 @@
|
||||||
|
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,6 +151,27 @@ 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,7 +62,6 @@
|
||||||
"@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,23 +7,17 @@ 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, filter) {
|
constructor(name, input, genMDParams, output) {
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +27,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/2.txt', value },
|
{ key: 'notes/spring/4.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 },
|
||||||
|
@ -56,6 +50,9 @@ 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 },
|
||||||
|
@ -78,15 +75,8 @@ 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', {}, {
|
||||||
|
@ -124,7 +114,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: '/',
|
||||||
|
@ -142,7 +132,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,
|
||||||
}, {
|
}, {
|
||||||
|
@ -219,12 +209,12 @@ const tests = [
|
||||||
marker: 'notes/summer0',
|
marker: 'notes/summer0',
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: `notes/summer${inc('/')}`,
|
gt: 'notes/summer0',
|
||||||
lt: `notes/summer${inc('/')}`,
|
lt: 'notes/summer0',
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/summer${inc('/')}`,
|
gt: `${DbPrefixes.Master}notes/summer0`,
|
||||||
lt: `${DbPrefixes.Master}notes/summer${inc('/')}`,
|
lt: `${DbPrefixes.Master}notes/summer0`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [],
|
Contents: [],
|
||||||
|
@ -232,18 +222,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: `notes${inc('/')}`,
|
lt: 'notes0',
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gte: `${DbPrefixes.Master}notes/`,
|
gte: `${DbPrefixes.Master}notes/`,
|
||||||
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
lt: `${DbPrefixes.Master}notes0`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [
|
Contents: [
|
||||||
|
@ -266,11 +256,11 @@ const tests = [
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/year.txt',
|
gt: 'notes/year.txt',
|
||||||
lt: `notes${inc('/')}`,
|
lt: 'notes0',
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/year.txt`,
|
gt: `${DbPrefixes.Master}notes/year.txt`,
|
||||||
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
lt: `${DbPrefixes.Master}notes0`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [
|
Contents: [
|
||||||
|
@ -282,8 +272,8 @@ const tests = [
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: false,
|
IsTruncated: false,
|
||||||
NextMarker: undefined,
|
NextMarker: undefined,
|
||||||
}, (e, input) => e.key > input.marker),
|
}),
|
||||||
new Test('all parameters 1/3', {
|
new Test('all parameters 1/5', {
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
prefix: 'notes/',
|
prefix: 'notes/',
|
||||||
marker: 'notes/',
|
marker: 'notes/',
|
||||||
|
@ -291,55 +281,55 @@ const tests = [
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/',
|
gt: 'notes/',
|
||||||
lt: `notes${inc('/')}`,
|
lt: 'notes0',
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/`,
|
gt: `${DbPrefixes.Master}notes/`,
|
||||||
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
lt: `${DbPrefixes.Master}notes0`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [],
|
Contents: [],
|
||||||
CommonPrefixes: ['notes/spring/'],
|
CommonPrefixes: ['notes/spring/'],
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: true,
|
IsTruncated: true,
|
||||||
NextMarker: 'notes/spring/',
|
NextMarker: 'notes/spring/1.txt',
|
||||||
}, (e, input) => e.key > input.marker),
|
}),
|
||||||
|
|
||||||
new Test('all parameters 2/3', {
|
new Test('all parameters 2/5', {
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
prefix: 'notes/', // prefix
|
prefix: 'notes/',
|
||||||
marker: 'notes/spring/',
|
marker: 'notes/spring/1.txt',
|
||||||
maxKeys: 1,
|
maxKeys: 1,
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/spring/',
|
gte: 'notes/spring0',
|
||||||
lt: `notes${inc('/')}`,
|
lt: 'notes0',
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/spring/`,
|
gte: `${DbPrefixes.Master}notes/spring0`,
|
||||||
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
lt: `${DbPrefixes.Master}notes0`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [],
|
Contents: [],
|
||||||
CommonPrefixes: ['notes/summer/'],
|
CommonPrefixes: ['notes/summer/'],
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: true,
|
IsTruncated: true,
|
||||||
NextMarker: 'notes/summer/',
|
NextMarker: 'notes/summer/1.txt',
|
||||||
}, (e, input) => e.key > input.marker),
|
}),
|
||||||
|
|
||||||
new Test('all parameters 3/3', {
|
new Test('all parameters 3/5', {
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
prefix: 'notes/', // prefix
|
prefix: 'notes/',
|
||||||
marker: 'notes/summer/',
|
marker: 'notes/summer/1.txt',
|
||||||
maxKeys: 1,
|
maxKeys: 1,
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/summer/',
|
gte: 'notes/summer0',
|
||||||
lt: `notes${inc('/')}`,
|
lt: 'notes0',
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/summer/`,
|
gte: `${DbPrefixes.Master}notes/summer0`,
|
||||||
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
lt: `${DbPrefixes.Master}notes0`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [
|
Contents: [
|
||||||
|
@ -349,21 +339,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/3', {
|
new Test('all parameters 4/5', {
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
prefix: 'notes/', // prefix
|
prefix: 'notes/',
|
||||||
marker: 'notes/year.txt',
|
marker: 'notes/year.txt',
|
||||||
maxKeys: 1,
|
maxKeys: 1,
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/year.txt',
|
gt: 'notes/year.txt',
|
||||||
lt: `notes${inc('/')}`,
|
lt: 'notes0',
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/year.txt`,
|
gt: `${DbPrefixes.Master}notes/year.txt`,
|
||||||
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
lt: `${DbPrefixes.Master}notes0`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [
|
Contents: [
|
||||||
|
@ -373,9 +363,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/3', {
|
new Test('all parameters 5/5', {
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
prefix: 'notes/',
|
prefix: 'notes/',
|
||||||
marker: 'notes/yore.rs',
|
marker: 'notes/yore.rs',
|
||||||
|
@ -383,11 +373,11 @@ const tests = [
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/yore.rs',
|
gt: 'notes/yore.rs',
|
||||||
lt: `notes${inc('/')}`,
|
lt: 'notes0',
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/yore.rs`,
|
gt: `${DbPrefixes.Master}notes/yore.rs`,
|
||||||
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
lt: `${DbPrefixes.Master}notes0`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [],
|
Contents: [],
|
||||||
|
@ -395,7 +385,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,
|
||||||
|
@ -435,7 +425,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: '/',
|
||||||
|
@ -454,7 +444,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,
|
||||||
|
@ -478,7 +468,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: '/',
|
||||||
|
@ -497,47 +487,49 @@ 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: {
|
||||||
gte: 'notes/summer/',
|
gt: 'notes/summer0',
|
||||||
lt: `notes/summer${inc('/')}`,
|
lt: 'notes/summer0',
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gte: `${DbPrefixes.Master}notes/summer/`,
|
gt: `${DbPrefixes.Master}notes/summer0`,
|
||||||
lt: `${DbPrefixes.Master}notes/summer${inc('/')}`,
|
lt: `${DbPrefixes.Master}notes/summer0`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [],
|
Contents: [],
|
||||||
CommonPrefixes: [],
|
CommonPrefixes: [],
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: false,
|
IsTruncated: false,
|
||||||
NextMarker: undefined,
|
NextContinuationToken: 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: {
|
||||||
gte: 'notes/summer/',
|
gt: 'notes/summer0',
|
||||||
lt: `notes/summer${inc('/')}`,
|
lt: 'notes/summer0',
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gte: `${DbPrefixes.Master}notes/summer/`,
|
gt: `${DbPrefixes.Master}notes/summer0`,
|
||||||
lt: `${DbPrefixes.Master}notes/summer${inc('/')}`,
|
lt: `${DbPrefixes.Master}notes/summer0`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [],
|
Contents: [],
|
||||||
CommonPrefixes: [],
|
CommonPrefixes: [],
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: false,
|
IsTruncated: false,
|
||||||
NextMarker: undefined,
|
NextContinuationToken: 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',
|
||||||
|
@ -559,9 +551,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/6', {
|
new Test('all parameters v2 1/5', {
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
prefix: 'notes/',
|
prefix: 'notes/',
|
||||||
startAfter: 'notes/',
|
startAfter: 'notes/',
|
||||||
|
@ -570,57 +562,57 @@ const tests = [
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/',
|
gt: 'notes/',
|
||||||
lt: `notes${inc('/')}`,
|
lt: 'notes0',
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/`,
|
gt: `${DbPrefixes.Master}notes/`,
|
||||||
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
lt: `${DbPrefixes.Master}notes0`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [],
|
Contents: [],
|
||||||
CommonPrefixes: ['notes/spring/'],
|
CommonPrefixes: ['notes/spring/'],
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: true,
|
IsTruncated: true,
|
||||||
NextContinuationToken: 'notes/spring/',
|
NextContinuationToken: 'notes/spring/1.txt',
|
||||||
}, (e, input) => e.key > input.startAfter),
|
}),
|
||||||
|
|
||||||
new Test('all parameters v2 2/6', {
|
new Test('all parameters v2 2/5', {
|
||||||
delimiter: '/',
|
delimiter: '/',
|
||||||
prefix: 'notes/',
|
prefix: 'notes/',
|
||||||
continuationToken: 'notes/spring/',
|
continuationToken: 'notes/spring/1.txt',
|
||||||
maxKeys: 1,
|
maxKeys: 1,
|
||||||
v2: true,
|
v2: true,
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/spring/',
|
gte: 'notes/spring0',
|
||||||
lt: `notes${inc('/')}`,
|
lt: 'notes0',
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/spring/`,
|
gte: `${DbPrefixes.Master}notes/spring0`,
|
||||||
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
lt: `${DbPrefixes.Master}notes0`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [],
|
Contents: [],
|
||||||
CommonPrefixes: ['notes/summer/'],
|
CommonPrefixes: ['notes/summer/'],
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: true,
|
IsTruncated: true,
|
||||||
NextContinuationToken: 'notes/summer/',
|
NextContinuationToken: 'notes/summer/1.txt',
|
||||||
}, (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/',
|
continuationToken: 'notes/summer/1.txt',
|
||||||
maxKeys: 1,
|
maxKeys: 1,
|
||||||
v2: true,
|
v2: true,
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/summer/',
|
gte: 'notes/summer0',
|
||||||
lt: `notes${inc('/')}`,
|
lt: 'notes0',
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/summer/`,
|
gte: `${DbPrefixes.Master}notes/summer0`,
|
||||||
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
lt: `${DbPrefixes.Master}notes0`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [
|
Contents: [
|
||||||
|
@ -630,7 +622,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: '/',
|
||||||
|
@ -641,11 +633,11 @@ const tests = [
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/year.txt',
|
gt: 'notes/year.txt',
|
||||||
lt: `notes${inc('/')}`,
|
lt: 'notes0',
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/year.txt`,
|
gt: `${DbPrefixes.Master}notes/year.txt`,
|
||||||
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
lt: `${DbPrefixes.Master}notes0`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [
|
Contents: [
|
||||||
|
@ -655,7 +647,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: '/',
|
||||||
|
@ -666,11 +658,11 @@ const tests = [
|
||||||
}, {
|
}, {
|
||||||
v0: {
|
v0: {
|
||||||
gt: 'notes/yore.rs',
|
gt: 'notes/yore.rs',
|
||||||
lt: `notes${inc('/')}`,
|
lt: 'notes0',
|
||||||
},
|
},
|
||||||
v1: {
|
v1: {
|
||||||
gt: `${DbPrefixes.Master}notes/yore.rs`,
|
gt: `${DbPrefixes.Master}notes/yore.rs`,
|
||||||
lt: `${DbPrefixes.Master}notes${inc('/')}`,
|
lt: `${DbPrefixes.Master}notes0`,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Contents: [],
|
Contents: [],
|
||||||
|
@ -678,35 +670,11 @@ const tests = [
|
||||||
Delimiter: '/',
|
Delimiter: '/',
|
||||||
IsTruncated: false,
|
IsTruncated: false,
|
||||||
NextContinuationToken: undefined,
|
NextContinuationToken: undefined,
|
||||||
}, (e, input) => e.key > input.startAfter),
|
}),
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const alphabeticalOrderTests = [
|
function getTestListing(mdParams, data, vFormat) {
|
||||||
{
|
|
||||||
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;
|
||||||
|
@ -718,7 +686,12 @@ function getTestListing(test, 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 => {
|
||||||
|
@ -735,15 +708,6 @@ function getTestListing(test, 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);
|
||||||
|
@ -751,9 +715,13 @@ function getTestListing(test, data, vFormat) {
|
||||||
assert.deepStrictEqual(params, test.genMDParams[vFormat]);
|
assert.deepStrictEqual(params, test.genMDParams[vFormat]);
|
||||||
});
|
});
|
||||||
it(`Should list ${test.name}`, () => {
|
it(`Should list ${test.name}`, () => {
|
||||||
// Simulate skip scan done by LevelDB
|
const listing = new Delimiter(test.input, logger, vFormat);
|
||||||
const d = getTestListing(test, data, vFormat);
|
const mdParams = listing.genMDParams();
|
||||||
const res = performListing(d, Delimiter, test.input, logger, vFormat);
|
const rawEntries = getTestListing(mdParams, data, vFormat);
|
||||||
|
for (const entry of rawEntries) {
|
||||||
|
listing.filter(entry);
|
||||||
|
}
|
||||||
|
const res = listing.result();
|
||||||
assert.deepStrictEqual(res, test.output);
|
assert.deepStrictEqual(res, test.output);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -762,49 +730,16 @@ function getTestListing(test, 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}`, () => {
|
||||||
// Simulate skip scan done by LevelDB
|
const listing = new DelimiterMaster(test.input, logger, vFormat);
|
||||||
const d = dataVersioned.filter(e => test.filter(e, test.input));
|
const mdParams = listing.genMDParams();
|
||||||
const res = performListing(d, DelimiterMaster, test.input, logger, vFormat);
|
const rawEntries = getTestListing(mdParams, dataVersioned, 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,11 +2144,6 @@ 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