Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support for the latest putBucketLifecycle api features #757

Merged
merged 1 commit into from
Mar 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -791,9 +791,24 @@ parameters:
- [id] {String} rule id, if not set, OSS will auto create it with random string.
- prefix {String} store prefix
- status {String} rule status, allow values: `Enabled` or `Disabled`
- [days] {Number|String} expire after the `days`
- [date] {String} expire date, e.g.: `2022-10-11T00:00:00.000Z`
`date` and `days` only set one.
- [expiration] {Object} specifies the expiration attribute of the lifecycle rules for the object.
- [days] {Number|String} expire after the `days`
- [createdBeforeDate] {String} expire date, e.g.: `2022-10-11T00:00:00.000Z`
`createdBeforeDate` and `days` must have one.
- [abortMultipartUpload] {Object} Specifies the expiration attribute of the multipart upload tasks that are not complete.
- [days] {Number|String} expire after the `days`
- [createdBeforeDate] {String} expire date, e.g.: `2022-10-11T00:00:00.000Z`
`createdBeforeDate` and `days` must have one.
- [transition] {Object} Specifies the time when an object is converted to the IA or archive storage class during a valid life cycle.
- storageClass {String} Specifies the storage class that objects that conform to the rule are converted into. allow values: `IA` or `Archive`
- [days] {Number|String} expire after the `days`
- [createdBeforeDate] {String} expire date, e.g.: `2022-10-11T00:00:00.000Z`
`createdBeforeDate` and `days` must have one.
`expiration`、 `abortMultipartUpload`、 `transition` must have one.
- [tag] {Object} Specifies the object tag applicable to a rule. Multiple tags are supported.
- key {String} Indicates the tag key.
- value {String} Indicates the tag value.
`tag` cannot be used with `abortMultipartUpload`
- [options] {Object} optional parameters
- [timeout] {Number} the operation timeout

Expand Down
70 changes: 0 additions & 70 deletions lib/browser/bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,76 +159,6 @@ proto.deleteBucketLogging = async function deleteBucketLogging(name, options) {
};
};

// lifecycle

proto.putBucketLifecycle = async function putBucketLifecycle(name, rules, options) {
// rules: [rule, ...]
// rule: [id], prefix, status, expiration, [days or date]
// status: 'Enabled' or 'Disabled'
const params = this._bucketRequestParams('PUT', name, 'lifecycle', options);
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n<LifecycleConfiguration>\n';
for (let i = 0; i < rules.length; i++) {
const rule = rules[i];
const expiration = rule.days ?
`<Days>${rule.days}</Days>`
:
`<Date>${rule.date}</Date>`;
const id = rule.id ? `<ID>${rule.id}</ID>\n` : '';
xml += ` <Rule>\n${id
} <Prefix>${rule.prefix}</Prefix>\n` +
` <Status>${rule.status}</Status>\n` +
` <Expiration>${expiration}</Expiration>\n` +
' </Rule>\n';
}
xml += '</LifecycleConfiguration>';
params.content = xml;
params.mime = 'xml';
params.successStatuses = [200];
const result = await this.request(params);
return {
res: result.res
};
};

proto.getBucketLifecycle = async function getBucketLifecycle(name, options) {
const params = this._bucketRequestParams('GET', name, 'lifecycle', options);
params.successStatuses = [200];
params.xmlResponse = true;
const result = await this.request(params);
let rules = result.data.Rule || null;
if (rules) {
if (!isArray(rules)) {
rules = [rules];
}
rules = rules.map((rule) => {
const item = {
id: rule.ID,
prefix: rule.Prefix,
status: rule.Status
};
if (rule.Expiration.Days) {
item.days = rule.Expiration.Days;
} else {
item.date = rule.Expiration.Date;
}
return item;
});
}
return {
rules,
res: result.res
};
};

proto.deleteBucketLifecycle = async function deleteBucketLifecycle(name, options) {
const params = this._bucketRequestParams('DELETE', name, 'lifecycle', options);
params.successStatuses = [204];
const result = await this.request(params);
return {
res: result.res
};
};

