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

fix async patch and post LRO #875

Merged
merged 15 commits into from
Apr 30, 2016
Original file line number Diff line number Diff line change
Expand Up @@ -1485,4 +1485,4 @@ LRORetrys.prototype.beginPostAsyncRelativeRetrySucceeded = function (options, ca
};


module.exports = LRORetrys;
module.exports = LRORetrys;
Original file line number Diff line number Diff line change
Expand Up @@ -7787,4 +7787,4 @@ LROs.prototype.beginPostAsyncRetrycanceled = function (options, callback) {
};


module.exports = LROs;
module.exports = LROs;
Original file line number Diff line number Diff line change
Expand Up @@ -931,4 +931,4 @@ LROsCustomHeader.prototype.beginPostAsyncRetrySucceeded = function (options, cal
};


module.exports = LROsCustomHeader;
module.exports = LROsCustomHeader;
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,7 @@ public string LongRunningOperationMethodNameInRuntime
string result = null;
if (this.IsLongRunningOperation)
{
if (HttpMethod == HttpMethod.Post || HttpMethod == HttpMethod.Delete)
{
result = "getPostOrDeleteOperationResult";
}
else if (HttpMethod == HttpMethod.Put || HttpMethod == HttpMethod.Patch)
{
result = "getPutOrPatchOperationResult";
}
result = "getLongRunningOperationResult";
}
return result;
}
Expand Down
204 changes: 73 additions & 131 deletions ClientRuntimes/NodeJS/ms-rest-azure/lib/azureServiceClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,92 +57,32 @@ function AzureServiceClient(credentials, options) {
util.inherits(AzureServiceClient, msRest.ServiceClient);

/**
* Poll Azure long running PUT operation.
* Poll Azure long running PUT or PATCH operation. (Deprecated, new version of the code-gen will generate code to call getLongRunningOperationResult)
* @param {object} [resultOfInitialRequest] - Response of the initial request for the long running operation.
* @param {object} [options]
* @param {object} [options.customHeaders] headers that will be added to request
*/
AzureServiceClient.prototype.getPutOrPatchOperationResult = function (resultOfInitialRequest, options, callback) {
var self = this;

if(!callback && typeof options === 'function') {
callback = options;
options = null;
}
if (!callback) {
throw new Error('Missing callback');
}

if (!resultOfInitialRequest) {
return callback(new Error('Missing resultOfInitialRequest parameter'));
}

if (resultOfInitialRequest.response.statusCode !== 200 &&
resultOfInitialRequest.response.statusCode !== 201 &&
resultOfInitialRequest.response.statusCode !== 202) {
return callback(new Error(util.format('Unexpected polling status code from long running operation \'%s\'',
resultOfInitialRequest.response.statusCode)));
}
var pollingState = null;
try {
pollingState = new PollingState(resultOfInitialRequest, this.longRunningOperationRetryTimeout);
} catch (error) {
callback(error);
}

var resourceUrl = resultOfInitialRequest.request.url;
this._options = options;

async.whilst(
//while condition
function () {
var finished = [LroStates.Succeeded, LroStates.Failed, LroStates.Canceled].some(function (e) {
return pollingState.status === e;
});
return !finished;
},
//while loop body
function (callback) {
setTimeout(function () {
if (pollingState.azureAsyncOperationHeaderLink) {
self._updateStateFromAzureAsyncOperationHeader(pollingState, false, function (err) {
return callback(err);
});
} else if (pollingState.locationHeaderLink) {
self._updateStateFromLocationHeaderOnPut(pollingState, function (err) {
return callback(err);
});
} else {
self._updateStateFromGetResourceOperation(resourceUrl, pollingState, function (err) {
return callback(err);
});
}
}, pollingState.getTimeout());
},
//when done
function (err) {
if (pollingState.status === LroStates.Succeeded) {
if (!pollingState.resource) {
self._updateStateFromGetResourceOperation(resourceUrl, pollingState, function (err) {
return callback(err, pollingState.getOperationResponse());
});
} else {
return callback(null, pollingState.getOperationResponse());
}
} else {
return callback(pollingState.getCloudError(err));
}
});
return this.getLongRunningOperationResult(resultOfInitialRequest, options, callback);
};


/**
* Poll Azure long running POST or DELETE operations.
* Poll Azure long running POST or DELETE operations. (Deprecated, new version of the code-gen will generate code to call getLongRunningOperationResult)
* @param {object} [resultOfInitialRequest] - result of the initial request.
* @param {object} [options]
* @param {object} [options.customHeaders] headers that will be added to request
*/
AzureServiceClient.prototype.getPostOrDeleteOperationResult = function (resultOfInitialRequest, options, callback) {
return this.getLongRunningOperationResult(resultOfInitialRequest, options, callback);
};

/**
* Poll Azure long running PUT, PATCH, POST or DELETE operations.
* @param {object} [resultOfInitialRequest] - result of the initial request.
* @param {object} [options]
* @param {object} [options.customHeaders] headers that will be added to request
*/
AzureServiceClient.prototype.getLongRunningOperationResult = function (resultOfInitialRequest, options, callback) {
var self = this;

if (!callback && typeof options === 'function') {
Expand All @@ -152,7 +92,7 @@ AzureServiceClient.prototype.getPostOrDeleteOperationResult = function (resultOf
if (!callback) {
throw new Error('Missing callback');
}

if (!resultOfInitialRequest) {
return callback(new Error('Missing resultOfInitialRequest parameter'));
}
Expand All @@ -161,52 +101,88 @@ AzureServiceClient.prototype.getPostOrDeleteOperationResult = function (resultOf
return callback(new Error('Missing resultOfInitialRequest.response'));
}

if (resultOfInitialRequest.response.statusCode !== 200 &&
resultOfInitialRequest.response.statusCode !== 202 &&
resultOfInitialRequest.response.statusCode !== 204) {
return callback(new Error(util.format('Unexpected polling status code from long running operation \'%s\'',
resultOfInitialRequest.response.statusCode)));
if (!resultOfInitialRequest.request) {
return callback(new Error('Missing resultOfInitialRequest.request'));
}

if (!resultOfInitialRequest.request.method) {
return callback(new Error('Missing resultOfInitialRequest.request.method'));
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@csmengwan Inside the "_checkInitialRequestResponseStatusCodeFailed" method, we are using resultOfInitialResponse.request. We should check that this value is not null before using it, by adding the check here. Note that if resultOfInitialRequest.request were null before, it would not have caused an error in the old code.

var initialRequestMethod = resultOfInitialRequest.request.method;

if (this._checkResponseStatusCodeFailed(resultOfInitialRequest)) {
return callback(new Error(util.format('Unexpected polling status code from long running operation \'%s\' for method \'%s\'',
resultOfInitialRequest.response.statusCode,
initialRequestMethod)));
}

var pollingState = null;

try {
pollingState = new PollingState(resultOfInitialRequest, this.longRunningOperationRetryTimeout);
} catch (error) {
callback(error);
}
var resourceUrl = resultOfInitialRequest.request.url;
this._options = options;

async.whilst(
function () {
var finished = [LroStates.Succeeded, LroStates.Failed, LroStates.Canceled].some(function (e) {
function() {
var finished = [LroStates.Succeeded, LroStates.Failed, LroStates.Canceled].some(function(e) {
return e === pollingState.status;
});
return !finished;
},
function (callback) {
setTimeout(function () {
setTimeout(function() {
if (pollingState.azureAsyncOperationHeaderLink) {
self._updateStateFromAzureAsyncOperationHeader(pollingState, true, function (err) {
self._updateStateFromAzureAsyncOperationHeader(pollingState, true, function(err) {
return callback(err);
});
} else if (pollingState.locationHeaderLink) {
self._updateStateFromLocationHeaderOnPostOrDelete(pollingState, function (err) {
self._updateStateFromLocationHeader(initialRequestMethod, pollingState, function(err) {
return callback(err);
});
} else if (initialRequestMethod === 'PUT') {
self._updateStateFromGetResourceOperation(resourceUrl, pollingState, function(err) {
return callback(err);
});
} else {
return callback(new Error('Location header is missing from long running operation.'));
}
}, pollingState.getTimeout());
},
//when done
function (err) {
if (pollingState.status === LroStates.Succeeded ) {
return callback(null, pollingState.getOperationResponse());
if (pollingState.status === LroStates.Succeeded) {
if ((pollingState.azureAsyncOperationHeaderLink || !pollingState.resource) &&
(initialRequestMethod === 'PUT' || initialRequestMethod === 'PATCH')) {
self._updateStateFromGetResourceOperation(resourceUrl, pollingState, function(err) {
return callback(err, pollingState.getOperationResponse());
});
} else {
return callback(null, pollingState.getOperationResponse());
}
} else {
return callback(pollingState.getCloudError(err));
}
});
};

AzureServiceClient.prototype._checkResponseStatusCodeFailed = function (initialRequest) {
var statusCode = initialRequest.response.statusCode;
var method = initialRequest.request.method;
if (statusCode === 200 || statusCode === 202 ||
(statusCode === 201 && method === 'PUT') ||
(statusCode === 204 && (method === 'DELETE' || method === 'POST'))) {
return false;
} else {
return true;
}
};


/**
* Retrieve operation status by polling from 'azure-asyncoperation' header.
* @param {object} [pollingState] - The object to persist current operation state.
Expand Down Expand Up @@ -236,8 +212,8 @@ AzureServiceClient.prototype._updateStateFromAzureAsyncOperationHeader = functio
* Retrieve PUT operation status by polling from 'location' header.
* @param {object} [pollingState] - The object to persist current operation state.
*/
AzureServiceClient.prototype._updateStateFromLocationHeaderOnPut = function (pollingState, callback) {
this._getStatus(pollingState.locationHeaderLink, function (err, result) {
AzureServiceClient.prototype._updateStateFromLocationHeader = function (method, pollingState, callback) {
this._getStatus(pollingState.locationHeaderLink, function(err, result) {
if (err) return callback(err);

pollingState.updateResponse(result.response);
Expand All @@ -246,54 +222,20 @@ AzureServiceClient.prototype._updateStateFromLocationHeaderOnPut = function (pol
var statusCode = result.response.statusCode;
if (statusCode === 202) {
pollingState.status = LroStates.InProgress;
}
else if (statusCode === 200 ||
statusCode === 201) {

if (!result.body) {
return callback(new Error('The response from long running operation does not contain a body.'));
}

// In 202 pattern on PUT ProvisioningState may not be present in
// the response. In that case the assumption is the status is Succeeded.
if (result.body.properties && result.body.properties.provisioningState) {
pollingState.status = result.body.properties.provisioningState;
}
else {
pollingState.status = LroStates.Succeeded;
}

} else if (statusCode === 200 ||
(statusCode === 201 && method === 'PUT') ||
(statusCode === 204 && (method === 'DELETE' || method === 'POST'))) {

pollingState.status = LroStates.Succeeded;

pollingState.error = {
code: pollingState.Status,
message: util.format('Long running operation failed with status \'%s\'.', pollingState.status)
};
pollingState.resource = result.body;
}
callback(null);
});
};

/**
* Retrieve POST or DELETE operation status by polling from 'location' header.
* @param {object} [pollingState] - The object to persist current operation state.
*/
AzureServiceClient.prototype._updateStateFromLocationHeaderOnPostOrDelete = function (pollingState, callback) {
this._getStatus(pollingState.locationHeaderLink, function (err, result) {
if (err) return callback(err);

pollingState.updateResponse(result.response);
pollingState.request = result.request;

var statusCode = result.response.statusCode;
if (statusCode === 202) {
pollingState.status = LroStates.InProgress;
}
else if (statusCode === 200 ||
statusCode === 201 ||
statusCode === 204) {
pollingState.status = LroStates.Succeeded;
pollingState.resource = result.body;
}
} else {
return callback(new Error('The response from long running operation does not have a valid status code.'));
}
callback(null);
});
};
Expand Down
39 changes: 23 additions & 16 deletions ClientRuntimes/NodeJS/ms-rest-azure/lib/pollingState.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,31 @@ function PollingState(resultOfInitialRequest, retryTimeout) {
throw deserializationError;
}

if (this.resource && this.resource.properties && this.resource.properties.provisioningState) {
this.status = this.resource.properties.provisioningState;
} else {
switch (this.response.statusCode) {
case 202:
this.status = LroStates.InProgress;
break;
switch (this.response.statusCode) {
case 202:
this.status = LroStates.InProgress;
break;

case 204:
case 201:
case 200:
case 204:
this.status = LroStates.Succeeded;
break;
case 201:
if (this.resource && this.resource.properties && this.resource.properties.provisioningState) {
this.status = this.resource.properties.provisioningState;
} else {
this.status = LroStates.InProgress;
}
break;
case 200:
if (this.resource && this.resource.properties && this.resource.properties.provisioningState) {
this.status = this.resource.properties.provisioningState;
} else {
this.status = LroStates.Succeeded;
break;

default:
this.status = LroStates.Failed;
break;
}
}
break;
default:
this.status = LroStates.Failed;
break;
}
}

Expand Down
Loading