Skip to content

Commit

Permalink
Merge pull request #276 from hasonmsft/dev
Browse files Browse the repository at this point in the history
Storage Library 2.1.0 release
  • Loading branch information
vinjiang committed Mar 2, 2017
2 parents d7d4223 + 2e72958 commit d84dff9
Show file tree
Hide file tree
Showing 12 changed files with 164 additions and 14 deletions.
15 changes: 7 additions & 8 deletions .travis.yml
@@ -1,20 +1,19 @@
language: node_js
node_js:
- "4.1"
- "4.0"
- "0.12"
- "0.10"
- "4"
- "5"
- "6"
- "7"

after_script:
- npm run coveralls

install:
- npm install -g npm@1.4.23
- npm --version
- npm install

script:
# `nsp check` doesn't work on NodeJS 0.8*. Don't run `nsp check` against 0.8*
- case ${TRAVIS_NODE_VERSION} in 0.8*) npm run testwithoutcheck;; *) npm test;; esac
allow_failures:
- node_js: "5"
- node_js: "7"

sudo: false
14 changes: 14 additions & 0 deletions ChangeLog.md
@@ -1,6 +1,20 @@
Note: This is an Azure Storage only package. The all up Azure node sdk still has the old storage bits in there. In a future release, those storage bits will be removed and an npm dependency to this storage node sdk will
be taken. This is a GA release and the changes described below indicate the changes from the Azure node SDK 0.9.8 available here - https://github.com/Azure/azure-sdk-for-node.

2017.03 Version 2.1.0

ALL
* Fixed the type script issue that AccessConditions is missing in the type definition file

BLOB
* Added support for page blob incremental copy. Refer to https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/incremental-copy-blob

QUEUE
* Fixed the issue that `responseObject` may not have response body in `createMessage` function.

BROWSER
* Generated browser compatible JavaScript files based on Microsoft Azure Storage SDK for Node.js 2.1.0

2017.01 Version 2.0.0

ALL
Expand Down
4 changes: 4 additions & 0 deletions browser/ChangeLog.md
@@ -1,5 +1,9 @@
Note: This is the change log file for Azure Storage JavaScript Client Library.

2017.03 Version 0.2.1-preview.2

* Generated browser compatible JavaScript files based on Microsoft Azure Storage SDK for Node.js 2.1.0

2017.03 Version 0.2.0-preview.1

* Generated browser compatible JavaScript files based on Microsoft Azure Storage SDK for Node.js 2.0.0
Expand Down
12 changes: 11 additions & 1 deletion lib/common/util/constants.js
Expand Up @@ -31,7 +31,7 @@ var Constants = {
/*
* Specifies the value to use for UserAgent header.
*/
USER_AGENT_PRODUCT_VERSION: '2.0.0',
USER_AGENT_PRODUCT_VERSION: '2.1.0',

/**
* The number of default concurrent requests for parallel operation.
Expand Down Expand Up @@ -1059,6 +1059,16 @@ var Constants = {
*/
COPY_ACTION: 'x-ms-copy-action',

/**
* Flag if the blob is incremental copy blob.
*/
INCREMENTAL_COPY: 'x-ms-incremental-copy',

/**
* Snapshot time of the last successful incremental copy snapshot for this blob.
*/
COPY_DESTINATION_SNAPSHOT: 'x-ms-copy-destination-snapshot',

/**
* The ContentID header.
*
Expand Down
4 changes: 3 additions & 1 deletion lib/common/util/util.js
Expand Up @@ -565,7 +565,9 @@ var normalizePropertyNameExceptionList = {
'copyProgress': 'copy.progress',
'copyCompletionTime': 'copy.completionTime',
'copyStatusDescription': 'copy.statusDescription',
'publicAccess': 'publicAccessLevel'
'copyDestinationSnapshot': 'copy.destinationSnapshot',
'publicAccess': 'publicAccessLevel',
'incrementalCopy': 'isIncrementalCopy'
};

/**
Expand Down
10 changes: 10 additions & 0 deletions lib/services/blob/blobservice.js
Expand Up @@ -1545,6 +1545,7 @@ BlobService.prototype.setBlobMetadata = function (container, blob, metadata, opt

/**
* Downloads a blob into a file.
* (Not recommended for use in the JavaScript Client Library)
*
* @this {BlobService}
* @param {string} container The container name.
Expand Down Expand Up @@ -2071,6 +2072,7 @@ BlobService.prototype.createBlobSnapshot = function (container, blob, optionsOrC
* @param {string} targetContainer The target container name.
* @param {string} targetBlob The target blob name.
* @param {object} [options] The request options.
* @param {boolean} [options.isIncrementalCopy] If it's incremental copy or not. Refer to https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/incremental-copy-blob
* @param {string} [options.snapshotId] The source blob snapshot identifier.
* @param {object} [options.metadata] The target blob metadata key/value pairs.
* @param {string} [options.leaseId] The target blob lease identifier.
Expand Down Expand Up @@ -2123,6 +2125,10 @@ BlobService.prototype.startCopyBlob = function (sourceUri, targetContainer, targ
var webResource = WebResource.put(targetResourceName)
.withHeader(HeaderConstants.COPY_SOURCE, sourceUri);

if (options.isIncrementalCopy) {
webResource.withQueryOption(QueryStringConstants.COMP, 'incrementalcopy');
}

webResource.withHeader(HeaderConstants.LEASE_ID, options.leaseId);
webResource.withHeader(HeaderConstants.SOURCE_LEASE_ID, options.sourceLeaseId);
webResource.addOptionalMetadataHeaders(options.metadata);
Expand Down Expand Up @@ -2399,6 +2405,7 @@ BlobService.prototype.createPageBlob = function (container, blob, length, option
/**
* Uploads a page blob from file. If the blob already exists on the service, it will be overwritten.
* To avoid overwriting and instead throw an error if the blob exists, please pass in an accessConditions parameter in the options object.
* (Not recommended for use in the JavaScript Client Library)
*
* @this {BlobService}
* @param {string} container The container name.
Expand Down Expand Up @@ -3038,6 +3045,7 @@ BlobService.prototype.setPageBlobSequenceNumber = function (container, blob, seq
/**
* Creates a new block blob. If the blob already exists on the service, it will be overwritten.
* To avoid overwriting and instead throw an error if the blob exists, please pass in an accessConditions parameter in the options object.
* (Not recommended for use in the JavaScript Client Library)
*
* @this {BlobService}
* @param {string} container The container name.
Expand Down Expand Up @@ -3612,6 +3620,7 @@ BlobService.prototype.createOrReplaceAppendBlob = function (container, blob, opt
* This API should be used strictly in a single writer scenario because the API internally uses the append-offset conditional header to avoid duplicate blocks.
* If you are guaranteed to have a single writer scenario, please look at options.absorbConditionalErrorsOnRetry and see if setting this flag to true is acceptable for you.
* If you want to append data to an already existing blob, please look at appendFromLocalFile.
* (Not recommended for use in the JavaScript Client Library)
*
* @this {BlobService}
* @param {string} container The container name.
Expand Down Expand Up @@ -3836,6 +3845,7 @@ BlobService.prototype.createWriteStreamToExistingAppendBlob = function (containe
* Appends to an append blob from a local file. Assumes the blob already exists on the service.
* This API should be used strictly in a single writer scenario because the API internally uses the append-offset conditional header to avoid duplicate blocks.
* If you are guaranteed to have a single writer scenario, please look at options.absorbConditionalErrorsOnRetry and see if setting this flag to true is acceptable for you.
* (Not recommended for use in the JavaScript Client Library)
*
* @this {BlobService}
* @param {string} container The container name.
Expand Down
16 changes: 15 additions & 1 deletion lib/services/blob/models/blobresult.js
Expand Up @@ -34,6 +34,7 @@ var HeaderConstants = Constants.HeaderConstants;
* @property {string} lastModified The date/time that the blob was last modified.
* @property {string} contentLength The size of the blob in bytes.
* @property {string} blobType The blob type.
* @property {boolean} isIncrementalCopy If the blob is incremental copy blob.
* @property {string} requestId The request id.
* @property {string} sequenceNumber The current sequence number for a page blob.
* @property {string} contentRange The content range.
Expand All @@ -56,6 +57,7 @@ var HeaderConstants = Constants.HeaderConstants;
* @property {string} copy.status The copy status.
* @property {string} copy.completionTime The copy completion time.
* @property {string} copy.statusDescription The copy status description.
* @property {string} copy.destinationSnapshot The snapshot time of the last successful incremental copy snapshot for this blob.
* @property {string} copy.progress The copy progress.
* @property {string} copy.source The copy source.
*
Expand Down Expand Up @@ -91,6 +93,11 @@ BlobResult.parse = function (blobXml) {
}
}

// convert isIncrementalCopy to boolean type
if (blobResult.isIncrementalCopy !== undefined) {
blobResult.isIncrementalCopy = (blobResult.isIncrementalCopy == 'true');
}

return blobResult;
};

Expand All @@ -111,6 +118,8 @@ var headersForProperties = {
'getContentMd5': 'RANGE_GET_CONTENT_MD5',
'acceptRanges': 'ACCEPT_RANGES',
'appendOffset': 'BLOB_APPEND_OFFSET',

'isIncrementalCopy': 'INCREMENTAL_COPY',

// ContentSettings
'contentSettings.contentType': 'CONTENT_TYPE',
Expand All @@ -132,7 +141,8 @@ var headersForProperties = {
'copy.source': 'COPY_SOURCE',
'copy.progress': 'COPY_PROGRESS',
'copy.completionTime': 'COPY_COMPLETION_TIME',
'copy.statusDescription': 'COPY_STATUS_DESCRIPTION'
'copy.statusDescription': 'COPY_STATUS_DESCRIPTION',
'copy.destinationSnapshot': 'COPY_DESTINATION_SNAPSHOT'
};

BlobResult.prototype.getPropertiesFromHeaders = function (headers) {
Expand All @@ -159,6 +169,10 @@ BlobResult.prototype.getPropertiesFromHeaders = function (headers) {
setBlobPropertyFromHeaders(property, header);
});

// convert isIncrementalCopy to boolean type
if (self.isIncrementalCopy !== undefined) {
self.isIncrementalCopy = (self.isIncrementalCopy == 'true');
}
};

/**
Expand Down
2 changes: 2 additions & 0 deletions lib/services/file/fileservice.js
Expand Up @@ -2269,6 +2269,7 @@ FileService.prototype.getFileToText = function (share, directory, file, optionsO

/**
* Downloads an Azure file into a file.
* (Not recommended for use in the JavaScript Client Library)
*
* @this {FileService}
* @param {string} share The share name.
Expand Down Expand Up @@ -2774,6 +2775,7 @@ FileService.prototype.createFileFromText = function (share, directory, file, tex

/**
* Uploads a file to storage from a local file. If the file already exists on the service, it will be overwritten.
* (Not recommended for use in the JavaScript Client Library)
*
* @this {FileService}
* @param {string} share The share name.
Expand Down
2 changes: 1 addition & 1 deletion lib/services/queue/queueservice.js
Expand Up @@ -731,7 +731,7 @@ QueueService.prototype.createMessage = function (queue, messageText, optionsOrCa
var processResponseCallback = function (responseObject, next) {
responseObject.queueMessageResults = [];

if (responseObject.response.body.QueueMessagesList && responseObject.response.body.QueueMessagesList.QueueMessage) {
if (responseObject.response && responseObject.response.body && responseObject.response.body.QueueMessagesList && responseObject.response.body.QueueMessagesList.QueueMessage) {
var messages = responseObject.response.body.QueueMessagesList.QueueMessage;

if (!_.isArray(messages)) {
Expand Down
3 changes: 1 addition & 2 deletions package.json
@@ -1,7 +1,7 @@
{
"name": "azure-storage",
"author": "Microsoft Corporation",
"version": "2.0.0",
"version": "2.1.0",
"description": "Microsoft Azure Storage Client Library for Node.js",
"typings": "typings/azure-storage/azure-storage.d.ts",
"tags": [
Expand Down Expand Up @@ -69,7 +69,6 @@
"scripts": {
"check": "jshint lib && nsp check",
"test": "jshint lib && nsp check && mocha --no-timeouts --recursive test",
"testwithoutcheck": "jshint lib && mocha --no-timeouts --recursive test",
"cover": "istanbul cover ./node_modules/mocha/bin/_mocha -- -R spec -u bdd --no-timeouts --recursive test",
"coveralls": "npm run cover && cat ./coverage/lcov.info | node ./node_modules/coveralls/bin/coveralls.js",
"genjs": "node ./browser/bundle.js"
Expand Down
81 changes: 81 additions & 0 deletions test/services/blob/blobservice-tests.js
Expand Up @@ -1121,6 +1121,87 @@ describe('BlobService', function () {
});
});
});

runOrSkip('incremental copy should work', function(done) {
var sourceContainerName = testutil.generateId(containerNamesPrefix, containerNames, suite.isMocked);
var targetContainerName = testutil.generateId(containerNamesPrefix, containerNames, suite.isMocked);

var sourceBlobName = testutil.generateId(blobNamesPrefix, blobNames, suite.isMocked);
var targetBlobName = testutil.generateId(blobNamesPrefix, blobNames, suite.isMocked);

blobService.createContainer(sourceContainerName, function (err) {
assert.equal(err, null);

blobService.createContainer(targetContainerName, function (err) {
assert.equal(err, null);

blobService.createPageBlob(sourceContainerName, sourceBlobName, 1024, function(err) {
assert.equal(err, null);

blobService.createBlobSnapshot(sourceContainerName, sourceBlobName, function (snapshotError, snapshotId, snapshotResponse) {
assert.equal(snapshotError, null);
assert.notEqual(snapshotResponse, null);
assert.notEqual(snapshotId, null);

var startDate = new Date();
var expiryDate = new Date(startDate);
expiryDate.setMinutes(startDate.getMinutes() + 100);
startDate.setMinutes(startDate.getMinutes() - 100);

var sourceSAS = blobService.generateSharedAccessSignature(sourceContainerName, sourceBlobName, {
AccessPolicy: {
Permissions: BlobUtilities.SharedAccessPermissions.READ,
Start: startDate,
Expiry: expiryDate
}
});

blobService.startCopyBlob(blobService.getUrl(sourceContainerName, sourceBlobName, sourceSAS), targetContainerName, targetBlobName, {isIncrementalCopy: true, 'snapshotId': snapshotId}, function(err, result){
assert.equal(err, null);
assert.notEqual(result, null);
assert.notEqual(result.copy, null);
assert.notEqual(result.copy.id, null);
assert.notEqual(result.copy.status, null);

setTimeout(function() {
blobService.listBlobsSegmented(targetContainerName, null, {include: 'snapshots'}, function(err, result) {
assert.equal(err, null);
// 1 base incremental copy blob + 1 snapshot
assert.equal(result.entries.length, 2);

var incrementalCopyBlob = result.entries.find(function(b) {
return b.snapshot === undefined;
});
var incrementalCopyBlobSnapshot = result.entries.find(function(b) {
return b.snapshot !== undefined;
});

assert.equal(incrementalCopyBlob.isIncrementalCopy, true);
assert.notEqual(incrementalCopyBlob.copy.destinationSnapshot, null);

blobService.getBlobProperties(targetContainerName, targetBlobName, function(err, result) {
assert.equal(err, null);
assert.equal(result.isIncrementalCopy, true);
assert.notEqual(result.copy.destinationSnapshot, null);

// base incremental blob cannot be read
blobService.getBlobToText(targetContainerName, targetBlobName, { rangeStart: 1, rangeEnd: 2}, function(err) {
assert.notEqual(err, null);

blobService.getBlobToText(targetContainerName, targetBlobName, { rangeStart: 1, rangeEnd: 2, snapshotId: incrementalCopyBlobSnapshot.snapshot}, function(err) {
assert.equal(err, null);
done();
});
});
});
});
}, 10000); // Wait for incremental copy to complete
});
});
});
});
});
});
});

describe('shared access signature', function () {
Expand Down
15 changes: 15 additions & 0 deletions typings/azure-storage/azure-storage.d.ts
Expand Up @@ -2611,6 +2611,7 @@ declare module azurestorage {
lastModified: string;
contentLength: string;
blobType: string;
isIncrementalCopy?: boolean;
requestId: string;
sequenceNumber?: string;
contentRange?: string;
Expand All @@ -2636,6 +2637,7 @@ declare module azurestorage {
status?: string;
completionTime?: string;
statusDescription?: string;
destinationSnapshot?: string;
progress?: string;
source?: string;
},
Expand Down Expand Up @@ -2698,6 +2700,7 @@ declare module azurestorage {
sourceLeaseId?: string;
accessConditions?: AccessConditions;
sourceAccessConditions?: AccessConditions;
isIncrementalCopy?: boolean;
}

export interface DeleteBlobRequestOptions extends BlobRequestOptions {
Expand Down Expand Up @@ -8900,10 +8903,22 @@ declare module azurestorage {
SequenceNumberEqual?: Number | string;
MaxBlobSize?: Number | string;
MaxAppendPosition?: Number | string;

generateEmptyCondition() : AccessConditions;
generateIfNotExistsCondition(): AccessConditions;
generateIfExistsCondition(): AccessConditions;
generateIfNoneMatchCondition(etag: string) : AccessConditions;
generateIfMatchCondition(etag: string) : AccessConditions;
generateIfModifiedSinceCondition(time: Date|string) : AccessConditions;
generateIfNotModifiedSinceCondition(time: Date|string) : AccessConditions;
generateSequenceNumberEqualCondition(sequenceNumber: Number|string) : AccessConditions;
generateSequenceNumberLessThanCondition(sequenceNumber: Number|string) : AccessConditions;
generateSequenceNumberLessThanOrEqualCondition(sequenceNumber: Number|string) : AccessConditions;
}

export import Constants = common.util.constants;
export import StorageUtilities = common.util.storageutilities;
export var AccessConditions : AccessConditions;
export import SR = common.util.sr.SR;
export import StorageServiceClient = common.services.storageserviceclient.StorageServiceClient;
export import Logger = common.diagnostics.logger.Logger;
Expand Down

0 comments on commit d84dff9

Please sign in to comment.