proto.putBucketCORS = async function putBucketCORS(name, rules, options) {
rules = rules || [];
assert(rules.length, 'rules is required');
Expand Down
5 changes: 4 additions & 1 deletion lib/browser/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,10 @@ merge(proto, require('../common/bucket/getBucketWebsite'));
merge(proto, require('../common/bucket/putBucketWebsite'));
merge(proto, require('../common/bucket/deleteBucketWebsite'));

// merge(proto, require('./bucket'));
// lifecycle
merge(proto, require('../common/bucket/getBucketLifecycle'));
merge(proto, require('../common/bucket/putBucketLifecycle'));
merge(proto, require('../common/bucket/deleteBucketLifecycle'));


// multipart upload
Expand Down
73 changes: 0 additions & 73 deletions lib/bucket.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,79 +186,6 @@ proto.deleteBucketLogging = async function deleteBucketLogging(name, options) {
};
};

// lifecycle

proto.putBucketLifecycle = async function putBucketLifecycle(name, rules, options) {
this._checkBucketName(name);
// rules: [rule, ...]
// rule: [id], prefix, status, expiration, [days or date]
// status: 'Enabled' or 'Disabled'
const params = this._bucketRequestParams('PUT', name, 'lifecycle', options);
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n<LifecycleConfiguration>\n';
for (let i = 0; i < rules.length; i++) {
const rule = rules[i];
const expiration = rule.days ?
`<Days>${rule.days}</Days>`
:
`<Date>${rule.date}</Date>`;
const id = rule.id ? `<ID>${rule.id}</ID>\n` : '';
xml += ` <Rule>\n${id
} <Prefix>${rule.prefix}</Prefix>\n` +
` <Status>${rule.status}</Status>\n` +
` <Expiration>${expiration}</Expiration>\n` +
' </Rule>\n';
}
xml += '</LifecycleConfiguration>';
params.content = xml;
params.mime = 'xml';
params.successStatuses = [200];
const result = await this.request(params);
return {
res: result.res
};
};

proto.getBucketLifecycle = async function getBucketLifecycle(name, options) {
this._checkBucketName(name);
const params = this._bucketRequestParams('GET', name, 'lifecycle', options);
params.successStatuses = [200];
params.xmlResponse = true;
const result = await this.request(params);
let rules = result.data.Rule || null;
if (rules) {
if (!isArray(rules)) {
rules = [rules];
}
rules = rules.map((rule) => {
const item = {
id: rule.ID,
prefix: rule.Prefix,
status: rule.Status
};
if (rule.Expiration.Days) {
item.days = rule.Expiration.Days;
} else {
item.date = rule.Expiration.Date;
}
return item;
});
}
return {
rules,
res: result.res
};
};

proto.deleteBucketLifecycle = async function deleteBucketLifecycle(name, options) {
this._checkBucketName(name);
const params = this._bucketRequestParams('DELETE', name, 'lifecycle', options);
params.successStatuses = [204];
const result = await this.request(params);
return {
res: result.res
};
};

