Compare commits
2 Commits
developmen
...
ft/arsenal
Author | SHA1 | Date |
---|---|---|
Dora Korpar | 77836d57c5 | |
Dora Korpar | e5fe2d2b7f |
|
@ -220,6 +220,10 @@
|
||||||
"code": 400,
|
"code": 400,
|
||||||
"description": "Request body is empty"
|
"description": "Request body is empty"
|
||||||
},
|
},
|
||||||
|
"MissingRequiredParameter": {
|
||||||
|
"code": 400,
|
||||||
|
"description": "Your request is missing a required parameter."
|
||||||
|
},
|
||||||
"MissingSecurityElement": {
|
"MissingSecurityElement": {
|
||||||
"code": 400,
|
"code": 400,
|
||||||
"description": "The SOAP 1.1 request is missing a security element."
|
"description": "The SOAP 1.1 request is missing a security element."
|
||||||
|
|
|
@ -127,8 +127,8 @@ class LifecycleConfiguration {
|
||||||
const rulesArray = lifecycleConf.Rule;
|
const rulesArray = lifecycleConf.Rule;
|
||||||
if (!rulesArray || !Array.isArray(rulesArray)
|
if (!rulesArray || !Array.isArray(rulesArray)
|
||||||
|| rulesArray.length === 0) {
|
|| rulesArray.length === 0) {
|
||||||
rules.error = errors.MalformedXML.customizeDescription(
|
rules.error = errors.MissingRequiredParameter.customizeDescription(
|
||||||
'request xml does not include at least one rule');
|
'missing required key \'Rules\' in LifecycleConfiguration');
|
||||||
return rules;
|
return rules;
|
||||||
}
|
}
|
||||||
if (rulesArray.length > 1000) {
|
if (rulesArray.length > 1000) {
|
||||||
|
@ -197,8 +197,8 @@ class LifecycleConfiguration {
|
||||||
return ruleObj;
|
return ruleObj;
|
||||||
}
|
}
|
||||||
if (!rule.Status) {
|
if (!rule.Status) {
|
||||||
ruleObj.error = errors.MalformedXML.customizeDescription(
|
ruleObj.error = errors.MissingRequiredParameter.
|
||||||
'Rule xml does not include Status');
|
customizeDescription('Rule xml does not include Status');
|
||||||
return ruleObj;
|
return ruleObj;
|
||||||
}
|
}
|
||||||
const subFilter = rule.Filter ? rule.Filter[0] : rule.Prefix;
|
const subFilter = rule.Filter ? rule.Filter[0] : rule.Prefix;
|
||||||
|
@ -254,12 +254,8 @@ class LifecycleConfiguration {
|
||||||
filterObj.rulePrefix = '';
|
filterObj.rulePrefix = '';
|
||||||
if (Array.isArray(filter)) {
|
if (Array.isArray(filter)) {
|
||||||
// if Prefix was included, not Filter, filter will be Prefix array
|
// if Prefix was included, not Filter, filter will be Prefix array
|
||||||
if (filter.length > 1) {
|
// if more than one Prefix is included, we ignore all but the last
|
||||||
filterObj.error = errors.MalformedXML.customizeDescription(
|
filterObj.rulePrefix = filter.pop();
|
||||||
'Rule includes more than one Prefix');
|
|
||||||
return filterObj;
|
|
||||||
}
|
|
||||||
filterObj.rulePrefix = filter[0];
|
|
||||||
return filterObj;
|
return filterObj;
|
||||||
}
|
}
|
||||||
if (filter.And && (filter.Prefix || filter.Tag) ||
|
if (filter.And && (filter.Prefix || filter.Tag) ||
|
||||||
|
@ -269,12 +265,7 @@ class LifecycleConfiguration {
|
||||||
return filterObj;
|
return filterObj;
|
||||||
}
|
}
|
||||||
if (filter.Prefix) {
|
if (filter.Prefix) {
|
||||||
if (filter.Prefix.length > 1) {
|
filterObj.rulePrefix = filter.Prefix.pop();
|
||||||
filterObj.error = errors.MalformedXML.customizeDescription(
|
|
||||||
'Filter includes more than one Prefix');
|
|
||||||
return filterObj;
|
|
||||||
}
|
|
||||||
filterObj.rulePrefix = filter.Prefix[0];
|
|
||||||
return filterObj;
|
return filterObj;
|
||||||
}
|
}
|
||||||
if (filter.Tag) {
|
if (filter.Tag) {
|
||||||
|
@ -288,22 +279,19 @@ class LifecycleConfiguration {
|
||||||
}
|
}
|
||||||
if (filter.And) {
|
if (filter.And) {
|
||||||
const andF = filter.And[0];
|
const andF = filter.And[0];
|
||||||
if (!andF.Tag || (!andF.Prefix && andF.Tag.length < 2)) {
|
if (!andF.Tags || (!andF.Prefix && andF.Tags.length < 2)) {
|
||||||
filterObj.error = errors.MalformedXML.customizeDescription(
|
filterObj.error = errors.MalformedXML.customizeDescription(
|
||||||
'And should include Prefix and Tag or more than one Tag');
|
'And should include Prefix and Tags or more than one Tag');
|
||||||
return filterObj;
|
return filterObj;
|
||||||
}
|
}
|
||||||
if (andF.Prefix && andF.Prefix.length > 1) {
|
if (andF.Prefix && andF.Prefix.length >= 1) {
|
||||||
filterObj.error = errors.MalformedXML.customizeDescription(
|
filterObj.rulePrefix = andF.Prefix.pop();
|
||||||
'And includes more than one Prefix');
|
|
||||||
return filterObj;
|
|
||||||
}
|
}
|
||||||
const tagObj = this._parseTags(andF.Tag[0]);
|
const tagObj = this._parseTags(andF.Tags[0]);
|
||||||
if (tagObj.error) {
|
if (tagObj.error) {
|
||||||
filterObj.error = tagObj.error;
|
filterObj.error = tagObj.error;
|
||||||
return filterObj;
|
return filterObj;
|
||||||
}
|
}
|
||||||
filterObj.rulePrefix = andF.Prefix ? andF.Prefix[0] : null;
|
|
||||||
filterObj.tags = tagObj.tags;
|
filterObj.tags = tagObj.tags;
|
||||||
return filterObj;
|
return filterObj;
|
||||||
}
|
}
|
||||||
|
@ -329,8 +317,11 @@ class LifecycleConfiguration {
|
||||||
_parseTags(tags) {
|
_parseTags(tags) {
|
||||||
const tagObj = {};
|
const tagObj = {};
|
||||||
tagObj.tags = [];
|
tagObj.tags = [];
|
||||||
|
// reset _tagKeys to empty because keys cannot overlap within a rule,
|
||||||
|
// but different rules can have the same tag keys
|
||||||
|
this._tagKeys = [];
|
||||||
if (!tags.Key || !tags.Value) {
|
if (!tags.Key || !tags.Value) {
|
||||||
tagObj.error = errors.MalformedXML.customizeDescription(
|
tagObj.error = errors.MissingRequiredParameter.customizeDescription(
|
||||||
'Tag XML does not contain both Key and Value');
|
'Tag XML does not contain both Key and Value');
|
||||||
return tagObj;
|
return tagObj;
|
||||||
}
|
}
|
||||||
|
@ -414,7 +405,7 @@ class LifecycleConfiguration {
|
||||||
_parseStatus(status) {
|
_parseStatus(status) {
|
||||||
const statusObj = {};
|
const statusObj = {};
|
||||||
statusObj.propName = 'ruleStatus';
|
statusObj.propName = 'ruleStatus';
|
||||||
const validStatuses = ['Enabled', 'Suspended'];
|
const validStatuses = ['Enabled', 'Disabled'];
|
||||||
if (!validStatuses.includes(status)) {
|
if (!validStatuses.includes(status)) {
|
||||||
statusObj.error = errors.MalformedXML.customizeDescription(
|
statusObj.error = errors.MalformedXML.customizeDescription(
|
||||||
'Status is not valid');
|
'Status is not valid');
|
||||||
|
@ -456,7 +447,7 @@ class LifecycleConfiguration {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (actionsObj.actions.length === 0) {
|
if (actionsObj.actions.length === 0) {
|
||||||
actionsObj.error = errors.MalformedXML.customizeDescription(
|
actionsObj.error = errors.InvalidRequest.customizeDescription(
|
||||||
'Rule does not include valid action');
|
'Rule does not include valid action');
|
||||||
return actionsObj;
|
return actionsObj;
|
||||||
}
|
}
|
||||||
|
@ -501,7 +492,7 @@ class LifecycleConfiguration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (filter && filter.Tag) {
|
if (filter && filter.Tag) {
|
||||||
abortObj.error = errors.MalformedXML.customizeDescription(
|
abortObj.error = errors.InvalidRequest.customizeDescription(
|
||||||
'Tag-based filter cannot be used with ' +
|
'Tag-based filter cannot be used with ' +
|
||||||
'AbortIncompleteMultipartUpload action');
|
'AbortIncompleteMultipartUpload action');
|
||||||
return abortObj;
|
return abortObj;
|
||||||
|
@ -515,7 +506,7 @@ class LifecycleConfiguration {
|
||||||
}
|
}
|
||||||
const daysInt = parseInt(subAbort.DaysAfterInitiation[0], 10);
|
const daysInt = parseInt(subAbort.DaysAfterInitiation[0], 10);
|
||||||
if (daysInt < 1) {
|
if (daysInt < 1) {
|
||||||
abortObj.error = errors.MalformedXML.customizeDescription(
|
abortObj.error = errors.InvalidArgument.customizeDescription(
|
||||||
'DaysAfterInitiation is not a positive integer');
|
'DaysAfterInitiation is not a positive integer');
|
||||||
return abortObj;
|
return abortObj;
|
||||||
}
|
}
|
||||||
|
@ -540,19 +531,25 @@ class LifecycleConfiguration {
|
||||||
_parseExpiration(rule) {
|
_parseExpiration(rule) {
|
||||||
const expObj = {};
|
const expObj = {};
|
||||||
const subExp = rule.Expiration[0];
|
const subExp = rule.Expiration[0];
|
||||||
if (!subExp.Date && !subExp.Days && !subExp.ExpiredObjDeleteMarker) {
|
if (!subExp.Date && !subExp.Days && !subExp.ExpiredObjectDeleteMarker) {
|
||||||
expObj.error = errors.MalformedXML.customizeDescription(
|
expObj.error = errors.MalformedXML.customizeDescription(
|
||||||
'Expiration action does not include an action time');
|
'Expiration action does not include an action time');
|
||||||
return expObj;
|
return expObj;
|
||||||
}
|
}
|
||||||
|
const eodm = 'ExpiredObjectDeleteMarker';
|
||||||
|
if (subExp.Date && (subExp.Days || subExp[eodm]) ||
|
||||||
|
(subExp.Days && subExp[eodm])) {
|
||||||
|
expObj.error = errors.MalformedXML.customizeDescription(
|
||||||
|
'Expiration action includes more than one time');
|
||||||
|
return expObj;
|
||||||
|
}
|
||||||
if (subExp.Date) {
|
if (subExp.Date) {
|
||||||
const isoRegex = new RegExp('^(-?(?:[1-9][0-9]*)?[0-9]{4})-' +
|
const isoRegex = new RegExp('^(-?(?:[1-9][0-9]*)?[0-9]{4})-' +
|
||||||
'(1[0-2]|0[1-9])-(3[0-1]|0[1-9]|[1-2][0-9])T(2[0-3]|[0-1]' +
|
'(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9])' +
|
||||||
'[0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z|[+-](?:2[0-3]' +
|
':([0-5][0-9]):([0-5][0-9])(.[0-9]+)?(Z)?$');
|
||||||
'|[0-1][0-9]):[0-5][0-9])?$');
|
|
||||||
if (!isoRegex.test(subExp.Date[0])) {
|
if (!isoRegex.test(subExp.Date[0])) {
|
||||||
expObj.error = errors.MalformedXML.customizeDescription(
|
expObj.error = errors.InvalidArgument.customizeDescription(
|
||||||
'Date is not in proper ISO 8601 format');
|
'Date must be in ISO 8601 format');
|
||||||
} else {
|
} else {
|
||||||
expObj.date = subExp.Date[0];
|
expObj.date = subExp.Date[0];
|
||||||
}
|
}
|
||||||
|
@ -560,19 +557,33 @@ class LifecycleConfiguration {
|
||||||
if (subExp.Days) {
|
if (subExp.Days) {
|
||||||
const daysInt = parseInt(subExp.Days[0], 10);
|
const daysInt = parseInt(subExp.Days[0], 10);
|
||||||
if (daysInt < 1) {
|
if (daysInt < 1) {
|
||||||
expObj.error = errors.MalformedXML.customizeDescription(
|
expObj.error = errors.InvalidArgument.customizeDescription(
|
||||||
'Expiration days is not a positive integer');
|
'Expiration days is not a positive integer');
|
||||||
} else {
|
} else {
|
||||||
expObj.days = daysInt;
|
expObj.days = daysInt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (subExp.ExpiredObjDeleteMarker) {
|
if (subExp.ExpiredObjectDeleteMarker) {
|
||||||
|
let filter = null;
|
||||||
|
if (rule.Filter && rule.Filter[0]) {
|
||||||
|
if (rule.Filter[0].And) {
|
||||||
|
filter = rule.Filter[0].And[0];
|
||||||
|
} else {
|
||||||
|
filter = rule.Filter[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (filter && filter.Tag) {
|
||||||
|
expObj.error = errors.InvalidRequest.customizeDescription(
|
||||||
|
'Tag-based filter cannot be used with ' +
|
||||||
|
'ExpiredObjectDeleteMarker action');
|
||||||
|
return expObj;
|
||||||
|
}
|
||||||
const validValues = ['true', 'false'];
|
const validValues = ['true', 'false'];
|
||||||
if (!validValues.includes(subExp.ExpiredObjDeleteMarker[0])) {
|
if (!validValues.includes(subExp.ExpiredObjectDeleteMarker[0])) {
|
||||||
expObj.error = errors.MalformedXML.customizeDescription(
|
expObj.error = errors.MalformedXML.customizeDescription(
|
||||||
'ExpiredObjDeleteMarker is not true or false');
|
'ExpiredObjDeleteMarker is not true or false');
|
||||||
} else {
|
} else {
|
||||||
expObj.deleteMarker = subExp.ExpiredObjDeleteMarker[0];
|
expObj.deleteMarker = subExp.ExpiredObjectDeleteMarker[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return expObj;
|
return expObj;
|
||||||
|
@ -601,7 +612,7 @@ class LifecycleConfiguration {
|
||||||
}
|
}
|
||||||
const daysInt = parseInt(subNVExp.NoncurrentDays[0], 10);
|
const daysInt = parseInt(subNVExp.NoncurrentDays[0], 10);
|
||||||
if (daysInt < 1) {
|
if (daysInt < 1) {
|
||||||
nvExpObj.error = errors.MalformedXML.customizeDescription(
|
nvExpObj.error = errors.InvalidArgument.customizeDescription(
|
||||||
'NoncurrentDays is not a positive integer');
|
'NoncurrentDays is not a positive integer');
|
||||||
} else {
|
} else {
|
||||||
nvExpObj.days = daysInt;
|
nvExpObj.days = daysInt;
|
||||||
|
@ -666,12 +677,20 @@ class LifecycleConfiguration {
|
||||||
const Prefix = rulePrefix ? `<Prefix>${rulePrefix}</Prefix>` : '';
|
const Prefix = rulePrefix ? `<Prefix>${rulePrefix}</Prefix>` : '';
|
||||||
let tagXML = '';
|
let tagXML = '';
|
||||||
if (tags) {
|
if (tags) {
|
||||||
tagXML = tags.map(t => {
|
if (Prefix || tags.length > 1) {
|
||||||
|
const keysVals = tags.map(t => {
|
||||||
const { key, val } = t;
|
const { key, val } = t;
|
||||||
const Tag = `<Tag><Key>${key}</Key>` +
|
const Tag = `<Key>${key}</Key>` +
|
||||||
`<Value>${val}</Value></Tag>`;
|
`<Value>${val}</Value>`;
|
||||||
return Tag;
|
return Tag;
|
||||||
}).join('');
|
}).join('');
|
||||||
|
tagXML = `<Tags>${keysVals}</Tags>`;
|
||||||
|
} else {
|
||||||
|
// only one tag included
|
||||||
|
const { key, val } = tags[0];
|
||||||
|
tagXML = `<Tag><Key>${key}</Key>` +
|
||||||
|
`<Value>${val}</Value></Tag>`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let Filter;
|
let Filter;
|
||||||
if (rulePrefix && !tags) {
|
if (rulePrefix && !tags) {
|
||||||
|
@ -686,7 +705,7 @@ class LifecycleConfiguration {
|
||||||
const Actions = actions.map(action => {
|
const Actions = actions.map(action => {
|
||||||
const { actionName, days, date, deleteMarker } = action;
|
const { actionName, days, date, deleteMarker } = action;
|
||||||
let Action;
|
let Action;
|
||||||
if (actionName === 'AbortIncompleteMultipartUploads') {
|
if (actionName === 'AbortIncompleteMultipartUpload') {
|
||||||
Action = `<${actionName}><DaysAfterInitiation>${days}` +
|
Action = `<${actionName}><DaysAfterInitiation>${days}` +
|
||||||
`</DaysAfterInitiation></${actionName}>`;
|
`</DaysAfterInitiation></${actionName}>`;
|
||||||
} else if (actionName === 'NoncurrentVersionExpiration') {
|
} else if (actionName === 'NoncurrentVersionExpiration') {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "6.9.5"
|
"node": "6.9.5"
|
||||||
},
|
},
|
||||||
"version": "7.4.0",
|
"version": "7.4.0-foo",
|
||||||
"description": "Common utilities for the S3 project components",
|
"description": "Common utilities for the S3 project components",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
|
@ -4,6 +4,15 @@ const { parseString } = require('xml2js');
|
||||||
const LifecycleConfiguration =
|
const LifecycleConfiguration =
|
||||||
require('../../../lib/models/LifecycleConfiguration.js');
|
require('../../../lib/models/LifecycleConfiguration.js');
|
||||||
|
|
||||||
|
const days = {
|
||||||
|
AbortIncompleteMultipartUpload: 'DaysAfterInitiation',
|
||||||
|
NoncurrentVersionExpiration: 'NoncurrentDays',
|
||||||
|
Expiration: 'Days',
|
||||||
|
};
|
||||||
|
|
||||||
|
const date = new Date();
|
||||||
|
date.setUTCHours(0, 0, 0, 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format of xml request:
|
* Format of xml request:
|
||||||
<LifecycleConfiguration>
|
<LifecycleConfiguration>
|
||||||
|
@ -37,32 +46,17 @@ const LifecycleConfiguration =
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const requiredTags = [
|
const requiredTags = [
|
||||||
{ tag: 'LifecycleConfiguration',
|
{ tag: 'LifecycleConfiguration', error: 'MalformedXML',
|
||||||
errMessage: 'request xml is undefined or empty' },
|
errMessage: 'request xml is undefined or empty' },
|
||||||
{ tag: 'Rule',
|
{ tag: 'Rule', error: 'MissingRequiredParameter',
|
||||||
errMessage: 'request xml does not include at least one rule' },
|
errMessage: 'missing required key \'Rules\' in ' +
|
||||||
{ tag: 'Status', errMessage: 'Rule xml does not include Status' },
|
'LifecycleConfiguration' },
|
||||||
{ tag: 'Filter', errMessage: 'Rule xml does not include valid Filter ' +
|
{ tag: 'Status', error: 'MissingRequiredParameter',
|
||||||
'or Prefix' },
|
errMessage: 'Rule xml does not include Status' },
|
||||||
{ tag: 'Action', errMessage: 'Rule does not include valid action' }];
|
{ tag: 'Filter', error: 'MalformedXML',
|
||||||
|
errMessage: 'Rule xml does not include valid Filter or Prefix' },
|
||||||
const validActions = [
|
{ tag: 'Action', error: 'InvalidRequest',
|
||||||
{ tag: 'Expiration',
|
errMessage: 'Rule does not include valid action' }];
|
||||||
errMessage: 'Expiration action does not include an action time' },
|
|
||||||
{ tag: 'NoncurrentVersionExpiration',
|
|
||||||
errMessage: 'NoncurrentVersionExpiration action does not include ' +
|
|
||||||
'NoncurrentDays' },
|
|
||||||
{ tag: 'AbortIncompleteMultipartUpload',
|
|
||||||
errMessage: 'AbortIncompleteMultipartUpload action does not ' +
|
|
||||||
'include DaysAfterInitiation' }];
|
|
||||||
|
|
||||||
const invalidActions = [
|
|
||||||
{ tag: 'foo', errMessage: 'Rule does not include valid action' },
|
|
||||||
{ tag: 'invalid-days-value',
|
|
||||||
errMessage: 'Expiration days is not a positive integer' },
|
|
||||||
{ tag: 'abortMPU-tag',
|
|
||||||
errMessage: 'Tag-based filter cannot be used with ' +
|
|
||||||
'AbortIncompleteMultipartUpload action' }];
|
|
||||||
|
|
||||||
const notImplementedActions = [
|
const notImplementedActions = [
|
||||||
{ tag: 'Transition',
|
{ tag: 'Transition',
|
||||||
|
@ -70,100 +64,175 @@ const notImplementedActions = [
|
||||||
{ tag: 'NoncurrentVersionTransition',
|
{ tag: 'NoncurrentVersionTransition',
|
||||||
errMessage: 'Transition lifecycle action not yet implemented' }];
|
errMessage: 'Transition lifecycle action not yet implemented' }];
|
||||||
|
|
||||||
const invalidFilters = [
|
const invalidActions = [
|
||||||
{ tag: 'two-prefixes',
|
{ tag: 'AbortIncompleteMultipartUpload', label: 'no-time',
|
||||||
errMessage: 'Filter includes more than one Prefix' },
|
error: 'MalformedXML',
|
||||||
{ tag: 'invalid-tag',
|
errMessage: 'AbortIncompleteMultipartUpload action does not ' +
|
||||||
errMessage: 'Tag XML does not contain both Key and Value' }];
|
'include DaysAfterInitiation' },
|
||||||
|
{ tag: 'AbortIncompleteMultipartUpload', label: 'no-tags',
|
||||||
|
error: 'InvalidRequest', errMessage: 'Tag-based filter cannot be ' +
|
||||||
|
'used with AbortIncompleteMultipartUpload action' },
|
||||||
|
{ tag: 'AbortIncompleteMultipartUpload', label: 'invalid-days',
|
||||||
|
error: 'InvalidArgument',
|
||||||
|
errMessage: 'DaysAfterInitiation is not a positive integer' },
|
||||||
|
{ tag: 'Expiration', label: 'no-time', error: 'MalformedXML',
|
||||||
|
errMessage: 'Expiration action does not include an action time' },
|
||||||
|
{ tag: 'Expiration', label: 'mult-times', error: 'MalformedXML',
|
||||||
|
errMessage: 'Expiration action includes more than one time' },
|
||||||
|
{ tag: 'Expiration', label: 'non-iso', error: 'InvalidArgument',
|
||||||
|
errMessage: 'Date must be in ISO 8601 format' },
|
||||||
|
{ tag: 'Expiration', label: 'invalid-days', error: 'InvalidArgument',
|
||||||
|
errMessage: 'Expiration days is not a positive integer' },
|
||||||
|
{ tag: 'Expiration', label: 'no-tags', inTag: 'ExpiredObjectDeleteMarker',
|
||||||
|
error: 'InvalidRequest',
|
||||||
|
errMessage: 'Tag-based filter cannot be used with ' +
|
||||||
|
'ExpiredObjectDeleteMarker action' },
|
||||||
|
{ tag: 'Expiration', label: 'invalid-eodm', error: 'MalformedXML',
|
||||||
|
errMessage: 'ExpiredObjDeleteMarker is not true or false' },
|
||||||
|
{ tag: 'NoncurrentVersionExpiration', label: 'no-time',
|
||||||
|
error: 'MalformedXML',
|
||||||
|
errMessage: 'NoncurrentVersionExpiration action does not include ' +
|
||||||
|
'NoncurrentDays' },
|
||||||
|
{ tag: 'NoncurrentVersionExpiration', label: 'invalid-days',
|
||||||
|
error: 'InvalidArgument',
|
||||||
|
errMessage: 'NoncurrentDays is not a positive integer' }];
|
||||||
|
|
||||||
const invalidRequests = [
|
const invalidFilters = [
|
||||||
{ tag: 'not-unique-id',
|
{ tag: 'Filter', label: 'also-prefix', error: 'MalformedXML',
|
||||||
errMessage: 'Rule ID must be unique' },
|
errMessage: 'Rule xml should not include both Filter and Prefix' },
|
||||||
{ tag: 'key-too-long',
|
{ tag: 'Filter', label: 'and-prefix-tag', error: 'MalformedXML',
|
||||||
|
errMessage: 'Filter should only include one of And, Prefix, or ' +
|
||||||
|
'Tag key' },
|
||||||
|
{ tag: 'And', label: 'only-prefix', error: 'MalformedXML',
|
||||||
|
errMessage: 'And should include Prefix and Tags or more than one Tag' },
|
||||||
|
{ tag: 'And', label: 'single-tag', error: 'MalformedXML',
|
||||||
|
errMessage: 'And should include Prefix and Tags or more than one Tag' },
|
||||||
|
{ tag: 'Tag', label: 'no-key', error: 'MissingRequiredParameter',
|
||||||
|
errMessage: 'Tag XML does not contain both Key and Value' },
|
||||||
|
{ tag: 'Tag', label: 'no-value', error: 'MissingRequiredParameter',
|
||||||
|
errMessage: 'Tag XML does not contain both Key and Value' },
|
||||||
|
{ tag: 'Tag', label: 'key-too-long', error: 'InvalidRequest',
|
||||||
errMessage: 'Tag Key must be a length between 1 and 128 char' }];
|
errMessage: 'Tag Key must be a length between 1 and 128 char' }];
|
||||||
|
|
||||||
function generateFilter(errorTag, tagValue) {
|
function generateAction(errorTag, tagObj) {
|
||||||
|
const xmlObj = {};
|
||||||
|
if (tagObj) {
|
||||||
|
let middleTags = '';
|
||||||
|
if (tagObj.label === 'no-time') {
|
||||||
|
middleTags = '';
|
||||||
|
}
|
||||||
|
if (tagObj.label === 'no-tags') {
|
||||||
|
middleTags = tagObj.inTag ?
|
||||||
|
`<${tagObj.inTag}>true</${tagObj.inTag}>` :
|
||||||
|
`<${days[tagObj.tag]}>1</${days[tagObj.tag]}>`;
|
||||||
|
xmlObj.filter = '<Filter><Tag><Key>key</Key>' +
|
||||||
|
'<Value></Value></Tag></Filter>';
|
||||||
|
}
|
||||||
|
if (tagObj.label === 'invalid-days') {
|
||||||
|
middleTags = `<${days[tagObj.tag]}>0</${days[tagObj.tag]}>`;
|
||||||
|
}
|
||||||
|
if (tagObj.label === 'mult-times') {
|
||||||
|
middleTags = `<Days>1</Days><Date>${date}</Date>`;
|
||||||
|
}
|
||||||
|
if (tagObj.label === 'non-iso') {
|
||||||
|
middleTags = '<Date>03-08-2018</Date>';
|
||||||
|
}
|
||||||
|
if (tagObj.label === 'invalid-eodm') {
|
||||||
|
middleTags = '<ExpiredObjectDeleteMarker>foo' +
|
||||||
|
'</ExpiredObjectDeleteMarker>';
|
||||||
|
}
|
||||||
|
xmlObj.actions = `<${tagObj.tag}>${middleTags}</${tagObj.tag}>`;
|
||||||
|
} else {
|
||||||
|
xmlObj.actions = '';
|
||||||
|
}
|
||||||
|
return xmlObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateFilter(errorTag, tagObj) {
|
||||||
let Filter;
|
let Filter;
|
||||||
if (tagValue && tagValue.filter === 'two-prefixes') {
|
let middleTags = '';
|
||||||
Filter = '<Filter><Prefix>foo</Prefix>' +
|
if (tagObj) {
|
||||||
'<Prefix>foo2</Prefix></Filter>';
|
if (tagObj.label === 'and-prefix-tag') {
|
||||||
} else if (tagValue && tagValue.filter === 'invalid-tag') {
|
middleTags = '<And></And><Prefix></Prefix><Tag></Tag>';
|
||||||
Filter = '<Filter><Tag></Tag></Filter>';
|
}
|
||||||
|
if (tagObj.label === 'only-prefix') {
|
||||||
|
middleTags = '<And><Prefix></Prefix></And>';
|
||||||
|
}
|
||||||
|
if (tagObj.label === 'single-tag') {
|
||||||
|
middleTags = '<And><Tags><Key>fo</Key><Value></Value></Tags></And>';
|
||||||
|
}
|
||||||
|
if (tagObj.label === 'no-key') {
|
||||||
|
middleTags = '<Tag><Value></Value></Tag>';
|
||||||
|
}
|
||||||
|
if (tagObj.label === 'no-value') {
|
||||||
|
middleTags = '<Tag><Key></Key></Tag>';
|
||||||
|
}
|
||||||
|
if (tagObj.label === 'key-too-long') {
|
||||||
|
const longKey = 'a'.repeat(129);
|
||||||
|
middleTags = `<Tag><Key>${longKey}</Key><Value></Value></Tag>`;
|
||||||
|
}
|
||||||
|
if (tagObj.label === 'mult-prefixes') {
|
||||||
|
middleTags = '<Prefix>foo</Prefix><Prefix>bar</Prefix>' +
|
||||||
|
`<Prefix>${tagObj.lastPrefix}</Prefix>`;
|
||||||
|
}
|
||||||
|
Filter = `<Filter>${middleTags}</Filter>`;
|
||||||
|
if (tagObj.label === 'also-prefix') {
|
||||||
|
Filter = '<Filter></Filter><Prefix></Prefix>';
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Filter = '';
|
Filter = '';
|
||||||
}
|
}
|
||||||
return Filter;
|
return Filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateAction(errorTag, tagValue) {
|
function generateRule(errorTag, tagObj, ID, Status, Filter, Action) {
|
||||||
const xmlObj = {};
|
|
||||||
if (tagValue && tagValue.action === 'invalid-days-value') {
|
|
||||||
xmlObj.actions = '<Expiration><Days>0</Days></Expiration>';
|
|
||||||
} else if (tagValue && tagValue.action === 'abortMPU-tag') {
|
|
||||||
xmlObj.actions = '<AbortIncompleteMultipartUpload>' +
|
|
||||||
'</AbortIncompleteMultipartUpload>';
|
|
||||||
xmlObj.filter = '<Filter><Tag><Key>key</Key>' +
|
|
||||||
'<Value></Value></Tag></Filter>';
|
|
||||||
} else {
|
|
||||||
xmlObj.actions = tagValue && tagValue.action ?
|
|
||||||
`<${tagValue.action}></${tagValue.action}>` : '';
|
|
||||||
}
|
|
||||||
return xmlObj;
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateRule(errorTag, tagValue, ID, Status, Filter, Action) {
|
|
||||||
let Rule;
|
let Rule;
|
||||||
if (tagValue && tagValue.rule === 'not-unique-id') {
|
if (tagObj && tagObj.rule === 'not-unique-id') {
|
||||||
Rule = `<Rule>${ID + Status + Filter + Action}</Rule>` +
|
Rule = `<Rule>${ID + Status + Filter + Action}</Rule>` +
|
||||||
`<Rule>${ID + Status + Filter + Action}</Rule>`;
|
`<Rule>${ID + Status + Filter + Action}</Rule>`;
|
||||||
} else if (tagValue && tagValue.rule === 'too-many-rules') {
|
} else if (tagObj && tagObj.rule === 'too-many-rules') {
|
||||||
for (let i = 0; i <= 1000; i++) {
|
for (let i = 0; i <= 1000; i++) {
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
ID = `<ID>foo${i}</ID>`;
|
ID = `<ID>foo${i}</ID>`;
|
||||||
Rule = `${Rule}<Rule>${ID + Status + Filter + Action}</Rule>`;
|
Rule = `${Rule}<Rule>${ID + Status + Filter + Action}</Rule>`;
|
||||||
}
|
}
|
||||||
} else if (tagValue && tagValue.rule === 'key-too-long') {
|
|
||||||
const key = 'a'.repeat(129);
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
Filter = `<Filter><Tag><Key>${key}</Key><Value></Value></Tag></Filter>`;
|
|
||||||
Rule = `${Rule}<Rule>${ID + Status + Filter + Action}</Rule>`;
|
|
||||||
} else {
|
} else {
|
||||||
Rule = '';
|
Rule = '';
|
||||||
}
|
}
|
||||||
return Rule;
|
return Rule;
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateXml(errorTag, tagValue) {
|
function generateXml(errorTag, tagObj) {
|
||||||
let ID;
|
let ID;
|
||||||
let Status;
|
let Status;
|
||||||
let Filter;
|
let Filter;
|
||||||
let Action;
|
let Action;
|
||||||
let Rule;
|
let Rule;
|
||||||
if (errorTag === 'ID') {
|
if (errorTag === 'ID') {
|
||||||
ID = tagValue && tagValue.id ? `<ID>${tagValue.id}</ID>` : '';
|
ID = tagObj && tagObj.id ? `<ID>${tagObj.id}</ID>` : '';
|
||||||
} else {
|
} else {
|
||||||
ID = '<ID>foo</ID>';
|
ID = '<ID>foo</ID>';
|
||||||
}
|
}
|
||||||
if (errorTag === 'Status') {
|
if (errorTag === 'Status') {
|
||||||
Status = tagValue && tagValue.status ?
|
Status = tagObj && tagObj.status ?
|
||||||
`<Status>${tagValue.status}</Status>` : '';
|
`<Status>${tagObj.status}</Status>` : '';
|
||||||
} else {
|
} else {
|
||||||
Status = '<Status>Enabled</Status>';
|
Status = '<Status>Enabled</Status>';
|
||||||
}
|
}
|
||||||
if (errorTag === 'Filter') {
|
if (errorTag === 'Filter') {
|
||||||
Filter = generateFilter(errorTag, tagValue);
|
Filter = generateFilter(errorTag, tagObj);
|
||||||
} else {
|
} else {
|
||||||
Filter = '<Filter></Filter>';
|
Filter = '<Filter></Filter>';
|
||||||
}
|
}
|
||||||
if (errorTag === 'Action') {
|
if (errorTag === 'Action') {
|
||||||
const xmlObj = generateAction(errorTag, tagValue);
|
const xmlObj = generateAction(errorTag, tagObj);
|
||||||
Action = xmlObj.actions;
|
Action = xmlObj.actions;
|
||||||
Filter = xmlObj.filter ? xmlObj.filter : Filter;
|
Filter = xmlObj.filter ? xmlObj.filter : Filter;
|
||||||
} else {
|
} else {
|
||||||
Action = '<Expiration><Days>1</Days></Expiration>';
|
Action = '<Expiration><Days>1</Days></Expiration>';
|
||||||
}
|
}
|
||||||
if (errorTag === 'Rule') {
|
if (errorTag === 'Rule') {
|
||||||
Rule = generateRule(errorTag, tagValue, ID, Status, Filter, Action);
|
Rule = generateRule(errorTag, tagObj, ID, Status, Filter, Action);
|
||||||
} else {
|
} else {
|
||||||
Rule = `<Rule>${ID + Status + Filter + Action}</Rule>`;
|
Rule = `<Rule>${ID + Status + Filter + Action}</Rule>`;
|
||||||
}
|
}
|
||||||
|
@ -173,8 +242,8 @@ function generateXml(errorTag, tagValue) {
|
||||||
return Lifecycle;
|
return Lifecycle;
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateParsedXml(errorTag, tagValue, cb) {
|
function generateParsedXml(errorTag, tagObj, cb) {
|
||||||
const xml = generateXml(errorTag, tagValue);
|
const xml = generateXml(errorTag, tagObj);
|
||||||
parseString(xml, (err, parsedXml) => {
|
parseString(xml, (err, parsedXml) => {
|
||||||
assert.equal(err, null, 'Error parsing xml');
|
assert.equal(err, null, 'Error parsing xml');
|
||||||
cb(parsedXml);
|
cb(parsedXml);
|
||||||
|
@ -182,90 +251,102 @@ function generateParsedXml(errorTag, tagValue, cb) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkError(parsedXml, error, errMessage, cb) {
|
function checkError(parsedXml, error, errMessage, cb) {
|
||||||
const lifecycleConfig =
|
const lcConfig = new LifecycleConfiguration(parsedXml).
|
||||||
new LifecycleConfiguration(parsedXml).
|
|
||||||
getLifecycleConfiguration();
|
getLifecycleConfiguration();
|
||||||
assert.strictEqual(
|
assert.strictEqual(lcConfig.error[error], true);
|
||||||
lifecycleConfig.error[error], true);
|
assert.strictEqual(lcConfig.error.description, errMessage);
|
||||||
assert.strictEqual(
|
|
||||||
lifecycleConfig.error.description, errMessage);
|
|
||||||
cb();
|
cb();
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('LifecycleConfiguration class getLifecycleConfiguration', () => {
|
describe('LifecycleConfiguration class getLifecycleConfiguration', () => {
|
||||||
const tagValue = {};
|
let tagObj;
|
||||||
requiredTags.forEach(tag => {
|
beforeEach(() => {
|
||||||
it(`should return MalformedXML error if ${tag.tag} tag is missing`,
|
tagObj = {};
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return MalformedXML error if request xml is empty', done => {
|
||||||
|
const errMessage = 'request xml is undefined or empty';
|
||||||
|
checkError('', 'MalformedXML', errMessage, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
requiredTags.forEach(t => {
|
||||||
|
it(`should return ${t.error} error if ${t.tag} tag is missing`,
|
||||||
done => {
|
done => {
|
||||||
generateParsedXml(tag.tag, tagValue, parsedXml => {
|
generateParsedXml(t.tag, null, parsedXml => {
|
||||||
checkError(parsedXml, 'MalformedXML', tag.errMessage, done);
|
checkError(parsedXml, t.error, t.errMessage, done);
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
validActions.forEach(action => {
|
|
||||||
it(`should return MalformedXML error if time for ${action.tag} ` +
|
|
||||||
'is not included', done => {
|
|
||||||
tagValue.action = action.tag;
|
|
||||||
generateParsedXml('Action', tagValue, parsedXml => {
|
|
||||||
checkError(parsedXml, 'MalformedXML', action.errMessage, done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
invalidActions.forEach(action => {
|
|
||||||
it(`should return MalformedXML error for invalid action ${action.tag}`,
|
|
||||||
done => {
|
|
||||||
tagValue.action = action.tag;
|
|
||||||
generateParsedXml('Action', tagValue, parsedXml => {
|
|
||||||
checkError(parsedXml, 'MalformedXML', action.errMessage, done);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
notImplementedActions.forEach(action => {
|
notImplementedActions.forEach(action => {
|
||||||
it(`should return NotImplemented error for ${action.tag} action`,
|
const expError = 'NotImplemented';
|
||||||
|
it(`should return ${expError} error for ${action.tag} action`,
|
||||||
done => {
|
done => {
|
||||||
tagValue.action = action.tag;
|
generateParsedXml('Action', action, parsedXml => {
|
||||||
generateParsedXml('Action', tagValue, parsedXml => {
|
checkError(parsedXml, expError, action.errMessage, done);
|
||||||
checkError(parsedXml, 'NotImplemented',
|
|
||||||
action.errMessage, done);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
invalidActions.forEach(a => {
|
||||||
|
it(`should return ${a.error} for ${a.label} action error`,
|
||||||
|
done => {
|
||||||
|
generateParsedXml('Action', a, parsedXml => {
|
||||||
|
checkError(parsedXml, a.error, a.errMessage, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
invalidFilters.forEach(filter => {
|
invalidFilters.forEach(filter => {
|
||||||
it(`should return MalformedXML error for ${filter.tag} filter error`,
|
it(`should return ${filter.error} for ${filter.label} filter error`,
|
||||||
done => {
|
done => {
|
||||||
tagValue.filter = filter.tag;
|
generateParsedXml('Filter', filter, parsedXml => {
|
||||||
generateParsedXml('Filter', tagValue, parsedXml => {
|
checkError(parsedXml, filter.error, filter.errMessage, done);
|
||||||
checkError(parsedXml, 'MalformedXML', filter.errMessage, done);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return MalformedXML error if invalid status', done => {
|
it('should return MalformedXML error if invalid status', done => {
|
||||||
tagValue.status = 'foo';
|
tagObj.status = 'foo';
|
||||||
const errMessage = 'Status is not valid';
|
const errMessage = 'Status is not valid';
|
||||||
generateParsedXml('Status', tagValue, parsedXml => {
|
generateParsedXml('Status', tagObj, parsedXml => {
|
||||||
checkError(parsedXml, 'MalformedXML', errMessage, done);
|
checkError(parsedXml, 'MalformedXML', errMessage, done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return InvalidRequest error if ID not unique', done => {
|
||||||
|
tagObj.rule = 'not-unique-id';
|
||||||
|
const errMessage = 'Rule ID must be unique';
|
||||||
|
generateParsedXml('Rule', tagObj, parsedXml => {
|
||||||
|
checkError(parsedXml, 'InvalidRequest', errMessage, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should return InvalidArgument error if invalid ID', done => {
|
it('should return InvalidArgument error if invalid ID', done => {
|
||||||
tagValue.id = 'a'.repeat(256);
|
tagObj.id = 'a'.repeat(256);
|
||||||
const errMessage = 'Rule ID is greater than 255 characters long';
|
const errMessage = 'Rule ID is greater than 255 characters long';
|
||||||
generateParsedXml('ID', tagValue, parsedXml => {
|
generateParsedXml('ID', tagObj, parsedXml => {
|
||||||
checkError(parsedXml, 'InvalidArgument', errMessage, done);
|
checkError(parsedXml, 'InvalidArgument', errMessage, done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return MalformedXML error if over 1000 rules', done => {
|
it('should return MalformedXML error if over 1000 rules', done => {
|
||||||
tagValue.rule = 'too-many-rules';
|
tagObj.rule = 'too-many-rules';
|
||||||
const errMessage = 'request xml includes over max limit of 1000 rules';
|
const errMessage = 'request xml includes over max limit of 1000 rules';
|
||||||
generateParsedXml('Rule', tagValue, parsedXml => {
|
generateParsedXml('Rule', tagObj, parsedXml => {
|
||||||
checkError(parsedXml, 'MalformedXML', errMessage, done);
|
checkError(parsedXml, 'MalformedXML', errMessage, done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
invalidRequests.forEach(req => {
|
|
||||||
it(`should return InvalidRequest error for ${req.tag}`, done => {
|
it('should use last listed Prefix if multiple Prefixes included', done => {
|
||||||
tagValue.rule = req.tag;
|
tagObj.label = 'mult-prefixes';
|
||||||
generateParsedXml('Rule', tagValue, parsedXml => {
|
tagObj.lastPrefix = 'coco';
|
||||||
checkError(parsedXml, 'InvalidRequest', req.errMessage, done);
|
generateParsedXml('Filter', tagObj, parsedXml => {
|
||||||
});
|
const lcConfig = new LifecycleConfiguration(parsedXml).
|
||||||
|
getLifecycleConfiguration();
|
||||||
|
assert.strictEqual(tagObj.lastPrefix,
|
||||||
|
lcConfig.rules[0].filter.rulePrefix);
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue