diff --git a/src/SdkCommon/ClientRuntime.Azure/ClientRuntime.Azure.Tests/LROOpertionTestResponses.cs b/src/SdkCommon/ClientRuntime.Azure/ClientRuntime.Azure.Tests/LROOpertionTestResponses.cs index 00cea42a16e2..4a032cb486b8 100644 --- a/src/SdkCommon/ClientRuntime.Azure/ClientRuntime.Azure.Tests/LROOpertionTestResponses.cs +++ b/src/SdkCommon/ClientRuntime.Azure/ClientRuntime.Azure.Tests/LROOpertionTestResponses.cs @@ -287,10 +287,7 @@ static internal IEnumerable MockLROPUTWithCanceledStateResp } #endregion - - - - + static internal IEnumerable MockCreateOrUpdateWithoutHeaderInResponses() { var response1 = new HttpResponseMessage(HttpStatusCode.OK) @@ -450,8 +447,7 @@ static internal IEnumerable MockAsyncOperaionWithNonSuccess }; yield return response2; } - - + static internal IEnumerable MockPutOperaionWitNonResource() { var response1 = new HttpResponseMessage(HttpStatusCode.Accepted) @@ -1231,5 +1227,318 @@ static internal IEnumerable MockCreateWithRetryAfterDefault yield return response2; } + static internal IEnumerable MockPutWebAppLRO() + { + var response1 = new HttpResponseMessage(HttpStatusCode.Created) + { + Content = new StringContent(@" + { + ""id"":""/subscriptions/ffa52f27-be12-b1ea-c2c1b6cceb/resourceGroups/rgnemv_bef11205b7b0/providers/Microsoft.Web/sites/webapp1-35965806af0/sourcecontrols/web"", + ""name"":""webapp1-35965806af0"", + ""type"":""Microsoft.Web/sites/sourcecontrols"", + ""location"":""West US 2"", + ""tags"":{}, + ""properties"": + { + ""repoUrl"":""https://github.com/foo/azure-site-test"", + ""branch"":""master"", + ""isManualIntegration"":true, + ""deploymentRollbackEnabled"":false, + ""isMercurial"":false, + ""provisioningState"":""InProgress"" + } + }") + }; + yield return response1; + + var response2 = new HttpResponseMessage(HttpStatusCode.Created) + { + Content = new StringContent(@" + { + ""id"":""/subscriptions/ffa52f27-be12-4cad-b1ea/resourceGroups/rgnemv_bef11205b7b0/providers/Microsoft.Web/sites/webapp1-35965806af0/sourcecontrols/web"", + ""name"":""webapp1-35965806af0"", + ""type"":""Microsoft.Web/sites/sourcecontrols"", + ""location"":""West US 2"", + ""tags"":{}, + ""properties"": + { + ""repoUrl"":""https://github.com/foo/azure-site-test"", + ""branch"":""master"", + ""isManualIntegration"":true, + ""deploymentRollbackEnabled"":false, + ""isMercurial"":false, + ""provisioningState"":""InProgress"", + ""provisioningDetails"":""2017-08-25T05:49:50.0340330 https://webapp1-359658.azurewebsites.net/api/deployments/latest?deployer=GitHub&time=2017-08-25_05-49-23"" + } + } + ") + }; + + yield return response2; + + var response3 = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(@" + { + ""id"":""/subscriptions/ffa52f27-be12-4cad-b1ea-c2c241b6cceb/resourceGroups/rgnemv_bef11205b7b0/providers/Microsoft.Web/sites/webapp1-35965806af0"", + ""name"":""webapp1-35965806af0"", + ""type"":""Microsoft.Web/sites"", + ""kind"":""app"", + ""location"":""West US 2"", + ""tags"":{}, + ""properties"": + { + ""name"":""webapp1-35965806af0"", + ""state"":""Running"", + ""hostNames"":[""webapp1-35965806af0.jsdkdemo-62e18289a.com"", + ""webapp1-35965806af0.azurewebsites.net""], + ""webSpace"":""rgnemv_bef11205b7b0-WestUS2webspace"", + ""selfLink"":""https://waws.api.azurewebsites.windows.net/subscriptions/ffa52f27-b1ea-c2c241b6cceb/webspaces/rgnemv_bef11205b7b0-WestUS2webspace/sites/webapp1-35965806af0"", + ""repositorySiteName"":""webapp1-35965806af0"", + ""owner"":null, + ""usageState"":""Normal"", + ""enabled"":true, + ""adminEnabled"":true, + ""enabledHostNames"":[""webapp1-35965806af0.jsdkdemo-62e18289a.com"", + ""webapp1-35965806af0.azurewebsites.net"", + ""webapp1-35965806af0.scm.azurewebsites.net""], + ""siteProperties"":{""metadata"":null, + ""properties"":[], + ""appSettings"":null}, + ""availabilityState"":""Normal"", + ""sslCertificates"":null, + ""csrs"":[], + ""cers"":null, + ""siteMode"":null, + ""hostNameSslStates"":[ + { + ""name"":""webapp1-35965806af0.azurewebsites.net"", + ""sslState"":""Disabled"", + ""ipBasedSslResult"":null, + ""virtualIP"":null, + ""thumbprint"":null, + ""toUpdate"":null, + ""toUpdateIpBasedSsl"":null, + ""ipBasedSslState"":""NotConfigured"", + ""hostType"":""Standard"" + }, + { + ""name"":""webapp1-35965806af0.jsdkdemo-62e18289a.com"", + ""sslState"":""SniEnabled"", + ""ipBasedSslResult"":null, + ""virtualIP"":null, + ""thumbprint"":""40ECC6C60434CF"", + ""toUpdate"":null, + ""toUpdateIpBasedSsl"":null, + ""ipBasedSslState"":""NotConfigured"", + ""hostType"":""Standard"" + }, + { + ""name"":""webapp.scm.azurewebsites.net"", + ""sslState"":""Disabled"", + ""ipBasedSslResult"":null, + ""virtualIP"":null, + ""thumbprint"":null, + ""toUpdate"":null, + ""toUpdateIpBasedSsl"":null, + ""ipBasedSslState"":""NotConfigured"", + ""hostType"":""Repository"" + }], + ""computeMode"":null, + ""serverFarm"":null, + ""serverFarmId"":""/subscriptions/ffa52f27-b1ea-c2c241b6cceb/resourceGroups/rgnemv_bef11205b7b0/providers/Microsoft.Web/serverfarms/jplan1_73a739850"", + ""reserved"":false, + ""lastModifiedTimeUtc"":""2017-08-25T05:48:34.92"", + ""storageRecoveryDefaultState"":""Running"", + ""contentAvailabilityState"":""Normal"", + ""runtimeAvailabilityState"":""Normal"", + ""siteConfig"":null, + ""deploymentId"":""webapp1-35965806af0"", + ""trafficManagerHostNames"":null, + ""sku"":""Basic"", + ""scmSiteAlsoStopped"":false, + ""targetSwapSlot"":null, + ""hostingEnvironment"":null, + ""hostingEnvironmentProfile"":null, + ""clientAffinityEnabled"":true, + ""clientCertEnabled"":false, + ""hostNamesDisabled"":false, + ""domainVerificationIdentifiers"":null, + ""kind"":""app"", + ""outboundIpAddresses"":""93.99.231.000,62.66.007.111,36.36.261.144,93.93.282.10"", + ""possibleOutboundIpAddresses"":""93.99.231.000,62.66.007.111,36.36.261.144,93.93.282.10"", + ""containerSize"":0, + ""dailyMemoryTimeQuota"":0, + ""suspendedTill"":null, + ""siteDisabledReason"":0, + ""functionExecutionUnitsCache"":null, + ""maxNumberOfWorkers"":null, + ""homeStamp"":""waws-wch237-003"", + ""cloningInfo"":null, + ""hostingEnvironmentId"":null, + ""tags"":{}, + ""resourceGroup"":""rgnemv_bef11205b7b0"", + ""defaultHostName"":""webapp1-35965806af0.azurewebsites.net"", + ""slotSwapStatus"":null + } + } + ") + }; + + yield return response3; + } + } + + static class LROOperationPatchTestResponses + { + static internal IEnumerable MockPatchWithAzureAsyncOperationHeader() + { + var response1 = new HttpResponseMessage(HttpStatusCode.Created) + { + Content = new StringContent(@" + { + ""id"": ""34adfa4f-cedf-4dc0-ba29-b6d1a69ab345"", + ""name"": ""test_account"", + ""type"": ""test_type"", + ""location"": ""test_location"", + ""tags"": { + ""test_key"": ""test_value"" + }, + ""properties"": { + ""provisioningState"": ""InProgress"", + ""state"": ""InActive"" + } + }") + }; + response1.Headers.Add("Azure-AsyncOperation", "http://custom/status"); + + yield return response1; + + var response2 = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(@" + { + ""status"": ""Succeeded"", + ""properties"": { + ""provisioningState"": ""Succeeded"", + ""state"": ""Active"" + } + } + ") + }; + + yield return response2; + + var response3 = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(@" + { + ""id"": ""34adfa4f-cedf-4dc0-ba29-b6d1a69ab345"", + ""name"": ""test_account"", + ""type"": ""test_type"", + ""location"": ""test_location"", + ""tags"": { + ""test_key"": ""test_value"" + } + } + ") + }; + yield return response3; + } + + static internal IEnumerable MockPatchWithLocationHeader() + { + var response1 = new HttpResponseMessage(HttpStatusCode.Accepted) + { + Content = new StringContent(@" + { + ""id"": ""34adfa4f-cedf-4dc0-ba29-b6d1a69ab345"", + ""name"": ""test_account"", + ""type"": ""test_type"", + ""location"": ""test_location"", + ""tags"": { + ""test_key"": ""test_value"" + }, + ""properties"": { + ""provisioningState"": ""InProgress"", + ""state"": ""InActive"" + } + }") + }; + response1.Headers.Add("Location", "https://management.azure.com:090/subscriptions/947c-43bc-83d3-6b318c6c7305/resourceGroups/hdisdk1706/providers/Microsoft.HDInsight/clusters/hdisdk-fail/azureasyncoperations/create?api-version=2015-03-01-preview"); + + yield return response1; + + var response2 = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(@" + { + ""status"": ""Succeeded"", + ""properties"": { + ""provisioningState"": ""Succeeded"", + ""state"": ""Active"" + } + } + ") + }; + + yield return response2; + } + } + + static class LROOperationFailedTestResponses + { + static internal IEnumerable MockLROAsyncOperationFailedOnlyStatus() + { + var response1 = new HttpResponseMessage(HttpStatusCode.Accepted) + { + Content = new StringContent(@" + { + ""location"": ""East US"", + ""etag"": ""9d8d7ed9-7422-46be-82b3-94c5345f6099"", + ""tags"": {}, + ""properties"": { + ""clusterVersion"": ""0.0.1000.0"", + ""osType"": ""Linux"", + ""provisioningState"": ""InProgress"", + ""clusterState"": ""Accepted"", + ""createdDate"": ""2017-07-25T21:48:17.427"", + ""quotaInfo"": + { + ""coresUsed"": ""200"" + }, + } + } + ") + }; + //response1.Headers.Add("Azure-AsyncOperation", "https://management.azure.com:090/subscriptions/434c10bb-83d3-6b318c6c7305/resourceGroups/hdisdk1706/providers/Microsoft.HDInsight/clusters/hdisdk-fail/azureasyncoperations/create?api-version=2015-03-01-preview"); + response1.Headers.Add("Location", "https://management.azure.com:090/subscriptions/947c-43bc-83d3-6b318c6c7305/resourceGroups/hdisdk1706/providers/Microsoft.HDInsight/clusters/hdisdk-fail/azureasyncoperations/create?api-version=2015-03-01-preview"); + + yield return response1; + + var response2 = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(@" + { + ""https://mdsbrketwprodsn1prod.blob.core.windows.net/cmakexe/abbd1dc4-44eb-4a66-9d90-156f7e5191a7/vpnclientconfiguration.zip?sv=2015-04-05&sr=b&sig=Ec6g2tlP0xktQSipQCTO55mnNjwOxTsge4Ot3sjX8Z8%3D&st=2017-08-28T21%3A25%3A34Z&se=2017-08-28T22%3A25%3A34Z&sp=r&fileExtension=.zip"" + } + ") + }; + + yield return response2; + + + //var response2 = new HttpResponseMessage(HttpStatusCode.OK) + //{ + // Content = new StringContent(@" + // { + // ""status"": ""Failed"" + // } + //") + //}; + + //yield return response2; + + } } } diff --git a/src/SdkCommon/ClientRuntime.Azure/ClientRuntime.Azure.Tests/LongRunningOperationsTest.cs b/src/SdkCommon/ClientRuntime.Azure/ClientRuntime.Azure.Tests/LongRunningOperationsTest.cs index 822fb7240bc7..67e9c29bb895 100644 --- a/src/SdkCommon/ClientRuntime.Azure/ClientRuntime.Azure.Tests/LongRunningOperationsTest.cs +++ b/src/SdkCommon/ClientRuntime.Azure/ClientRuntime.Azure.Tests/LongRunningOperationsTest.cs @@ -14,7 +14,12 @@ namespace Microsoft.Rest.ClientRuntime.Azure.Test using Microsoft.Azure; using Microsoft.Rest.Azure; using LROResponse = Microsoft.Rest.ClientRuntime.Azure.Tests.LROOpertionTestResponses; + using LROPatchResponses = Microsoft.Rest.ClientRuntime.Azure.Tests.LROOperationPatchTestResponses; + using LROFailedResponses = Microsoft.Rest.ClientRuntime.Azure.Tests.LROOperationFailedTestResponses; + /// + /// + /// public class LongRunningOperationsTest { /// @@ -585,10 +590,7 @@ public void TestDeleteWithLocationHeaderErrorHandlingSecondTime() var ex = Assert.Throws(()=>fakeClient.RedisOperations.Delete("rg", "redis", "1234")); Assert.Equal("Long running operation failed with status 'InternalServerError'.", ex.Message); } - - - - + /// /// Test /// @@ -664,6 +666,23 @@ public void TestLROWithNonStandardTerminalStatus() var foo = fakeClient.RedisOperations.PostWithHttpMessagesAsync("rg", "redis", "1234").ConfigureAwait(false).GetAwaiter().GetResult(); Assert.Equal("OK", foo.Response.StatusCode.ToString()); } + + /// + /// Test + /// + [Fact] + public void TestPutForWebAppLRO() + { + var tokenCredentials = new TokenCredentials("123", "abc"); + var handler = new PlaybackTestHandler(LROResponse.MockPutWebAppLRO()); + var fakeClient = new RedisManagementClient(tokenCredentials, handler); + fakeClient.LongRunningOperationInitialTimeout = fakeClient.LongRunningOperationRetryTimeout = 2; + var webAppResponse = fakeClient.RedisOperations.CreateOrUpdate("rg", "redis", new RedisCreateOrUpdateParameters(), "1234"); + + Assert.Equal("webapp1-35965806af0", webAppResponse.Name); + Assert.Equal(HttpMethod.Put, handler.Requests[0].Method); + Assert.Equal(HttpMethod.Get, handler.Requests[1].Method); + } } /// @@ -758,4 +777,77 @@ public void TestCreateWithRetryAfterDefaultMax() Assert.True(DateTime.Now - before >= TimeSpan.FromSeconds(40)); } } + + /// + /// + /// + public class LRO_PatchTests + { + /// + /// Test + /// + [Fact] + public void TestPatchWithAsyncHeader() + { + var tokenCredentials = new TokenCredentials("123", "abc"); + var handler = new PlaybackTestHandler(LROPatchResponses.MockPatchWithAzureAsyncOperationHeader()); + var fakeClient = new RedisManagementClient(tokenCredentials, handler); + fakeClient.LongRunningOperationInitialTimeout = fakeClient.LongRunningOperationRetryTimeout = 2; + fakeClient.RedisOperations.Patch("rg", "redis", new RedisCreateOrUpdateParameters(), "1234"); + + Assert.Equal(new HttpMethod("PATCH"), handler.Requests[0].Method); + Assert.Equal("https://management.azure.com/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Cache/Redis/redis", + handler.Requests[0].RequestUri.ToString()); + Assert.Equal(HttpMethod.Get, handler.Requests[1].Method); + Assert.Equal("http://custom/status", + handler.Requests[1].RequestUri.ToString()); + + Assert.Equal(HttpMethod.Get, handler.Requests[2].Method); + Assert.Equal("https://management.azure.com/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Cache/Redis/redis", + handler.Requests[2].RequestUri.ToString()); + } + + /// + /// Test + /// + [Fact] + public void TestPatchWithLocationHeader() + { + var tokenCredentials = new TokenCredentials("123", "abc"); + var handler = new PlaybackTestHandler(LROPatchResponses.MockPatchWithLocationHeader()); + var fakeClient = new RedisManagementClient(tokenCredentials, handler); + fakeClient.LongRunningOperationInitialTimeout = fakeClient.LongRunningOperationRetryTimeout = 2; + fakeClient.RedisOperations.Patch("rg", "redis", new RedisCreateOrUpdateParameters(), "1234"); + + Assert.Equal(new HttpMethod("PATCH"), handler.Requests[0].Method); + Assert.Equal("https://management.azure.com/subscriptions/1234/resourceGroups/rg/providers/Microsoft.Cache/Redis/redis", + handler.Requests[0].RequestUri.ToString()); + Assert.Equal(HttpMethod.Get, handler.Requests[1].Method); + Assert.Equal("https://management.azure.com:90/subscriptions/947c-43bc-83d3-6b318c6c7305/resourceGroups/hdisdk1706/providers/Microsoft.HDInsight/clusters/hdisdk-fail/azureasyncoperations/create?api-version=2015-03-01-preview", + handler.Requests[1].RequestUri.ToString()); + } + } + + /// + /// LOR Failed test scenrios + /// + public class LRO_FailedTests + { + [Fact(Skip = "Potential scenario that will have to be supported")] + public void TestLROAsynOperationFailureWith200() + { + var tokenCredentials = new TokenCredentials("123", "abc"); + var handler = new PlaybackTestHandler(LROFailedResponses.MockLROAsyncOperationFailedOnlyStatus()); + var fakeClient = new RedisManagementClient(tokenCredentials, handler); + fakeClient.LongRunningOperationInitialTimeout = fakeClient.LongRunningOperationRetryTimeout = 0; + try + { + var foo = fakeClient.RedisOperations.CreateOrUpdate("rg", "redis", new RedisCreateOrUpdateParameters(), "1234"); + } + catch (Exception ex) + { + Assert.Contains("Long running operation failed with status", ex.Message); + } + } + } } diff --git a/src/SdkCommon/ClientRuntime.Azure/ClientRuntime.Azure/AzureClientExtensions.cs b/src/SdkCommon/ClientRuntime.Azure/ClientRuntime.Azure/AzureClientExtensions.cs index 4154abf8f7b7..6a3cd1a3e067 100644 --- a/src/SdkCommon/ClientRuntime.Azure/ClientRuntime.Azure/AzureClientExtensions.cs +++ b/src/SdkCommon/ClientRuntime.Azure/ClientRuntime.Azure/AzureClientExtensions.cs @@ -110,7 +110,7 @@ public static async Task> GetLongRunningO if (!string.IsNullOrEmpty(pollingState.AzureAsyncOperationHeaderLink)) { - await UpdateStateFromAzureAsyncOperationHeader(client, pollingState, customHeaders, cancellationToken); + await UpdateStateFromAzureAsyncOperationHeader(client, pollingState, customHeaders, cancellationToken, initialRequestMethod); } else if (!string.IsNullOrEmpty(pollingState.LocationHeaderLink)) { @@ -119,7 +119,7 @@ public static async Task> GetLongRunningO else if (initialRequestMethod == HttpMethod.Put) { await UpdateStateFromGetResourceOperation(client, pollingState, getOperationUrl, - customHeaders, cancellationToken); + customHeaders, cancellationToken, initialRequestMethod); } else { @@ -133,7 +133,7 @@ await UpdateStateFromGetResourceOperation(client, pollingState, getOperationUrl, (initialRequestMethod == HttpMethod.Put || initialRequestMethod == new HttpMethod("PATCH"))) { await UpdateStateFromGetResourceOperation(client, pollingState, getOperationUrl, customHeaders, - cancellationToken); + cancellationToken, initialRequestMethod); } } @@ -349,183 +349,183 @@ private static bool CheckResponseStatusCodeFailed( var statusCode = initialResponse.Response.StatusCode; var method = initialResponse.Request.Method; if (statusCode == HttpStatusCode.OK || statusCode == HttpStatusCode.Accepted || - (statusCode == HttpStatusCode.Created && (method == HttpMethod.Put)) || + (statusCode == HttpStatusCode.Created && ((method == HttpMethod.Put) || (method == new HttpMethod("PATCH")))) || (statusCode == HttpStatusCode.NoContent && (method == HttpMethod.Delete || method == HttpMethod.Post))) { return false; } return true; } - - /* - /// - /// Updates PollingState from GET operations. - /// - /// Type of the resource body. - /// Type of the resource header. - /// IAzureClient - /// Current polling state. - /// Uri for the get operation - /// Headers that will be added to request - /// Cancellation token - /// Task. - private static async Task UpdateStateFromGetResourceOperation( - IAzureClient client, - PollingState pollingState, - Uri getOperationUri, - Dictionary> customHeaders, - CancellationToken cancellationToken) where TBody : class where THeader : class - { - AzureOperationResponse responseWithResource = await GetRawAsync(client, - getOperationUri.AbsoluteUri, customHeaders, cancellationToken).ConfigureAwait(false); - - if (responseWithResource.Body == null) - { - throw new CloudException(Resources.NoBody); - } - - // In 202 pattern on PUT ProvisioningState may not be present in - // the response. In that case the assumption is the status is Succeeded. - var resource = responseWithResource.Body; - if (resource["properties"] != null && resource["properties"]["provisioningState"] != null) - { - pollingState.Status = (string)resource["properties"]["provisioningState"]; - } - else - { - pollingState.Status = AzureAsyncOperation.SuccessStatus; - } - - if(pollingState?.Error == null) - { - pollingState.Error = new CloudError() - { - Code = pollingState.Status, - Message = string.Format(Resources.LongRunningOperationFailed, pollingState.Status) - }; - } - - pollingState.Response = responseWithResource.Response; - pollingState.Request = responseWithResource.Request; - pollingState.Resource = responseWithResource.Body.ToObject(JsonSerializer - .Create(client.DeserializationSettings)); - pollingState.ResourceHeaders = responseWithResource.Headers.ToObject(JsonSerializer - .Create(client.DeserializationSettings)); - } - - /// - /// Updates PollingState from Location header. - /// - /// Type of the resource body. - /// Type of the resource header. - /// IAzureClient - /// Current polling state. - /// Headers that will be added to request - /// Cancellation token - /// Http method of the initial long running operation request - /// Task. - private static async Task UpdateStateFromLocationHeader( - IAzureClient client, - PollingState pollingState, - Dictionary> customHeaders, - CancellationToken cancellationToken, - HttpMethod method) where TBody : class where THeader : class - { - AzureOperationResponse responseWithResource = await client.GetRawAsync( - pollingState.LocationHeaderLink, - customHeaders, - cancellationToken).ConfigureAwait(false); - - pollingState.Response = responseWithResource.Response; - pollingState.Request = responseWithResource.Request; - - var statusCode = responseWithResource.Response.StatusCode; - var resource = responseWithResource.Body; - if (statusCode == HttpStatusCode.Accepted) - { - pollingState.Status = AzureAsyncOperation.InProgressStatus; - } - else if (statusCode == HttpStatusCode.OK || - (statusCode == HttpStatusCode.Created && method == HttpMethod.Put) || - (statusCode == HttpStatusCode.NoContent && (method == HttpMethod.Delete || method == HttpMethod.Post))) - { - if (resource != null && - resource["properties"] != null && - resource["properties"]["provisioningState"] != null) - { - pollingState.Status = (string)resource["properties"]["provisioningState"]; - } - else - { - pollingState.Status = AzureAsyncOperation.SuccessStatus; - } - - pollingState.Error = new CloudError() - { - Code = pollingState.Status, - Message = string.Format(Resources.LongRunningOperationFailed, pollingState.Status) - }; - pollingState.Resource = responseWithResource.Body == null ? null : responseWithResource.Body.ToObject(JsonSerializer - .Create(client.DeserializationSettings)); - pollingState.ResourceHeaders = responseWithResource.Headers.ToObject(JsonSerializer - .Create(client.DeserializationSettings)); - } - else - { - throw new CloudException("The response from long running operation does not have a valid status code."); - } - - return; - } - - /// - /// Updates PollingState from Azure-AsyncOperation header. - /// - /// Type of the resource body. - /// Type of the resource header. - /// IAzureClient - /// Current polling state. - /// Headers that will be added to request - /// Cancellation token - /// Task. - private static async Task UpdateStateFromAzureAsyncOperationHeader( - IAzureClient client, - PollingState pollingState, - Dictionary> customHeaders, - CancellationToken cancellationToken) where TBody : class where THeader : class - { - AzureOperationResponse asyncOperationResponse = - await client.GetAsync( - pollingState.AzureAsyncOperationHeaderLink, - customHeaders, - cancellationToken).ConfigureAwait(false); - - if (asyncOperationResponse.Body == null || asyncOperationResponse.Body.Status == null) - { - throw new CloudException(Resources.NoBody); - } - - pollingState.Status = asyncOperationResponse.Body.Status; - pollingState.Error = asyncOperationResponse.Body.Error; - pollingState.Response = asyncOperationResponse.Response; - pollingState.Request = asyncOperationResponse.Request; - pollingState.Resource = null; - //Try to de-serialize to the response model. (Not required for "PutOrPatch" - //which has the fallback of invoking generic "resource get".) - string responseContent = await pollingState.Response.Content.ReadAsStringAsync(); - var responseHeaders = pollingState.Response.Headers.ToJson(); - try - { - pollingState.Resource = JObject.Parse(responseContent) - .ToObject(JsonSerializer.Create(client.DeserializationSettings)); - pollingState.ResourceHeaders = - responseHeaders.ToObject(JsonSerializer.Create(client.DeserializationSettings)); - } - catch { }; - } - */ + /* + /// + /// Updates PollingState from GET operations. + /// + /// Type of the resource body. + /// Type of the resource header. + /// IAzureClient + /// Current polling state. + /// Uri for the get operation + /// Headers that will be added to request + /// Cancellation token + /// Task. + private static async Task UpdateStateFromGetResourceOperation( + IAzureClient client, + PollingState pollingState, + Uri getOperationUri, + Dictionary> customHeaders, + CancellationToken cancellationToken) where TBody : class where THeader : class + { + AzureOperationResponse responseWithResource = await GetRawAsync(client, + getOperationUri.AbsoluteUri, customHeaders, cancellationToken).ConfigureAwait(false); + + if (responseWithResource.Body == null) + { + throw new CloudException(Resources.NoBody); + } + + // In 202 pattern on PUT ProvisioningState may not be present in + // the response. In that case the assumption is the status is Succeeded. + var resource = responseWithResource.Body; + if (resource["properties"] != null && resource["properties"]["provisioningState"] != null) + { + pollingState.Status = (string)resource["properties"]["provisioningState"]; + } + else + { + pollingState.Status = AzureAsyncOperation.SuccessStatus; + } + + if(pollingState?.Error == null) + { + pollingState.Error = new CloudError() + { + Code = pollingState.Status, + Message = string.Format(Resources.LongRunningOperationFailed, pollingState.Status) + }; + } + + pollingState.Response = responseWithResource.Response; + pollingState.Request = responseWithResource.Request; + pollingState.Resource = responseWithResource.Body.ToObject(JsonSerializer + .Create(client.DeserializationSettings)); + pollingState.ResourceHeaders = responseWithResource.Headers.ToObject(JsonSerializer + .Create(client.DeserializationSettings)); + } + + /// + /// Updates PollingState from Location header. + /// + /// Type of the resource body. + /// Type of the resource header. + /// IAzureClient + /// Current polling state. + /// Headers that will be added to request + /// Cancellation token + /// Http method of the initial long running operation request + /// Task. + private static async Task UpdateStateFromLocationHeader( + IAzureClient client, + PollingState pollingState, + Dictionary> customHeaders, + CancellationToken cancellationToken, + HttpMethod method) where TBody : class where THeader : class + { + AzureOperationResponse responseWithResource = await client.GetRawAsync( + pollingState.LocationHeaderLink, + customHeaders, + cancellationToken).ConfigureAwait(false); + + pollingState.Response = responseWithResource.Response; + pollingState.Request = responseWithResource.Request; + + var statusCode = responseWithResource.Response.StatusCode; + var resource = responseWithResource.Body; + if (statusCode == HttpStatusCode.Accepted) + { + pollingState.Status = AzureAsyncOperation.InProgressStatus; + } + else if (statusCode == HttpStatusCode.OK || + (statusCode == HttpStatusCode.Created && method == HttpMethod.Put) || + (statusCode == HttpStatusCode.NoContent && (method == HttpMethod.Delete || method == HttpMethod.Post))) + { + if (resource != null && + resource["properties"] != null && + resource["properties"]["provisioningState"] != null) + { + pollingState.Status = (string)resource["properties"]["provisioningState"]; + } + else + { + pollingState.Status = AzureAsyncOperation.SuccessStatus; + } + + pollingState.Error = new CloudError() + { + Code = pollingState.Status, + Message = string.Format(Resources.LongRunningOperationFailed, pollingState.Status) + }; + pollingState.Resource = responseWithResource.Body == null ? null : responseWithResource.Body.ToObject(JsonSerializer + .Create(client.DeserializationSettings)); + pollingState.ResourceHeaders = responseWithResource.Headers.ToObject(JsonSerializer + .Create(client.DeserializationSettings)); + } + else + { + throw new CloudException("The response from long running operation does not have a valid status code."); + } + + return; + } + + /// + /// Updates PollingState from Azure-AsyncOperation header. + /// + /// Type of the resource body. + /// Type of the resource header. + /// IAzureClient + /// Current polling state. + /// Headers that will be added to request + /// Cancellation token + /// Task. + private static async Task UpdateStateFromAzureAsyncOperationHeader( + IAzureClient client, + PollingState pollingState, + Dictionary> customHeaders, + CancellationToken cancellationToken) where TBody : class where THeader : class + { + AzureOperationResponse asyncOperationResponse = + await client.GetAsync( + pollingState.AzureAsyncOperationHeaderLink, + customHeaders, + cancellationToken).ConfigureAwait(false); + + if (asyncOperationResponse.Body == null || asyncOperationResponse.Body.Status == null) + { + throw new CloudException(Resources.NoBody); + } + + pollingState.Status = asyncOperationResponse.Body.Status; + pollingState.Error = asyncOperationResponse.Body.Error; + pollingState.Response = asyncOperationResponse.Response; + pollingState.Request = asyncOperationResponse.Request; + pollingState.Resource = null; + //Try to de-serialize to the response model. (Not required for "PutOrPatch" + //which has the fallback of invoking generic "resource get".) + string responseContent = await pollingState.Response.Content.ReadAsStringAsync(); + var responseHeaders = pollingState.Response.Headers.ToJson(); + try + { + pollingState.Resource = JObject.Parse(responseContent) + .ToObject(JsonSerializer.Create(client.DeserializationSettings)); + pollingState.ResourceHeaders = + responseHeaders.ToObject(JsonSerializer.Create(client.DeserializationSettings)); + } + catch { }; + } + + */ /// /// Gets a resource from the specified URL. diff --git a/src/SdkCommon/ClientRuntime.Azure/ClientRuntime.Azure/AzureLROOperationExtensions.cs b/src/SdkCommon/ClientRuntime.Azure/ClientRuntime.Azure/AzureLROOperationExtensions.cs index 57ed22e34136..e618a66dbd51 100644 --- a/src/SdkCommon/ClientRuntime.Azure/ClientRuntime.Azure/AzureLROOperationExtensions.cs +++ b/src/SdkCommon/ClientRuntime.Azure/ClientRuntime.Azure/AzureLROOperationExtensions.cs @@ -26,14 +26,14 @@ public static partial class AzureClientExtensions /// Current polling state. /// Headers that will be added to request /// Cancellation token - /// Http method of the initial long running operation request + /// Http method of the initial long running operation request /// Task. private static async Task UpdateStateFromLocationHeader( IAzureClient client, PollingState pollingState, Dictionary> customHeaders, CancellationToken cancellationToken, - HttpMethod method) where TBody : class where THeader : class + HttpMethod initialRequestMethod) where TBody : class where THeader : class { AzureAsyncOperation asyncOperation = null; @@ -62,7 +62,7 @@ private static async Task UpdateStateFromLocationHeader( } catch { } - pollingState = GetUpdatedPollingStatus(asyncOperation, responseWithResource, pollingState, responseContent, method); + pollingState = GetUpdatedPollingStatus(asyncOperation, responseWithResource, pollingState, responseContent, initialRequestMethod); } @@ -82,7 +82,8 @@ private static async Task UpdateStateFromGetResourceOperation( PollingState pollingState, Uri getOperationUri, Dictionary> customHeaders, - CancellationToken cancellationToken) where TBody : class where THeader : class + CancellationToken cancellationToken, + HttpMethod initialRequestMethod) where TBody : class where THeader : class { AzureAsyncOperation asyncOperation = null; @@ -116,7 +117,7 @@ private static async Task UpdateStateFromGetResourceOperation( } catch { } - pollingState = GetUpdatedPollingStatus(asyncOperation, responseWithResource, pollingState, responseContent, null); + pollingState = GetUpdatedPollingStatus(asyncOperation, responseWithResource, pollingState, responseContent, initialRequestMethod); } @@ -134,7 +135,8 @@ private static async Task UpdateStateFromAzureAsyncOperationHeader pollingState, Dictionary> customHeaders, - CancellationToken cancellationToken) where TBody : class where THeader : class + CancellationToken cancellationToken, + HttpMethod initialRequestMethod) where TBody : class where THeader : class { string errMessage = string.Empty; @@ -160,9 +162,9 @@ await client.GetAsync( if (AzureAsyncOperation.FailedStatuses.Any( s => s.Equals(pollingState.Status, StringComparison.OrdinalIgnoreCase))) { - + //As this is for AsyncOperation header, we pass AzureOperationResponse as null pollingState = GetUpdatedPollingStatus(asyncOperationResponse.Body, - null, pollingState, responseContent, null); + null, pollingState, responseContent, initialRequestMethod); } //Try to de-serialize to the response model. (Not required for "PutOrPatch" @@ -195,14 +197,14 @@ await client.GetAsync( /// /// /// - /// + /// /// private static PollingState GetUpdatedPollingStatus( AzureAsyncOperation asyncOperation, AzureOperationResponse azureResponse, PollingState pollState, string responseContent, - HttpMethod method) + HttpMethod initialRequestMethod) where TBody : class where THeader : class { @@ -215,8 +217,17 @@ private static PollingState GetUpdatedPollingStatus GetUpdatedPollingStatus GetUpdatedPollingStatus3.4.0 Microsoft.Rest.ClientRuntime.Azure Microsoft Azure AutoRest ClientRuntime REST $(NugetCommonTags) $(NugetCommonProfileTags) + + + + + + net452;netstandard1.4 diff --git a/src/SdkCommon/ClientRuntime/ClientRuntime/Microsoft.Rest.ClientRuntime.csproj b/src/SdkCommon/ClientRuntime/ClientRuntime/Microsoft.Rest.ClientRuntime.csproj index 4b58d370f18d..34caa6276a0a 100644 --- a/src/SdkCommon/ClientRuntime/ClientRuntime/Microsoft.Rest.ClientRuntime.csproj +++ b/src/SdkCommon/ClientRuntime/ClientRuntime/Microsoft.Rest.ClientRuntime.csproj @@ -7,6 +7,16 @@ 2.3.9 Microsoft.Rest.ClientRuntime Microsoft AutoRest ClientRuntime $(NugetCommonTags) $(NugetCommonProfileTags) + + + + + + net452;netstandard1.4