proto.putBucketCORS = async function putBucketCORS(name, rules, options) {
this._checkBucketName(name);
rules = rules || [];
Expand Down
11 changes: 11 additions & 0 deletions lib/common/bucket/deleteBucketLifecycle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const proto = exports;

proto.deleteBucketLifecycle = async function deleteBucketLifecycle(name, options) {
this._checkBucketName(name);
const params = this._bucketRequestParams('DELETE', name, 'lifecycle', options);
params.successStatuses = [204];
const result = await this.request(params);
return {
res: result.res
};
};
33 changes: 33 additions & 0 deletions lib/common/bucket/getBucketLifecycle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const isArray = require('../utils/isArray');
const formatObjKey = require('../utils/formatObjKey');

const proto = exports;

proto.getBucketLifecycle = async function getBucketLifecycle(name, options) {
this._checkBucketName(name);
const params = this._bucketRequestParams('GET', name, 'lifecycle', options);
params.successStatuses = [200];
params.xmlResponse = true;
const result = await this.request(params);
let rules = result.data.Rule || null;
if (rules) {
if (!isArray(rules)) {
rules = [rules];
}
rules = rules.map((_) => {
if (_.ID) {
_.id = _.ID;
delete _.ID;
}
if (_.Tag && !isArray(_.Tag)) {
_.Tag = [_.Tag];
}
return formatObjKey(_, 'firstLowerCase');
});
}
return {
rules,
res: result.res
};
};

3 changes: 3 additions & 0 deletions lib/common/bucket/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ merge(proto, require('./_checkBucketName'));
merge(proto, require('./getBucketWebsite'));
merge(proto, require('./putBucketWebsite'));
merge(proto, require('./deleteBucketWebsite'));
merge(proto, require('./getBucketLifecycle'));
merge(proto, require('./putBucketLifecycle'));
merge(proto, require('./deleteBucketLifecycle'));
122 changes: 122 additions & 0 deletions lib/common/bucket/putBucketLifecycle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/* eslint-disable no-use-before-define */

const isArray = require('../utils/isArray');
const deepCopy = require('../utils/deepCopy');
const isObject = require('../utils/isObject');
const obj2xml = require('../utils/obj2xml');
const checkObjectTag = require('../utils/checkObjectTag');
const getStrBytesCount = require('../utils/getStrBytesCount');

const proto = exports;


proto.putBucketLifecycle = async function putBucketLifecycle(name, rules, options) {
this._checkBucketName(name);

if (!isArray(rules)) {
throw new Error('rules must be Array');
}

const params = this._bucketRequestParams('PUT', name, 'lifecycle', options);
const Rule = [];
const paramXMLObj = {
LifecycleConfiguration: {
Rule
}
};

rules.forEach((_) => {
defaultDaysAndDate2Expiration(_); // todo delete, 兼容旧版本
checkRule(_);
if (_.id) {
_.ID = _.id;
delete _.id;
}
Rule.push(_);
});

const paramXML = obj2xml(paramXMLObj, {
headers: true,
firstUpperCase: true
});

params.content = paramXML;
params.mime = 'xml';
params.successStatuses = [200];
const result = await this.request(params);
return {
res: result.res
};
};

// todo delete, 兼容旧版本
function defaultDaysAndDate2Expiration(obj) {
if (obj.days) {
obj.expiration = {
days: obj.days
};
}
if (obj.date) {
obj.expiration = {
createdBeforeDate: obj.date
};
}
}

function checkDaysAndDate(obj, key) {
const { days, createdBeforeDate } = obj;
if (!days && !createdBeforeDate) {
throw new Error(`${key} must includes days or createdBeforeDate`);
} else if (days && !/^[1-9][0-9]*$/.test(days)) {
throw new Error('days must be a positive integer');
} else if (createdBeforeDate && !/\d{4}-\d{2}-\d{2}T00:00:00.000Z/.test(createdBeforeDate)) {
throw new Error('createdBeforeDate must be date and conform to iso8601 format');
}
}

function handleCheckTag(tag) {
if (!isArray(tag) && !isObject(tag)) {
throw new Error('tag must be Object or Array');
}
tag = isObject(tag) ? [tag] : tag;
const tagObj = {};
const tagClone = deepCopy(tag);
tagClone.forEach((v) => {
tagObj[v.key] = v.value;
});

checkObjectTag(tagObj);
}

function checkRule(rule) {
if (rule.id && getStrBytesCount(rule.id) > 255) throw new Error('ID is composed of 255 bytes at most');

if (rule.prefix === '' || rule.prefix === undefined) throw new Error('Rule must includes prefix');

if (!['Enabled', 'Disabled'].includes(rule.status)) throw new Error('Status must be Enabled or Disabled');

if (rule.transition) {
if (!['IA', 'Archive'].includes(rule.transition.storageClass)) throw new Error('StorageClass must be IA or Archive');
checkDaysAndDate(rule.transition, 'Transition');
}

if (rule.expiration) {
checkDaysAndDate(rule.expiration, 'Expiration');
}

if (rule.abortMultipartUpload) {
checkDaysAndDate(rule.abortMultipartUpload, 'AbortMultipartUpload');
}

if (!rule.expiration && !rule.abortMultipartUpload && !rule.transition) {
throw new Error('Rule must includes expiration or abortMultipartUpload or transition');
}

if (rule.tag) {
if (rule.abortMultipartUpload) {
throw new Error('Tag cannot be used with abortMultipartUpload');
}
handleCheckTag(rule.tag);
}
}

Loading