Compare commits

...

2 Commits

Author SHA1 Message Date
Dora Korpar 77836d57c5 check s3 tests work 2018-03-12 15:40:43 -07:00
Dora Korpar e5fe2d2b7f bf: minor lifecycle fixes 2018-03-12 14:57:23 -07:00
4 changed files with 274 additions and 170 deletions

View File

@ -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."

View File

@ -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 { key, val } = t; const keysVals = tags.map(t => {
const Tag = `<Tag><Key>${key}</Key>` + const { key, val } = t;
const Tag = `<Key>${key}</Key>` +
`<Value>${val}</Value>`;
return Tag;
}).join('');
tagXML = `<Tags>${keysVals}</Tags>`;
} else {
// only one tag included
const { key, val } = tags[0];
tagXML = `<Tag><Key>${key}</Key>` +
`<Value>${val}</Value></Tag>`; `<Value>${val}</Value></Tag>`;
return Tag; }
}).join('');
} }
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') {

View File

@ -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": {

View File

@ -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();
}); });
}); });
}); });