diff --git a/README.md b/README.md index cc269ef1224..eab23b86ae4 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ Welcome to the Microsoft Quantum Development Kit! This repository contains the runtime components for the [Quantum Development Kit](https://docs.microsoft.com/quantum/). It consists of the libraries and packages needed to create and simulate quantum applications using Q#. +- **[Azure/](./src/Azure/)**: Source for client package to create and manage jobs in Azure Quantum. - **[Simulation/](./src/Simulation/)**: Source for Q# simulation. Includes code generation, full-state and other simulators. - **[xUnit/](./src/Xunit/)**: Source for the xUnit's Q# test-case discoverer. diff --git a/Simulation.sln b/Simulation.sln index ca07a7fe45b..252a287f9d0 100644 --- a/Simulation.sln +++ b/Simulation.sln @@ -3,6 +3,14 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.28809.33 MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{99E234BC-997E-4E63-9F5C-3C3977543404}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Azure", "Azure", "{A1E878CB-ADF1-457A-9223-06F96ED8F7A6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Azure.Quantum.Client", "src\Azure\Azure.Quantum.Client\Microsoft.Azure.Quantum.Client.csproj", "{DF654202-0008-4CDE-B35E-018CEFD0FC68}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Azure.Quantum.Client.Test", "src\Azure\Azure.Quantum.Client.Test\Microsoft.Azure.Quantum.Client.Test.csproj", "{1467128C-90E4-4723-B5C7-9469B83F54A6}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Quantum.Runtime.Core", "src\Simulation\Core\Microsoft.Quantum.Runtime.Core.csproj", "{E9123D45-C1B0-4462-8810-D26ED6D31944}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Quantum.Simulation.QCTraceSimulatorRuntime", "src\Simulation\QCTraceSimulator\Microsoft.Quantum.Simulation.QCTraceSimulatorRuntime.csproj", "{058CB08D-BFA7-41E2-BE6B-0A0A72054F91}" @@ -53,6 +61,38 @@ Global RelWithDebInfo|x64 = RelWithDebInfo|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DF654202-0008-4CDE-B35E-018CEFD0FC68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DF654202-0008-4CDE-B35E-018CEFD0FC68}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DF654202-0008-4CDE-B35E-018CEFD0FC68}.Debug|x64.ActiveCfg = Debug|Any CPU + {DF654202-0008-4CDE-B35E-018CEFD0FC68}.Debug|x64.Build.0 = Debug|Any CPU + {DF654202-0008-4CDE-B35E-018CEFD0FC68}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU + {DF654202-0008-4CDE-B35E-018CEFD0FC68}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU + {DF654202-0008-4CDE-B35E-018CEFD0FC68}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU + {DF654202-0008-4CDE-B35E-018CEFD0FC68}.MinSizeRel|x64.Build.0 = Debug|Any CPU + {DF654202-0008-4CDE-B35E-018CEFD0FC68}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DF654202-0008-4CDE-B35E-018CEFD0FC68}.Release|Any CPU.Build.0 = Release|Any CPU + {DF654202-0008-4CDE-B35E-018CEFD0FC68}.Release|x64.ActiveCfg = Release|Any CPU + {DF654202-0008-4CDE-B35E-018CEFD0FC68}.Release|x64.Build.0 = Release|Any CPU + {DF654202-0008-4CDE-B35E-018CEFD0FC68}.RelWithDebInfo|Any CPU.ActiveCfg = Debug|Any CPU + {DF654202-0008-4CDE-B35E-018CEFD0FC68}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU + {DF654202-0008-4CDE-B35E-018CEFD0FC68}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU + {DF654202-0008-4CDE-B35E-018CEFD0FC68}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU + {1467128C-90E4-4723-B5C7-9469B83F54A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1467128C-90E4-4723-B5C7-9469B83F54A6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1467128C-90E4-4723-B5C7-9469B83F54A6}.Debug|x64.ActiveCfg = Debug|Any CPU + {1467128C-90E4-4723-B5C7-9469B83F54A6}.Debug|x64.Build.0 = Debug|Any CPU + {1467128C-90E4-4723-B5C7-9469B83F54A6}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU + {1467128C-90E4-4723-B5C7-9469B83F54A6}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU + {1467128C-90E4-4723-B5C7-9469B83F54A6}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU + {1467128C-90E4-4723-B5C7-9469B83F54A6}.MinSizeRel|x64.Build.0 = Debug|Any CPU + {1467128C-90E4-4723-B5C7-9469B83F54A6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1467128C-90E4-4723-B5C7-9469B83F54A6}.Release|Any CPU.Build.0 = Release|Any CPU + {1467128C-90E4-4723-B5C7-9469B83F54A6}.Release|x64.ActiveCfg = Release|Any CPU + {1467128C-90E4-4723-B5C7-9469B83F54A6}.Release|x64.Build.0 = Release|Any CPU + {1467128C-90E4-4723-B5C7-9469B83F54A6}.RelWithDebInfo|Any CPU.ActiveCfg = Debug|Any CPU + {1467128C-90E4-4723-B5C7-9469B83F54A6}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU + {1467128C-90E4-4723-B5C7-9469B83F54A6}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU + {1467128C-90E4-4723-B5C7-9469B83F54A6}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU {E9123D45-C1B0-4462-8810-D26ED6D31944}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E9123D45-C1B0-4462-8810-D26ED6D31944}.Debug|Any CPU.Build.0 = Debug|Any CPU {E9123D45-C1B0-4462-8810-D26ED6D31944}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -297,6 +337,9 @@ Global {09C842CB-930C-4C7D-AD5F-E30DE4A55820} = {34D419E9-CCF1-4E48-9FA4-3AD4B86BEEB4} {2F5796A7-4AF8-4B78-928A-0A3A80752F9D} = {09C842CB-930C-4C7D-AD5F-E30DE4A55820} {944FE7EF-9220-4CC6-BB20-CE517195B922} = {A567C185-A429-418B-AFDE-6F1785BA4A77} + {A1E878CB-ADF1-457A-9223-06F96ED8F7A6} = {99E234BC-997E-4E63-9F5C-3C3977543404} + {DF654202-0008-4CDE-B35E-018CEFD0FC68} = {A1E878CB-ADF1-457A-9223-06F96ED8F7A6} + {1467128C-90E4-4723-B5C7-9469B83F54A6} = {A1E878CB-ADF1-457A-9223-06F96ED8F7A6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {929C0464-86D8-4F70-8835-0A5EAF930821} diff --git a/build/manifest.ps1 b/build/manifest.ps1 index cf7afbc72b4..07a8fca41f3 100644 --- a/build/manifest.ps1 +++ b/build/manifest.ps1 @@ -5,6 +5,7 @@ @{ Packages = @( + "Microsoft.Azure.Quantum.Client", "Microsoft.Quantum.CsharpGeneration", "Microsoft.Quantum.Development.Kit", "Microsoft.Quantum.QSharp.Core", @@ -13,6 +14,7 @@ "Microsoft.Quantum.Xunit" ); Assemblies = @( + ".\src\Azure\Azure.Quantum.Client\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Azure.Quantum.Client.dll", ".\src\simulation\CsharpGeneration\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.CsharpGeneration.dll", ".\src\simulation\CsharpGeneration.App\bin\$Env:BUILD_CONFIGURATION\netcoreapp3.1\Microsoft.Quantum.CsharpGeneration.App.dll", ".\src\simulation\CsharpGeneration.App\bin\$Env:BUILD_CONFIGURATION\netcoreapp3.1\Microsoft.Quantum.RoslynWrapper.dll", diff --git a/build/pack.ps1 b/build/pack.ps1 index 53c7d617f7f..7aa7227d8da 100644 --- a/build/pack.ps1 +++ b/build/pack.ps1 @@ -54,6 +54,7 @@ function Pack-Dotnet() { Write-Host "##[info]Using nuget to create packages" +Pack-Dotnet '../src/Azure/Azure.Quantum.Client/Microsoft.Azure.Quantum.Client.csproj' Pack-One '../src/Simulation/CsharpGeneration/Microsoft.Quantum.CsharpGeneration.fsproj' '-IncludeReferencedProjects' Pack-Dotnet '../src/Simulation/Core/Microsoft.Quantum.Runtime.Core.csproj' Pack-Dotnet '../src/Simulation/QsharpCore/Microsoft.Quantum.QSharp.Core.csproj' diff --git a/src/Azure/Azure.Quantum.Client.Test/Helpers/MockHelper.cs b/src/Azure/Azure.Quantum.Client.Test/Helpers/MockHelper.cs new file mode 100644 index 00000000000..3cdc42675aa --- /dev/null +++ b/src/Azure/Azure.Quantum.Client.Test/Helpers/MockHelper.cs @@ -0,0 +1,34 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Moq; +using Moq.Protected; + +namespace Microsoft.Azure.Quantum.Test +{ + public static class MockHelper + { + internal static HttpRequestMessage RequestMessage { get; private set; } + + internal static HttpResponseMessage ResponseMessage { get; set; } + + public static HttpClient GetHttpClientMock() + { + Mock mock = new Mock(); + + mock.Protected() + .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) + .Returns((HttpRequestMessage request, CancellationToken token) => + { + RequestMessage = request; + return Task.FromResult(ResponseMessage); + }); + + return new HttpClient(mock.Object); + } + } +} diff --git a/src/Azure/Azure.Quantum.Client.Test/Helpers/TestConstants.cs b/src/Azure/Azure.Quantum.Client.Test/Helpers/TestConstants.cs new file mode 100644 index 00000000000..2d35b2cfe14 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client.Test/Helpers/TestConstants.cs @@ -0,0 +1,15 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +namespace Microsoft.Azure.Quantum.Test +{ + public static class TestConstants + { + public const string SubscriptionId = "sub1"; + public const string ResourceGroupName = "rg1"; + public const string WorkspaceName = "ws1"; + public const string ProviderId = "provider1"; + public const string Endpoint = "https://test"; + } +} diff --git a/src/Azure/Azure.Quantum.Client.Test/Microsoft.Azure.Quantum.Client.Test.csproj b/src/Azure/Azure.Quantum.Client.Test/Microsoft.Azure.Quantum.Client.Test.csproj new file mode 100644 index 00000000000..7965e696874 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client.Test/Microsoft.Azure.Quantum.Client.Test.csproj @@ -0,0 +1,34 @@ + + + + netcoreapp3.1 + x64 + false + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + ..\Common\StyleCop.ruleset + Microsoft.Azure.Quantum.Test + + + + + + + + + + + diff --git a/src/Azure/Azure.Quantum.Client.Test/WorkspaceTest.cs b/src/Azure/Azure.Quantum.Client.Test/WorkspaceTest.cs new file mode 100644 index 00000000000..eb96b2d1e00 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client.Test/WorkspaceTest.cs @@ -0,0 +1,233 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using Microsoft.Azure.Quantum.Client; +using Microsoft.Azure.Quantum.Client.Models; +using Microsoft.Rest; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; + +namespace Microsoft.Azure.Quantum.Test +{ + [TestClass] + public class WorkspaceTest + { + [TestMethod] + public void SubmitJobTest() + { + string jobId = Guid.NewGuid().ToString(); + + // Craft response + SetJobResponseMessage(jobId); + + // Create Job + IWorkspace workspace = GetWorkspace(); + + JobDetails jobDetails = CreateJobDetails(jobId); + CloudJob job = new CloudJob(workspace, jobDetails); + CloudJob receivedJob; + + // -ve cases + try + { + jobDetails.ContainerUri = null; + receivedJob = workspace.SubmitJob(job); + Assert.Fail(); + } + catch (ValidationException) + { + jobDetails.ContainerUri = "https://uri"; + } + + try + { + jobDetails.ProviderId = null; + receivedJob = workspace.SubmitJob(job); + Assert.Fail(); + } + catch (ValidationException) + { + jobDetails.ProviderId = TestConstants.ProviderId; + } + + // Success + receivedJob = workspace.SubmitJob(job); + + // Validate request + ValidateJobRequestMessage(jobId, HttpMethod.Put); + + // Validate response + Assert.IsNotNull(receivedJob); + + Assert.IsNotNull(receivedJob.Workspace); + + Assert.AreEqual( + expected: jobId, + actual: receivedJob.Details.Id); + } + + [TestMethod] + public void GetJobTest() + { + string jobId = Guid.NewGuid().ToString(); + + // Craft response + SetJobResponseMessage(jobId); + + // Get Job + IWorkspace workspace = GetWorkspace(); + + CloudJob receivedJob = workspace.GetJob(jobId); + + // Validate request + ValidateJobRequestMessage(jobId, HttpMethod.Get); + + // Validate response + Assert.IsNotNull(receivedJob); + + Assert.IsNotNull(receivedJob.Workspace); + + Assert.AreEqual( + expected: jobId, + actual: receivedJob.Details.Id); + } + + [TestMethod] + public void CancelJobTest() + { + string jobId = Guid.NewGuid().ToString(); + + // Craft response + SetJobResponseMessage(jobId); + + // Cancel Job + IWorkspace workspace = GetWorkspace(); + + CloudJob receivedJob = workspace.CancelJob(jobId); + + // Validate request + ValidateJobRequestMessage(jobId, HttpMethod.Delete); + + // Validate response + Assert.IsNotNull(receivedJob); + + Assert.IsNotNull(receivedJob.Workspace); + + Assert.AreEqual( + expected: jobId, + actual: receivedJob.Details.Id); + + // Convenience method + CloudJob job = new CloudJob(workspace, CreateJobDetails(jobId)); + string newJobId = Guid.NewGuid().ToString(); + SetJobResponseMessage(newJobId); + + Assert.AreEqual( + jobId, + job.Details.Id); + + job.CancelAsync().Wait(); + + Assert.AreEqual( + newJobId, + job.Details.Id); + } + + [TestMethod] + public void ListJobsTest() + { + // Craft response + JobDetails jobDetails = new JobDetails + { + ProviderId = TestConstants.ProviderId, + }; + + Page page = new Page() + { + Items = new List { jobDetails }, + }; + + MockHelper.ResponseMessage = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(JsonConvert.SerializeObject(page)), + }; + + // Cancel Job + IWorkspace workspace = GetWorkspace(); + + List receivedJobs = workspace.ListJobs().ToList(); + + // Validate request + ValidateJobRequestMessage(null, HttpMethod.Get); + + // Validate response + Assert.IsNotNull(receivedJobs); + + Assert.IsNotNull(receivedJobs.Single().Workspace); + + Assert.AreEqual( + expected: jobDetails.ProviderId, + actual: receivedJobs.Single().Details.ProviderId); + } + + private static IWorkspace GetWorkspace() + { + return new Workspace( + subscriptionId: TestConstants.SubscriptionId, + resourceGroupName: TestConstants.ResourceGroupName, + workspaceName: TestConstants.WorkspaceName) + { + // Mock jobs client (only needed for unit tests) + JobsClient = new QuantumClient(MockHelper.GetHttpClientMock(), true) + { + SubscriptionId = TestConstants.SubscriptionId, + ResourceGroupName = TestConstants.ResourceGroupName, + WorkspaceName = TestConstants.WorkspaceName, + BaseUri = new Uri(TestConstants.Endpoint), + }.Jobs, + }; + } + + private static void SetJobResponseMessage(string jobId) + { + MockHelper.ResponseMessage = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(JsonConvert.SerializeObject(CreateJobDetails(jobId))), + }; + } + + private static JobDetails CreateJobDetails(string jobId) + { + return new JobDetails( + id: jobId, + containerUri: "https://uri", + inputDataFormat: "format1", + providerId: TestConstants.ProviderId, + target: "target"); + } + + private static void ValidateJobRequestMessage( + string jobId, + HttpMethod method) + { + var requestMessage = MockHelper.RequestMessage; + + // Url + string expectedUri = $"{TestConstants.Endpoint}/v1.0/subscriptions/{TestConstants.SubscriptionId}/resourceGroups/{TestConstants.ResourceGroupName}/providers/Microsoft.Quantum/workspaces/{TestConstants.WorkspaceName}/jobs/{jobId}"; + Assert.AreEqual( + expected: expectedUri.TrimEnd('/'), + actual: requestMessage.RequestUri.ToString()); + + // Method + Assert.AreEqual( + expected: method, + actual: requestMessage.Method); + } + } +} diff --git a/src/Azure/Azure.Quantum.Client/Authentication/AuthorizationClientHandler.cs b/src/Azure/Azure.Quantum.Client/Authentication/AuthorizationClientHandler.cs new file mode 100644 index 00000000000..8617be8c8dd --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/Authentication/AuthorizationClientHandler.cs @@ -0,0 +1,49 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Azure.Quantum.Authentication +{ + /// + /// Authorization client handler class. + /// + internal class AuthorizationClientHandler : HttpClientHandler + { + private const string AuthorizationHeaderName = "Authorization"; + private const string BearerScheme = "Bearer"; + + private readonly IAccessTokenProvider accessTokenProvider; + + /// + /// Initializes a new instance of the class. + /// + /// The access token provider. + public AuthorizationClientHandler(IAccessTokenProvider accessTokenProvider) + { + this.accessTokenProvider = accessTokenProvider; + } + + /// + /// Creates an instance of based on the information provided in the as an operation that will not block. + /// + /// The HTTP request message. + /// A cancellation token to cancel the operation. + /// + /// The task object representing the asynchronous operation. + /// + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + if (!request.Headers.Contains(AuthorizationHeaderName)) + { + string accessToken = await this.accessTokenProvider.GetAccessTokenAsync(cancellationToken); + request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(BearerScheme, accessToken); + } + + return await base.SendAsync(request, cancellationToken); + } + } +} diff --git a/src/Azure/Azure.Quantum.Client/Authentication/CustomAccessTokenProvider.cs b/src/Azure/Azure.Quantum.Client/Authentication/CustomAccessTokenProvider.cs new file mode 100644 index 00000000000..b20e013b091 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/Authentication/CustomAccessTokenProvider.cs @@ -0,0 +1,128 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System.Linq; +using System.Net.Http; +using System.Security.Authentication; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Azure.Quantum.Utility; +using Microsoft.Identity.Client; +using Microsoft.Identity.Client.Extensions.Msal; + +namespace Microsoft.Azure.Quantum.Authentication +{ + /// + /// This class manually uses MSAL to get an access token. + /// It first tries to get silently, if that doesn't work, it tries to get interactively. + /// + /// + internal class CustomAccessTokenProvider : IAccessTokenProvider + { + private readonly LazyAsync applicationLazy; + private readonly string[] scopes; + private readonly string subscriptionId; + + /// + /// Initializes a new instance of the class. + /// + /// The Subscription Id of the account in use. + public CustomAccessTokenProvider(string subscriptionId) + { + static async Task GetSubscriptionTenantUri(string subscriptionId) + { + var uri = $"https://management.azure.com/subscriptions/{subscriptionId}?api-version=2018-01-01"; + try + { + static string GetTenantUriFromHeader(System.Net.Http.Headers.AuthenticationHeaderValue header) => + header + .Parameter + .Replace("Bearer ", string.Empty) + .Split(",") + .Select(part => part.Split("=")) + .ToDictionary(rg => rg[0], rg => rg[1])["authorization_uri"] + .Trim('\'', '"'); + + using var client = new HttpClient(); + var httpResult = await client.GetAsync(uri); + + return httpResult + .Headers + .WwwAuthenticate + .Select(GetTenantUriFromHeader) + .Single(); + } + catch (System.Exception ex) + { + throw new AuthenticationException("Unable to extract tenantUri!", ex); + } + } + + this.scopes = new string[] { Constants.Aad.Audience }; + this.subscriptionId = subscriptionId; + this.applicationLazy = + new LazyAsync(async () => + { + var application = PublicClientApplicationBuilder + .Create(Constants.Aad.ApplicationId) + .WithDefaultRedirectUri() + .WithAuthority(await GetSubscriptionTenantUri(subscriptionId)) + .Build(); + var cacheHelper = await CreateCacheHelperAsync(); + cacheHelper.RegisterCache(application.UserTokenCache); + return application; + }); + } + + /// + /// Tries to get access token silently, if didn't work, tries to get it interactively. + /// + /// The cancellation token. + /// A encapsulating the access token. + public async Task GetAccessTokenAsync(CancellationToken cancellationToken) + { + var application = applicationLazy.Value; + + try + { + var accounts = await application.GetAccountsAsync(); + + // Try silently first + var result = await application + .AcquireTokenSilent(scopes, accounts.FirstOrDefault()) + .ExecuteAsync(); + + return result.AccessToken; + } + catch (MsalUiRequiredException) + { + // Didn't work, perform interactive logging + var result = await application + .AcquireTokenInteractive(scopes) + .ExecuteAsync(); + + return result.AccessToken; + } + } + + private static async Task CreateCacheHelperAsync() + { + StorageCreationProperties storageProperties; + + storageProperties = new StorageCreationPropertiesBuilder( + Constants.Aad.CacheFileName, + MsalCacheHelper.UserRootDirectory, + Constants.Aad.ApplicationId) + .WithMacKeyChain( + Constants.Aad.KeyChainServiceName, + Constants.Aad.KeyChainAccountName) + .Build(); + + var cacheHelper = await MsalCacheHelper.CreateAsync(storageProperties).ConfigureAwait(false); + + cacheHelper.VerifyPersistence(); + return cacheHelper; + } + } +} \ No newline at end of file diff --git a/src/Azure/Azure.Quantum.Client/Authentication/IAccessTokenProvider.cs b/src/Azure/Azure.Quantum.Client/Authentication/IAccessTokenProvider.cs new file mode 100644 index 00000000000..4ad34c537f4 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/Authentication/IAccessTokenProvider.cs @@ -0,0 +1,22 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Azure.Quantum.Authentication +{ + /// + /// Generic interface to retrieve access token. This is not exposed to the user. + /// + internal interface IAccessTokenProvider + { + /// + /// Gets the access token. + /// + /// The cancellation token. + /// Access token string + Task GetAccessTokenAsync(CancellationToken cancellationToken); + } +} diff --git a/src/Azure/Azure.Quantum.Client/Authentication/StaticAccessTokenProvider.cs b/src/Azure/Azure.Quantum.Client/Authentication/StaticAccessTokenProvider.cs new file mode 100644 index 00000000000..bcbfe47bfd4 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/Authentication/StaticAccessTokenProvider.cs @@ -0,0 +1,38 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Azure.Quantum.Utility; + +namespace Microsoft.Azure.Quantum.Authentication +{ + /// + /// This class accepts an access token string and provides it. + /// + /// + internal class StaticAccessTokenProvider : IAccessTokenProvider + { + private readonly string accessToken; + + /// + /// Initializes a new instance of the class. + /// + public StaticAccessTokenProvider(string accessToken) + { + Ensure.NotNullOrWhiteSpace(accessToken, nameof(accessToken)); + + this.accessToken = accessToken; + } + + /// + /// Returns the static access token. + /// + /// The cancellation token. + public Task GetAccessTokenAsync(CancellationToken cancellationToken) + { + return Task.FromResult(this.accessToken); + } + } +} diff --git a/src/Azure/Azure.Quantum.Client/Authentication/TokenCredentialProvider.cs b/src/Azure/Azure.Quantum.Client/Authentication/TokenCredentialProvider.cs new file mode 100644 index 00000000000..53b8c3fcd60 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/Authentication/TokenCredentialProvider.cs @@ -0,0 +1,44 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System.Threading; +using System.Threading.Tasks; +using Azure.Core; +using Microsoft.Azure.Quantum.Utility; + +namespace Microsoft.Azure.Quantum.Authentication +{ + /// + /// A provider for TokenCredential + /// + /// + internal class TokenCredentialProvider : IAccessTokenProvider + { + private readonly TokenCredential tokenCredential; + private readonly string[] scopes; + + /// + /// Initializes a new instance of the class. + /// + /// The token credential. + public TokenCredentialProvider(TokenCredential tokenCredential) + { + this.tokenCredential = tokenCredential; + this.scopes = new string[] { Constants.Aad.Audience }; + } + + /// + /// Gets the access token. + /// + /// The cancellation token. + /// + /// Access token string + /// + public async Task GetAccessTokenAsync(CancellationToken cancellationToken) + { + AccessToken accessToken = await this.tokenCredential.GetTokenAsync(new TokenRequestContext(this.scopes), cancellationToken); + return accessToken.Token; + } + } +} diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/CloudJob.cs b/src/Azure/Azure.Quantum.Client/JobManagement/CloudJob.cs new file mode 100644 index 00000000000..32137454db7 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/JobManagement/CloudJob.cs @@ -0,0 +1,96 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Azure.Quantum.Client.Models; +using Microsoft.Azure.Quantum.Utility; +using Microsoft.Quantum.Runtime; + +namespace Microsoft.Azure.Quantum +{ + /// + /// Cloud job class. + /// + public class CloudJob : IQuantumMachineJob + { + /// + /// Initializes a new instance of the class. + /// + /// The workspace. + /// The job details. + public CloudJob(IWorkspace workspace, JobDetails jobDetails) + { + Ensure.NotNull(workspace, nameof(workspace)); + Ensure.NotNull(jobDetails, nameof(jobDetails)); + + Workspace = workspace; + Details = jobDetails; + } + + /// + /// Gets whether job execution failed. + /// + public bool Failed => !InProgress && + !Succeeded; + + /// + /// Gets the job id. + /// + public string Id => Details.Id; + + /// + /// Gets whether the job execution has completed. + /// + public bool InProgress => Status != "Cancelled" && + Status != "Failed" && + Status != "Succeeded"; + + /// + /// Gets the status of the submitted job. + /// + public string Status => Details.Status; + + /// + /// Gets whether the job execution completed successfully. + /// + public bool Succeeded => Status == "Succeeded"; + + /// + /// Gets an URI to access the job. + /// + public Uri Uri => throw new NotImplementedException(); + + /// + /// Gets the workspace. + /// + public IWorkspace Workspace { get; private set; } + + /// + /// Gets the job details. + /// + public JobDetails Details { get; private set; } + + /// + /// Refreshes the job. + /// + /// The cancellation token. + public async Task RefreshAsync(CancellationToken cancellationToken = default) + { + CloudJob job = (CloudJob)await this.Workspace.GetJobAsync(this.Details.Id, cancellationToken); + this.Details = job.Details; + } + + /// + /// Cancels the job. + /// + /// The cancellation token. + public async Task CancelAsync(CancellationToken cancellationToken = default) + { + CloudJob job = (CloudJob)await this.Workspace.CancelJobAsync(this.Details.Id, cancellationToken); + this.Details = job.Details; + } + } +} diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/IWorkspace.cs b/src/Azure/Azure.Quantum.Client/JobManagement/IWorkspace.cs new file mode 100644 index 00000000000..91bbb6d3132 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/JobManagement/IWorkspace.cs @@ -0,0 +1,55 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Quantum.Runtime; + +namespace Microsoft.Azure.Quantum +{ + /// + /// IWorkspace interface. + /// + public interface IWorkspace + { + /// + /// Submits the job. + /// + /// The job definition. + /// The cancellation token. + /// The job response. + Task SubmitJobAsync( + CloudJob jobDefinition, + CancellationToken cancellationToken = default); + + /// + /// Cancels the job. + /// + /// The job identifier. + /// The cancellation token. + /// The job response. + Task CancelJobAsync( + string jobId, + CancellationToken cancellationToken = default); + + /// + /// Gets the job. + /// + /// The job identifier. + /// The cancellation token. + /// The job object. + Task GetJobAsync( + string jobId, + CancellationToken cancellationToken = default); + + /// + /// Lists the jobs. + /// + /// The cancellation token. + /// List of jobs + Task> ListJobsAsync( + CancellationToken cancellationToken = default); + } +} diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/Workspace.cs b/src/Azure/Azure.Quantum.Client/JobManagement/Workspace.cs new file mode 100644 index 00000000000..9af37ffd70e --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/JobManagement/Workspace.cs @@ -0,0 +1,181 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Azure.Core; +using Microsoft.Azure.Quantum.Authentication; +using Microsoft.Azure.Quantum.Client; +using Microsoft.Azure.Quantum.Client.Models; +using Microsoft.Azure.Quantum.Storage; +using Microsoft.Azure.Quantum.Utility; +using Microsoft.Quantum.Runtime; + +namespace Microsoft.Azure.Quantum +{ + /// + /// Workspace class. + /// + /// + public class Workspace : IWorkspace + { + /// + /// Initializes a new instance of the class. + /// + /// The subscription identifier. + /// Name of the resource group. + /// Name of the workspace. + /// The token credential. + /// The base URI. + public Workspace( + string subscriptionId, + string resourceGroupName, + string workspaceName, + TokenCredential tokenCredential = null, + Uri baseUri = null) + : this( + subscriptionId, + resourceGroupName, + workspaceName, + tokenCredential == null ? null : new TokenCredentialProvider(tokenCredential), + baseUri) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The subscription identifier. + /// Name of the resource group. + /// Name of the workspace. + /// The access token. + /// The base URI. + public Workspace( + string subscriptionId, + string resourceGroupName, + string workspaceName, + string accessToken, + Uri baseUri = null) + : this( + subscriptionId, + resourceGroupName, + workspaceName, + new StaticAccessTokenProvider(accessToken), + baseUri) + { + } + + private Workspace( + string subscriptionId, + string resourceGroupName, + string workspaceName, + IAccessTokenProvider accessTokenProvider, + Uri baseUri = null) + { + Ensure.NotNullOrWhiteSpace(subscriptionId, nameof(subscriptionId)); + Ensure.NotNullOrWhiteSpace(resourceGroupName, nameof(resourceGroupName)); + Ensure.NotNullOrWhiteSpace(workspaceName, nameof(workspaceName)); + + accessTokenProvider = accessTokenProvider ?? new CustomAccessTokenProvider(subscriptionId); + + Ensure.NotNull(accessTokenProvider, nameof(accessTokenProvider)); + + this.JobsClient = new QuantumClient(new AuthorizationClientHandler(accessTokenProvider)) + { + BaseUri = baseUri ?? new Uri(Constants.DefaultBaseUri), + SubscriptionId = subscriptionId, + ResourceGroupName = resourceGroupName, + WorkspaceName = workspaceName, + }.Jobs; + } + + /// + /// Gets or sets the jobs client. + /// Internal only. + /// + /// + /// The jobs client. + /// + internal IJobsOperations JobsClient { get; set; } + + /// + /// Submits the job. + /// + /// The job definition. + /// The cancellation token. + /// + /// The job response. + /// + public async Task SubmitJobAsync( + CloudJob jobDefinition, + CancellationToken cancellationToken = default) + { + Ensure.NotNull(jobDefinition, nameof(jobDefinition)); + Ensure.NotNullOrWhiteSpace(jobDefinition.Details.Id, nameof(jobDefinition.Details.Id)); + + JobDetails jobDetails = await this.JobsClient.PutAsync( + jobId: jobDefinition.Details.Id, + jobDefinition: jobDefinition.Details, + cancellationToken: cancellationToken); + + return new CloudJob(this, jobDetails); + } + + /// + /// Cancels the job. + /// + /// The job identifier. + /// The cancellation token. + /// Cloud job. + public async Task CancelJobAsync(string jobId, CancellationToken cancellationToken = default) + { + Ensure.NotNullOrWhiteSpace(jobId, nameof(jobId)); + + JobDetails jobDetails = await this.JobsClient.DeleteAsync( + jobId: jobId, + cancellationToken: cancellationToken); + + return new CloudJob(this, jobDetails); + } + + /// + /// Gets the job. + /// + /// The job identifier. + /// The cancellation token. + /// + /// The job response. + /// + public async Task GetJobAsync(string jobId, CancellationToken cancellationToken = default) + { + Ensure.NotNullOrWhiteSpace(jobId, nameof(jobId)); + + JobDetails jobDetails = await this.JobsClient.GetAsync( + jobId: jobId, + cancellationToken: cancellationToken); + + return new CloudJob(this, jobDetails); + } + + /// + /// Lists the jobs. + /// + /// The cancellation token. + /// + /// List of jobs. + /// + public async Task> ListJobsAsync(CancellationToken cancellationToken = default) + { + var jobs = await this.JobsClient.ListAsync( + cancellationToken: cancellationToken); + + return jobs + .Select(details => new CloudJob(this, details)); + } + } +} diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/WorkspaceExtensions.cs b/src/Azure/Azure.Quantum.Client/JobManagement/WorkspaceExtensions.cs new file mode 100644 index 00000000000..4f5c60589ec --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/JobManagement/WorkspaceExtensions.cs @@ -0,0 +1,59 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System.Collections.Generic; + +namespace Microsoft.Azure.Quantum +{ + /// + /// Extension methods for Workspace. + /// + public static class WorkspaceExtensions + { + /// + /// Submits the job. + /// + /// The workspace. + /// The job definition. + /// The job response. + public static CloudJob SubmitJob( + this IWorkspace workspace, + CloudJob jobDefinition) + { + return workspace.SubmitJobAsync(jobDefinition).GetAwaiter().GetResult(); + } + + /// + /// Cancels the job. + /// + /// The workspace. + /// The job identifier. + /// The job response. + public static CloudJob CancelJob(this IWorkspace workspace, string jobId) + { + return workspace.CancelJobAsync(jobId).GetAwaiter().GetResult(); + } + + /// + /// Gets the job. + /// + /// The workspace. + /// The job identifier. + /// The job response. + public static CloudJob GetJob(this IWorkspace workspace, string jobId) + { + return workspace.GetJobAsync(jobId).GetAwaiter().GetResult(); + } + + /// + /// Lists the jobs. + /// + /// The workspace. + /// List of job identifiers. + public static IEnumerable ListJobs(this IWorkspace workspace) + { + return workspace.ListJobsAsync().GetAwaiter().GetResult(); + } + } +} diff --git a/src/Azure/Azure.Quantum.Client/Machine/QuantumMachineFactory.cs b/src/Azure/Azure.Quantum.Client/Machine/QuantumMachineFactory.cs new file mode 100644 index 00000000000..d6ca6671156 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/Machine/QuantumMachineFactory.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Threading.Tasks; +using Microsoft.Quantum.Runtime; + +namespace Microsoft.Azure.Quantum +{ + public static class QuantumMachineFactory + { + /// + /// Creates a quantum machine for job submission to an Azure Quantum workspace. + /// + /// The Azure Quantum workspace. + /// The execution target for job submission. + /// The connection string for the Azure storage account. + /// A quantum machine for job submission targeting targetName. + public static IQuantumMachine? CreateMachine(Workspace workspace, string targetName, string storageAccountConnectionString) + { + if (string.IsNullOrEmpty(targetName)) + { + return null; + } + + if (targetName.StartsWith("ionq.")) + { + var ionQType = Type.GetType( + "Microsoft.Quantum.Providers.IonQ.Targets.IonQQuantumMachine, Microsoft.Quantum.Providers.IonQ", + throwOnError: true); + return (IQuantumMachine)Activator.CreateInstance( + ionQType, targetName, storageAccountConnectionString, workspace); + } + + return null; + } + } +} \ No newline at end of file diff --git a/src/Azure/Azure.Quantum.Client/Microsoft.Azure.Quantum.Client.csproj b/src/Azure/Azure.Quantum.Client/Microsoft.Azure.Quantum.Client.csproj new file mode 100644 index 00000000000..d4b6c807b9e --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/Microsoft.Azure.Quantum.Client.csproj @@ -0,0 +1,49 @@ + + + + + + netstandard2.1 + x64 + Client library for Azure Quantum. + + + + https://docs.microsoft.com/quantum/ + See: https://docs.microsoft.com/quantum/relnotes/ + MIT + https://github.com/microsoft/qsharp-runtime + Azure Quantum Q# Qsharp + https://secure.gravatar.com/avatar/bd1f02955b2853ba0a3b1cdc2434e8ec.png + true + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + ..\Common\StyleCop.ruleset + + + + + + + + + + + diff --git a/src/Azure/Azure.Quantum.Client/Properties/AssemblyInfo.cs b/src/Azure/Azure.Quantum.Client/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..098be651ff1 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/Properties/AssemblyInfo.cs @@ -0,0 +1,7 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Microsoft.Azure.Quantum.Client.Test" + DelaySign.PUBLIC_KEY)] \ No newline at end of file diff --git a/src/Azure/Azure.Quantum.Client/Storage/IJobStorageHelper.cs b/src/Azure/Azure.Quantum.Client/Storage/IJobStorageHelper.cs new file mode 100644 index 00000000000..5e30e1f20f4 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/Storage/IJobStorageHelper.cs @@ -0,0 +1,62 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Bond; + +namespace Microsoft.Azure.Quantum.Storage +{ + /// + /// Job storage helper. + /// + public interface IJobStorageHelper + { + /// + /// Gets the underlying storage helper. + /// + IStorageHelper StorageHelper { get; } + + /// + /// Uploads the job input. + /// + /// The job id. + /// The input. + /// Serialization protocol of the input to upload. + /// The cancellation token. + /// Container uri + Input uri. + Task<(string containerUri, string inputUri)> UploadJobInputAsync( + string jobId, + Stream input, + ProtocolType protocol = ProtocolType.COMPACT_PROTOCOL, + CancellationToken cancellationToken = default); + + /// + /// Uploads the job program output mapping. + /// + /// The job id. + /// The job program output mapping. + /// Serialization protocol of the mapping to upload. + /// The cancellation token. + /// Container uri + Mapping uri. + Task<(string containerUri, string mappingUri)> UploadJobMappingAsync( + string jobId, + Stream mapping, + ProtocolType protocol = ProtocolType.COMPACT_PROTOCOL, + CancellationToken cancellationToken = default); + + /// + /// Downloads the job's execution output. + /// + /// The job id. + /// The destination stream. + /// The cancellation token. + /// Serialization protocol of the downloaded execution output. + Task DownloadJobOutputAsync( + string jobId, + Stream destination, + CancellationToken cancellationToken = default); + } +} diff --git a/src/Azure/Azure.Quantum.Client/Storage/IStorageHelper.cs b/src/Azure/Azure.Quantum.Client/Storage/IStorageHelper.cs new file mode 100644 index 00000000000..72eb5db7f23 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/Storage/IStorageHelper.cs @@ -0,0 +1,72 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Bond; +using Microsoft.WindowsAzure.Storage.Blob; + +namespace Microsoft.Azure.Quantum.Storage +{ + public interface IStorageHelper + { + /// + /// Downloads the BLOB. + /// + /// Name of the container. + /// Name of the BLOB. + /// The destination. + /// The cancellation token. + /// Serialization protocol of the downloaded BLOB. + Task DownloadBlobAsync( + string containerName, + string blobName, + Stream destination, + CancellationToken cancellationToken = default); + + /// + /// Uploads the BLOB. + /// + /// Name of the container. + /// Name of the BLOB. + /// The input. + /// Serialization protocol of the BLOB to upload. + /// The cancellation token. + /// async task. + Task UploadBlobAsync( + string containerName, + string blobName, + Stream input, + ProtocolType protocol = ProtocolType.COMPACT_PROTOCOL, + CancellationToken cancellationToken = default); + + /// + /// Gets the BLOB sas URI. + /// + /// Name of the container. + /// Name of the BLOB. + /// The expiry interval. + /// The permissions. + /// Blob uri. + string GetBlobSasUri( + string containerName, + string blobName, + TimeSpan expiryInterval, + SharedAccessBlobPermissions permissions); + + /// + /// Gets the BLOB container sas URI. + /// + /// Name of the container. + /// The expiry interval. + /// The permissions. + /// Container uri. + string GetBlobContainerSasUri( + string containerName, + TimeSpan expiryInterval, + SharedAccessBlobPermissions permissions); + } +} diff --git a/src/Azure/Azure.Quantum.Client/Storage/JobStorageHelper.cs b/src/Azure/Azure.Quantum.Client/Storage/JobStorageHelper.cs new file mode 100644 index 00000000000..ac52ac1ae4d --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/Storage/JobStorageHelper.cs @@ -0,0 +1,133 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Bond; +using Microsoft.Azure.Quantum.Utility; +using Microsoft.WindowsAzure.Storage.Blob; + +namespace Microsoft.Azure.Quantum.Storage +{ + public class JobStorageHelper : IJobStorageHelper + { + private readonly TimeSpan expiryInterval; + + /// + /// Initializes a new instance of the class. + /// + /// The connection string. + public JobStorageHelper(string connectionString) + { + this.StorageHelper = new StorageHelper(connectionString); + this.expiryInterval = TimeSpan.FromDays(Constants.Storage.ExpiryIntervalInDays); + } + + /// + /// Gets the underlying storage helper. + /// + public IStorageHelper StorageHelper { get; } + + /// + /// Uploads the job input. + /// + /// The job id. + /// The input. + /// Serialization protocol of the input to upload. + /// The cancellation token. + /// + /// Container uri + Input uri. + /// + public async Task<(string containerUri, string inputUri)> UploadJobInputAsync( + string jobId, + Stream input, + ProtocolType protocol = ProtocolType.COMPACT_PROTOCOL, + CancellationToken cancellationToken = default) + { + string containerName = GetContainerName(jobId); + await this.StorageHelper.UploadBlobAsync( + containerName, + Constants.Storage.InputBlobName, + input, + protocol, + cancellationToken); + + string containerUri = this.StorageHelper.GetBlobContainerSasUri( + containerName, + this.expiryInterval, + SharedAccessBlobPermissions.Create | SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.Read); + + string inputUri = this.StorageHelper.GetBlobSasUri( + containerName, + Constants.Storage.InputBlobName, + this.expiryInterval, + SharedAccessBlobPermissions.Read); + + return (containerUri, inputUri); + } + + /// + /// Uploads the job program output mapping. + /// + /// The job id. + /// The job program output mapping. + /// Serialization protocol of the mapping to upload. + /// The cancellation token. + /// Container uri + Mapping uri. + public async Task<(string containerUri, string mappingUri)> UploadJobMappingAsync( + string jobId, + Stream mapping, + ProtocolType protocol = ProtocolType.COMPACT_PROTOCOL, + CancellationToken cancellationToken = default) + { + string containerName = GetContainerName(jobId); + await this.StorageHelper.UploadBlobAsync( + containerName, + Constants.Storage.MappingBlobName, + mapping, + protocol, + cancellationToken); + + string containerUri = this.StorageHelper.GetBlobContainerSasUri( + containerName, + this.expiryInterval, + SharedAccessBlobPermissions.Create | SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.Read); + + string mappingUri = this.StorageHelper.GetBlobSasUri( + containerName, + Constants.Storage.MappingBlobName, + this.expiryInterval, + SharedAccessBlobPermissions.Read); + + return (containerUri, mappingUri); + } + + /// + /// Downloads the job's execution output. + /// + /// The job id. + /// The destination stream. + /// The cancellation token. + /// Serialization protocol of the downloaded execution output. + public Task DownloadJobOutputAsync( + string jobId, + Stream destination, + CancellationToken cancellationToken = default) + { + string containerName = GetContainerName(jobId); + return this.StorageHelper.DownloadBlobAsync( + containerName, + "rawOutputData", // TODO: 14643 + destination, + cancellationToken); + } + + private static string GetContainerName(string jobId) + { + return Constants.Storage.ContainerNamePrefix + jobId.ToLowerInvariant(); + } + } +} diff --git a/src/Azure/Azure.Quantum.Client/Storage/StorageHelper.cs b/src/Azure/Azure.Quantum.Client/Storage/StorageHelper.cs new file mode 100644 index 00000000000..350a97f0f10 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/Storage/StorageHelper.cs @@ -0,0 +1,144 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Azure.Storage.Blobs; +using Azure.Storage.Blobs.Models; +using Bond; +using Microsoft.WindowsAzure.Storage; +using Microsoft.WindowsAzure.Storage.Blob; + +namespace Microsoft.Azure.Quantum.Storage +{ + internal class StorageHelper : IStorageHelper + { + private readonly string connectionString; + private readonly CloudStorageAccount storageAccount; + + /// + /// Initializes a new instance of the class. + /// + /// The connection string. + public StorageHelper(string connectionString) + { + this.connectionString = connectionString; + this.storageAccount = CloudStorageAccount.Parse(connectionString); + } + + /// + /// Downloads the BLOB. + /// + /// Name of the container. + /// Name of the BLOB. + /// The destination. + /// The cancellation token. + /// Serialization protocol of the downloaded BLOB. + public async Task DownloadBlobAsync( + string containerName, + string blobName, + Stream destination, + CancellationToken cancellationToken = default) + { + BlobClient blob = await this.GetBlobClient(containerName, blobName, false, cancellationToken); + await blob.DownloadToAsync(destination, cancellationToken); + return ProtocolType.COMPACT_PROTOCOL; + } + + /// + /// Uploads the BLOB. + /// + /// Name of the container. + /// Name of the BLOB. + /// The input. + /// Serialization protocol of the BLOB to upload. + /// The cancellation token. + /// Async task. + public async Task UploadBlobAsync( + string containerName, + string blobName, + Stream input, + ProtocolType protocol = ProtocolType.COMPACT_PROTOCOL, + CancellationToken cancellationToken = default) + { + BlobClient blob = await this.GetBlobClient(containerName, blobName, true, cancellationToken); + await blob.UploadAsync(input, overwrite: true, cancellationToken); + } + + /// + /// Gets the BLOB sas URI. + /// + /// Name of the container. + /// Name of the BLOB. + /// The expiry interval. + /// The permissions. + /// Blob uri. + public string GetBlobSasUri( + string containerName, + string blobName, + TimeSpan expiryInterval, + SharedAccessBlobPermissions permissions) + { + SharedAccessBlobPolicy adHocSAS = CreateSharedAccessBlobPolicy(expiryInterval, permissions); + + CloudBlob blob = this.storageAccount + .CreateCloudBlobClient() + .GetContainerReference(containerName) + .GetBlobReference(blobName); + + return blob.Uri + blob.GetSharedAccessSignature(adHocSAS); + } + + /// + /// Gets the BLOB container sas URI. + /// + /// Name of the container. + /// The expiry interval. + /// The permissions. + /// Container uri. + public string GetBlobContainerSasUri( + string containerName, + TimeSpan expiryInterval, + SharedAccessBlobPermissions permissions) + { + SharedAccessBlobPolicy adHocPolicy = CreateSharedAccessBlobPolicy(expiryInterval, permissions); + + // Generate the shared access signature on the container, setting the constraints directly on the signature. + CloudBlobContainer container = this.storageAccount.CreateCloudBlobClient().GetContainerReference(containerName); + return container.Uri + container.GetSharedAccessSignature(adHocPolicy, null); + } + + private async Task GetBlobClient( + string containerName, + string blobName, + bool createContainer, + CancellationToken cancellationToken) + { + BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString); + BlobContainerClient blobContainerClient = blobServiceClient.GetBlobContainerClient(containerName); + + if (createContainer) + { + await blobContainerClient.CreateIfNotExistsAsync(PublicAccessType.Blob, cancellationToken: cancellationToken); + } + + return blobContainerClient.GetBlobClient(blobName); + } + + private static SharedAccessBlobPolicy CreateSharedAccessBlobPolicy( + TimeSpan expiryInterval, + SharedAccessBlobPermissions permissions) + { + return new SharedAccessBlobPolicy() + { + // When the start time for the SAS is omitted, the start time is assumed to be the time when the storage service receives the request. + // Omitting the start time for a SAS that is effective immediately helps to avoid clock skew. + SharedAccessExpiryTime = DateTime.UtcNow.Add(expiryInterval), + Permissions = permissions, + }; + } + } +} diff --git a/src/Azure/Azure.Quantum.Client/Utility/Constants.cs b/src/Azure/Azure.Quantum.Client/Utility/Constants.cs new file mode 100644 index 00000000000..8dde31e9a76 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/Utility/Constants.cs @@ -0,0 +1,35 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +namespace Microsoft.Azure.Quantum.Utility +{ + internal static class Constants + { + internal const string DefaultBaseUri = "https://app-jobscheduler-prod.azurewebsites.net/"; + + internal static class Aad + { + internal const string ApplicationId = "84ba0947-6c53-4dd2-9ca9-b3694761521b"; + + // TODO: Confirm audience + internal const string Audience = "https://quantum.microsoft.com/Jobs.ReadWrite"; + + // Cache settings + internal const string CacheFileName = "msal_cache.dat"; + + // Mac Keychain settings + internal const string KeyChainServiceName = "msal_service"; + internal const string KeyChainAccountName = "msal_account"; + } + + internal static class Storage + { + internal const string ContainerNamePrefix = "quantum-job-"; + internal const string InputBlobName = "inputData"; + internal const string MappingBlobName = "mappingData"; + internal const string OutputBlobName = "outputData"; + internal const int ExpiryIntervalInDays = 14; + } + } +} diff --git a/src/Azure/Azure.Quantum.Client/Utility/Ensure.cs b/src/Azure/Azure.Quantum.Client/Utility/Ensure.cs new file mode 100644 index 00000000000..d9045d37790 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/Utility/Ensure.cs @@ -0,0 +1,40 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System; + +namespace Microsoft.Azure.Quantum.Utility +{ + /// + /// Validation class. + /// + internal static class Ensure + { + /// + /// Ensures not null or whitespace. + /// + /// The value. + /// The name. + public static void NotNullOrWhiteSpace(string value, string name) + { + if (string.IsNullOrWhiteSpace(value)) + { + throw new ArgumentException(name); + } + } + + /// + /// Ensures not null. + /// + /// The value. + /// The name. + public static void NotNull(object value, string name) + { + if (value == null) + { + throw new ArgumentNullException(name); + } + } + } +} diff --git a/src/Azure/Azure.Quantum.Client/Utility/LazyAsync.cs b/src/Azure/Azure.Quantum.Client/Utility/LazyAsync.cs new file mode 100644 index 00000000000..99af6446af0 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/Utility/LazyAsync.cs @@ -0,0 +1,44 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// Based on https://gist.github.com/johnazariah/ab269f7e005d538ed706b7a9cdb15bf1 + +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; + +namespace Microsoft.Azure.Quantum.Utility +{ + internal sealed class LazyAsync + { + private readonly Lazy> instance; + private readonly Lazy valueL; + + /// + /// Constructor for use with synchronous factories + /// + public LazyAsync(Func synchronousFactory) + : this(new Lazy>(() => Task.Run(synchronousFactory))) + { + } + + /// + /// Constructor for use with asynchronous factories + /// + public LazyAsync(Func> asynchronousFactory) + : this(new Lazy>(() => asynchronousFactory())) + { + } + + // private constructor which sets both fields + private LazyAsync(Lazy> instance) + { + this.instance = instance; + this.valueL = new Lazy(() => this.instance.Value.GetAwaiter().GetResult()); + } + + public T Value => valueL.Value; + + public TaskAwaiter GetAwaiter() => instance.Value.GetAwaiter(); + } +} \ No newline at end of file diff --git a/src/Azure/Azure.Quantum.Client/generated/IJobsOperations.cs b/src/Azure/Azure.Quantum.Client/generated/IJobsOperations.cs new file mode 100644 index 00000000000..74fd4d11463 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/generated/IJobsOperations.cs @@ -0,0 +1,136 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. +// + +namespace Microsoft.Azure.Quantum.Client +{ + using Microsoft.Rest; + using Microsoft.Rest.Azure; + using Models; + using System.Collections; + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + + /// + /// JobsOperations operations. + /// + public partial interface IJobsOperations + { + /// + /// List jobs. + /// + /// + /// The headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// Thrown when the operation returned an invalid status code + /// + /// + /// Thrown when unable to deserialize the response + /// + /// + /// Thrown when a required parameter is null + /// + Task>> ListWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); + /// + /// Get job by id + /// + /// + /// Id of the job. + /// + /// + /// The headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// Thrown when the operation returned an invalid status code + /// + /// + /// Thrown when unable to deserialize the response + /// + /// + /// Thrown when a required parameter is null + /// + Task> GetWithHttpMessagesAsync(string jobId, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); + /// + /// Create a job. + /// + /// + /// Id of the job. + /// + /// + /// + /// + /// The headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// Thrown when the operation returned an invalid status code + /// + /// + /// Thrown when unable to deserialize the response + /// + /// + /// Thrown when a required parameter is null + /// + Task> PutWithHttpMessagesAsync(string jobId, JobDetails jobDefinition, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); + /// + /// Delete a job. + /// + /// + /// Id of the job. + /// + /// + /// The headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// Thrown when the operation returned an invalid status code + /// + /// + /// Thrown when unable to deserialize the response + /// + /// + /// Thrown when a required parameter is null + /// + Task> DeleteWithHttpMessagesAsync(string jobId, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); + /// + /// List jobs. + /// + /// + /// The NextLink from the previous successful call to List operation. + /// + /// + /// The headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// Thrown when the operation returned an invalid status code + /// + /// + /// Thrown when unable to deserialize the response + /// + /// + /// Thrown when a required parameter is null + /// + Task>> ListNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); + } +} diff --git a/src/Azure/Azure.Quantum.Client/generated/IProvidersOperations.cs b/src/Azure/Azure.Quantum.Client/generated/IProvidersOperations.cs new file mode 100644 index 00000000000..2cc635ca7c8 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/generated/IProvidersOperations.cs @@ -0,0 +1,68 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. +// + +namespace Microsoft.Azure.Quantum.Client +{ + using Microsoft.Rest; + using Microsoft.Rest.Azure; + using Models; + using System.Collections; + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + + /// + /// ProvidersOperations operations. + /// + public partial interface IProvidersOperations + { + /// + /// Get provider status. + /// + /// + /// The headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// Thrown when the operation returned an invalid status code + /// + /// + /// Thrown when unable to deserialize the response + /// + /// + /// Thrown when a required parameter is null + /// + Task>> GetStatusWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); + /// + /// Get provider status. + /// + /// + /// The NextLink from the previous successful call to List operation. + /// + /// + /// The headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// Thrown when the operation returned an invalid status code + /// + /// + /// Thrown when unable to deserialize the response + /// + /// + /// Thrown when a required parameter is null + /// + Task>> GetStatusNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); + } +} diff --git a/src/Azure/Azure.Quantum.Client/generated/IQuantumClient.cs b/src/Azure/Azure.Quantum.Client/generated/IQuantumClient.cs new file mode 100644 index 00000000000..f4ae50f1a37 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/generated/IQuantumClient.cs @@ -0,0 +1,89 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. +// + +namespace Microsoft.Azure.Quantum.Client +{ + using Microsoft.Rest; + using Microsoft.Rest.Azure; + using Models; + using Newtonsoft.Json; + + /// + /// Azure Quantum REST API client + /// + public partial interface IQuantumClient : System.IDisposable + { + /// + /// The base URI of the service. + /// + System.Uri BaseUri { get; set; } + + /// + /// Gets or sets json serialization settings. + /// + JsonSerializerSettings SerializationSettings { get; } + + /// + /// Gets or sets json deserialization settings. + /// + JsonSerializerSettings DeserializationSettings { get; } + + /// + /// Credentials needed for the client to connect to Azure. + /// + ServiceClientCredentials Credentials { get; } + + /// + /// The Azure subscription ID. This is a GUID-formatted string (e.g. + /// 00000000-0000-0000-0000-000000000000) + /// + string SubscriptionId { get; set; } + + /// + /// Name of an Azure resource group. + /// + string ResourceGroupName { get; set; } + + /// + /// Name of the workspace. + /// + string WorkspaceName { get; set; } + + /// + /// The preferred language for the response. + /// + string AcceptLanguage { get; set; } + + /// + /// The retry timeout in seconds for Long Running Operations. Default + /// value is 30. + /// + int? LongRunningOperationRetryTimeout { get; set; } + + /// + /// Whether a unique x-ms-client-request-id should be generated. When + /// set to true a unique x-ms-client-request-id value is generated and + /// included in each request. Default is true. + /// + bool? GenerateClientRequestId { get; set; } + + + /// + /// Gets the IJobsOperations. + /// + IJobsOperations Jobs { get; } + + /// + /// Gets the IProvidersOperations. + /// + IProvidersOperations Providers { get; } + + } +} diff --git a/src/Azure/Azure.Quantum.Client/generated/JobsOperations.cs b/src/Azure/Azure.Quantum.Client/generated/JobsOperations.cs new file mode 100644 index 00000000000..81697920294 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/generated/JobsOperations.cs @@ -0,0 +1,994 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. +// + +namespace Microsoft.Azure.Quantum.Client +{ + using Microsoft.Rest; + using Microsoft.Rest.Azure; + using Models; + using Newtonsoft.Json; + using System.Collections; + using System.Collections.Generic; + using System.Linq; + using System.Net; + using System.Net.Http; + using System.Threading; + using System.Threading.Tasks; + + /// + /// JobsOperations operations. + /// + internal partial class JobsOperations : IServiceOperations, IJobsOperations + { + /// + /// Initializes a new instance of the JobsOperations class. + /// + /// + /// Reference to the service client. + /// + /// + /// Thrown when a required parameter is null + /// + internal JobsOperations(QuantumClient client) + { + if (client == null) + { + throw new System.ArgumentNullException("client"); + } + Client = client; + } + + /// + /// Gets a reference to the QuantumClient + /// + public QuantumClient Client { get; private set; } + + /// + /// List jobs. + /// + /// + /// Headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// Thrown when the operation returned an invalid status code + /// + /// + /// Thrown when unable to deserialize the response + /// + /// + /// Thrown when a required parameter is null + /// + /// + /// Thrown when a required parameter is null + /// + /// + /// A response object containing the response body and response headers. + /// + public async Task>> ListWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) + { + if (Client.SubscriptionId == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.SubscriptionId"); + } + if (Client.ResourceGroupName == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.ResourceGroupName"); + } + if (Client.WorkspaceName == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.WorkspaceName"); + } + // Tracing + bool _shouldTrace = ServiceClientTracing.IsEnabled; + string _invocationId = null; + if (_shouldTrace) + { + _invocationId = ServiceClientTracing.NextInvocationId.ToString(); + Dictionary tracingParameters = new Dictionary(); + tracingParameters.Add("cancellationToken", cancellationToken); + ServiceClientTracing.Enter(_invocationId, this, "List", tracingParameters); + } + // Construct URL + var _baseUrl = Client.BaseUri.AbsoluteUri; + var _url = new System.Uri(new System.Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "v1.0/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Quantum/workspaces/{workspaceName}/jobs").ToString(); + _url = _url.Replace("{subscriptionId}", System.Uri.EscapeDataString(Client.SubscriptionId)); + _url = _url.Replace("{resourceGroupName}", System.Uri.EscapeDataString(Client.ResourceGroupName)); + _url = _url.Replace("{workspaceName}", System.Uri.EscapeDataString(Client.WorkspaceName)); + List _queryParameters = new List(); + if (_queryParameters.Count > 0) + { + _url += (_url.Contains("?") ? "&" : "?") + string.Join("&", _queryParameters); + } + // Create HTTP transport objects + var _httpRequest = new HttpRequestMessage(); + HttpResponseMessage _httpResponse = null; + _httpRequest.Method = new HttpMethod("GET"); + _httpRequest.RequestUri = new System.Uri(_url); + // Set Headers + if (Client.GenerateClientRequestId != null && Client.GenerateClientRequestId.Value) + { + _httpRequest.Headers.TryAddWithoutValidation("x-ms-client-request-id", System.Guid.NewGuid().ToString()); + } + if (Client.AcceptLanguage != null) + { + if (_httpRequest.Headers.Contains("accept-language")) + { + _httpRequest.Headers.Remove("accept-language"); + } + _httpRequest.Headers.TryAddWithoutValidation("accept-language", Client.AcceptLanguage); + } + + + if (customHeaders != null) + { + foreach(var _header in customHeaders) + { + if (_httpRequest.Headers.Contains(_header.Key)) + { + _httpRequest.Headers.Remove(_header.Key); + } + _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + } + } + + // Serialize Request + string _requestContent = null; + // Set Credentials + if (Client.Credentials != null) + { + cancellationToken.ThrowIfCancellationRequested(); + await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + } + // Send Request + if (_shouldTrace) + { + ServiceClientTracing.SendRequest(_invocationId, _httpRequest); + } + cancellationToken.ThrowIfCancellationRequested(); + _httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + if (_shouldTrace) + { + ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); + } + HttpStatusCode _statusCode = _httpResponse.StatusCode; + cancellationToken.ThrowIfCancellationRequested(); + string _responseContent = null; + if ((int)_statusCode != 200) + { + var ex = new CloudException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); + try + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + CloudError _errorBody = Microsoft.Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); + if (_errorBody != null) + { + ex = new CloudException(_errorBody.Message); + ex.Body = _errorBody; + } + } + catch (JsonException) + { + // Ignore the exception + } + ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); + ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); + if (_httpResponse.Headers.Contains("x-ms-request-id")) + { + ex.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); + } + if (_shouldTrace) + { + ServiceClientTracing.Error(_invocationId, ex); + } + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw ex; + } + // Create Result + var _result = new AzureOperationResponse>(); + _result.Request = _httpRequest; + _result.Response = _httpResponse; + if (_httpResponse.Headers.Contains("x-ms-request-id")) + { + _result.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); + } + // Deserialize Response + if ((int)_statusCode == 200) + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + _result.Body = Microsoft.Rest.Serialization.SafeJsonConvert.DeserializeObject>(_responseContent, Client.DeserializationSettings); + } + catch (JsonException ex) + { + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + } + } + if (_shouldTrace) + { + ServiceClientTracing.Exit(_invocationId, _result); + } + return _result; + } + + /// + /// Get job by id + /// + /// + /// Id of the job. + /// + /// + /// Headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// Thrown when the operation returned an invalid status code + /// + /// + /// Thrown when unable to deserialize the response + /// + /// + /// Thrown when a required parameter is null + /// + /// + /// Thrown when a required parameter is null + /// + /// + /// A response object containing the response body and response headers. + /// + public async Task> GetWithHttpMessagesAsync(string jobId, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) + { + if (Client.SubscriptionId == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.SubscriptionId"); + } + if (Client.ResourceGroupName == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.ResourceGroupName"); + } + if (Client.WorkspaceName == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.WorkspaceName"); + } + if (jobId == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "jobId"); + } + // Tracing + bool _shouldTrace = ServiceClientTracing.IsEnabled; + string _invocationId = null; + if (_shouldTrace) + { + _invocationId = ServiceClientTracing.NextInvocationId.ToString(); + Dictionary tracingParameters = new Dictionary(); + tracingParameters.Add("jobId", jobId); + tracingParameters.Add("cancellationToken", cancellationToken); + ServiceClientTracing.Enter(_invocationId, this, "Get", tracingParameters); + } + // Construct URL + var _baseUrl = Client.BaseUri.AbsoluteUri; + var _url = new System.Uri(new System.Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "v1.0/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Quantum/workspaces/{workspaceName}/jobs/{jobId}").ToString(); + _url = _url.Replace("{subscriptionId}", System.Uri.EscapeDataString(Client.SubscriptionId)); + _url = _url.Replace("{resourceGroupName}", System.Uri.EscapeDataString(Client.ResourceGroupName)); + _url = _url.Replace("{workspaceName}", System.Uri.EscapeDataString(Client.WorkspaceName)); + _url = _url.Replace("{jobId}", System.Uri.EscapeDataString(jobId)); + List _queryParameters = new List(); + if (_queryParameters.Count > 0) + { + _url += (_url.Contains("?") ? "&" : "?") + string.Join("&", _queryParameters); + } + // Create HTTP transport objects + var _httpRequest = new HttpRequestMessage(); + HttpResponseMessage _httpResponse = null; + _httpRequest.Method = new HttpMethod("GET"); + _httpRequest.RequestUri = new System.Uri(_url); + // Set Headers + if (Client.GenerateClientRequestId != null && Client.GenerateClientRequestId.Value) + { + _httpRequest.Headers.TryAddWithoutValidation("x-ms-client-request-id", System.Guid.NewGuid().ToString()); + } + if (Client.AcceptLanguage != null) + { + if (_httpRequest.Headers.Contains("accept-language")) + { + _httpRequest.Headers.Remove("accept-language"); + } + _httpRequest.Headers.TryAddWithoutValidation("accept-language", Client.AcceptLanguage); + } + + + if (customHeaders != null) + { + foreach(var _header in customHeaders) + { + if (_httpRequest.Headers.Contains(_header.Key)) + { + _httpRequest.Headers.Remove(_header.Key); + } + _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + } + } + + // Serialize Request + string _requestContent = null; + // Set Credentials + if (Client.Credentials != null) + { + cancellationToken.ThrowIfCancellationRequested(); + await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + } + // Send Request + if (_shouldTrace) + { + ServiceClientTracing.SendRequest(_invocationId, _httpRequest); + } + cancellationToken.ThrowIfCancellationRequested(); + _httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + if (_shouldTrace) + { + ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); + } + HttpStatusCode _statusCode = _httpResponse.StatusCode; + cancellationToken.ThrowIfCancellationRequested(); + string _responseContent = null; + if ((int)_statusCode != 200) + { + var ex = new RestErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); + try + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + RestError _errorBody = Microsoft.Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); + if (_errorBody != null) + { + ex.Body = _errorBody; + } + } + catch (JsonException) + { + // Ignore the exception + } + ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); + ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); + if (_shouldTrace) + { + ServiceClientTracing.Error(_invocationId, ex); + } + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw ex; + } + // Create Result + var _result = new AzureOperationResponse(); + _result.Request = _httpRequest; + _result.Response = _httpResponse; + if (_httpResponse.Headers.Contains("x-ms-request-id")) + { + _result.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); + } + // Deserialize Response + if ((int)_statusCode == 200) + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + _result.Body = Microsoft.Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); + } + catch (JsonException ex) + { + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + } + } + if (_shouldTrace) + { + ServiceClientTracing.Exit(_invocationId, _result); + } + return _result; + } + + /// + /// Create a job. + /// + /// + /// Id of the job. + /// + /// + /// + /// + /// Headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// Thrown when the operation returned an invalid status code + /// + /// + /// Thrown when unable to deserialize the response + /// + /// + /// Thrown when a required parameter is null + /// + /// + /// Thrown when a required parameter is null + /// + /// + /// A response object containing the response body and response headers. + /// + public async Task> PutWithHttpMessagesAsync(string jobId, JobDetails jobDefinition, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) + { + if (Client.SubscriptionId == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.SubscriptionId"); + } + if (Client.ResourceGroupName == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.ResourceGroupName"); + } + if (Client.WorkspaceName == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.WorkspaceName"); + } + if (jobId == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "jobId"); + } + if (jobDefinition == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "jobDefinition"); + } + if (jobDefinition != null) + { + jobDefinition.Validate(); + } + // Tracing + bool _shouldTrace = ServiceClientTracing.IsEnabled; + string _invocationId = null; + if (_shouldTrace) + { + _invocationId = ServiceClientTracing.NextInvocationId.ToString(); + Dictionary tracingParameters = new Dictionary(); + tracingParameters.Add("jobId", jobId); + tracingParameters.Add("jobDefinition", jobDefinition); + tracingParameters.Add("cancellationToken", cancellationToken); + ServiceClientTracing.Enter(_invocationId, this, "Put", tracingParameters); + } + // Construct URL + var _baseUrl = Client.BaseUri.AbsoluteUri; + var _url = new System.Uri(new System.Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "v1.0/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Quantum/workspaces/{workspaceName}/jobs/{jobId}").ToString(); + _url = _url.Replace("{subscriptionId}", System.Uri.EscapeDataString(Client.SubscriptionId)); + _url = _url.Replace("{resourceGroupName}", System.Uri.EscapeDataString(Client.ResourceGroupName)); + _url = _url.Replace("{workspaceName}", System.Uri.EscapeDataString(Client.WorkspaceName)); + _url = _url.Replace("{jobId}", System.Uri.EscapeDataString(jobId)); + List _queryParameters = new List(); + if (_queryParameters.Count > 0) + { + _url += (_url.Contains("?") ? "&" : "?") + string.Join("&", _queryParameters); + } + // Create HTTP transport objects + var _httpRequest = new HttpRequestMessage(); + HttpResponseMessage _httpResponse = null; + _httpRequest.Method = new HttpMethod("PUT"); + _httpRequest.RequestUri = new System.Uri(_url); + // Set Headers + if (Client.GenerateClientRequestId != null && Client.GenerateClientRequestId.Value) + { + _httpRequest.Headers.TryAddWithoutValidation("x-ms-client-request-id", System.Guid.NewGuid().ToString()); + } + if (Client.AcceptLanguage != null) + { + if (_httpRequest.Headers.Contains("accept-language")) + { + _httpRequest.Headers.Remove("accept-language"); + } + _httpRequest.Headers.TryAddWithoutValidation("accept-language", Client.AcceptLanguage); + } + + + if (customHeaders != null) + { + foreach(var _header in customHeaders) + { + if (_httpRequest.Headers.Contains(_header.Key)) + { + _httpRequest.Headers.Remove(_header.Key); + } + _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + } + } + + // Serialize Request + string _requestContent = null; + if(jobDefinition != null) + { + _requestContent = Microsoft.Rest.Serialization.SafeJsonConvert.SerializeObject(jobDefinition, Client.SerializationSettings); + _httpRequest.Content = new StringContent(_requestContent, System.Text.Encoding.UTF8); + _httpRequest.Content.Headers.ContentType =System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); + } + // Set Credentials + if (Client.Credentials != null) + { + cancellationToken.ThrowIfCancellationRequested(); + await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + } + // Send Request + if (_shouldTrace) + { + ServiceClientTracing.SendRequest(_invocationId, _httpRequest); + } + cancellationToken.ThrowIfCancellationRequested(); + _httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + if (_shouldTrace) + { + ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); + } + HttpStatusCode _statusCode = _httpResponse.StatusCode; + cancellationToken.ThrowIfCancellationRequested(); + string _responseContent = null; + if ((int)_statusCode != 200 && (int)_statusCode != 201) + { + var ex = new RestErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); + try + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + RestError _errorBody = Microsoft.Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); + if (_errorBody != null) + { + ex.Body = _errorBody; + } + } + catch (JsonException) + { + // Ignore the exception + } + ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); + ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); + if (_shouldTrace) + { + ServiceClientTracing.Error(_invocationId, ex); + } + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw ex; + } + // Create Result + var _result = new AzureOperationResponse(); + _result.Request = _httpRequest; + _result.Response = _httpResponse; + if (_httpResponse.Headers.Contains("x-ms-request-id")) + { + _result.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); + } + // Deserialize Response + if ((int)_statusCode == 200) + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + _result.Body = Microsoft.Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); + } + catch (JsonException ex) + { + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + } + } + // Deserialize Response + if ((int)_statusCode == 201) + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + _result.Body = Microsoft.Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); + } + catch (JsonException ex) + { + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + } + } + if (_shouldTrace) + { + ServiceClientTracing.Exit(_invocationId, _result); + } + return _result; + } + + /// + /// Delete a job. + /// + /// + /// Id of the job. + /// + /// + /// Headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// Thrown when the operation returned an invalid status code + /// + /// + /// Thrown when unable to deserialize the response + /// + /// + /// Thrown when a required parameter is null + /// + /// + /// Thrown when a required parameter is null + /// + /// + /// A response object containing the response body and response headers. + /// + public async Task> DeleteWithHttpMessagesAsync(string jobId, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) + { + if (Client.SubscriptionId == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.SubscriptionId"); + } + if (Client.ResourceGroupName == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.ResourceGroupName"); + } + if (Client.WorkspaceName == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.WorkspaceName"); + } + if (jobId == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "jobId"); + } + // Tracing + bool _shouldTrace = ServiceClientTracing.IsEnabled; + string _invocationId = null; + if (_shouldTrace) + { + _invocationId = ServiceClientTracing.NextInvocationId.ToString(); + Dictionary tracingParameters = new Dictionary(); + tracingParameters.Add("jobId", jobId); + tracingParameters.Add("cancellationToken", cancellationToken); + ServiceClientTracing.Enter(_invocationId, this, "Delete", tracingParameters); + } + // Construct URL + var _baseUrl = Client.BaseUri.AbsoluteUri; + var _url = new System.Uri(new System.Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "v1.0/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Quantum/workspaces/{workspaceName}/jobs/{jobId}").ToString(); + _url = _url.Replace("{subscriptionId}", System.Uri.EscapeDataString(Client.SubscriptionId)); + _url = _url.Replace("{resourceGroupName}", System.Uri.EscapeDataString(Client.ResourceGroupName)); + _url = _url.Replace("{workspaceName}", System.Uri.EscapeDataString(Client.WorkspaceName)); + _url = _url.Replace("{jobId}", System.Uri.EscapeDataString(jobId)); + List _queryParameters = new List(); + if (_queryParameters.Count > 0) + { + _url += (_url.Contains("?") ? "&" : "?") + string.Join("&", _queryParameters); + } + // Create HTTP transport objects + var _httpRequest = new HttpRequestMessage(); + HttpResponseMessage _httpResponse = null; + _httpRequest.Method = new HttpMethod("DELETE"); + _httpRequest.RequestUri = new System.Uri(_url); + // Set Headers + if (Client.GenerateClientRequestId != null && Client.GenerateClientRequestId.Value) + { + _httpRequest.Headers.TryAddWithoutValidation("x-ms-client-request-id", System.Guid.NewGuid().ToString()); + } + if (Client.AcceptLanguage != null) + { + if (_httpRequest.Headers.Contains("accept-language")) + { + _httpRequest.Headers.Remove("accept-language"); + } + _httpRequest.Headers.TryAddWithoutValidation("accept-language", Client.AcceptLanguage); + } + + + if (customHeaders != null) + { + foreach(var _header in customHeaders) + { + if (_httpRequest.Headers.Contains(_header.Key)) + { + _httpRequest.Headers.Remove(_header.Key); + } + _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + } + } + + // Serialize Request + string _requestContent = null; + // Set Credentials + if (Client.Credentials != null) + { + cancellationToken.ThrowIfCancellationRequested(); + await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + } + // Send Request + if (_shouldTrace) + { + ServiceClientTracing.SendRequest(_invocationId, _httpRequest); + } + cancellationToken.ThrowIfCancellationRequested(); + _httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + if (_shouldTrace) + { + ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); + } + HttpStatusCode _statusCode = _httpResponse.StatusCode; + cancellationToken.ThrowIfCancellationRequested(); + string _responseContent = null; + if ((int)_statusCode != 200 && (int)_statusCode != 204) + { + var ex = new RestErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); + try + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + RestError _errorBody = Microsoft.Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); + if (_errorBody != null) + { + ex.Body = _errorBody; + } + } + catch (JsonException) + { + // Ignore the exception + } + ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); + ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); + if (_shouldTrace) + { + ServiceClientTracing.Error(_invocationId, ex); + } + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw ex; + } + // Create Result + var _result = new AzureOperationResponse(); + _result.Request = _httpRequest; + _result.Response = _httpResponse; + if (_httpResponse.Headers.Contains("x-ms-request-id")) + { + _result.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); + } + // Deserialize Response + if ((int)_statusCode == 200) + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + _result.Body = Microsoft.Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); + } + catch (JsonException ex) + { + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + } + } + if (_shouldTrace) + { + ServiceClientTracing.Exit(_invocationId, _result); + } + return _result; + } + + /// + /// List jobs. + /// + /// + /// The NextLink from the previous successful call to List operation. + /// + /// + /// Headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// Thrown when the operation returned an invalid status code + /// + /// + /// Thrown when unable to deserialize the response + /// + /// + /// Thrown when a required parameter is null + /// + /// + /// Thrown when a required parameter is null + /// + /// + /// A response object containing the response body and response headers. + /// + public async Task>> ListNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) + { + if (nextPageLink == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "nextPageLink"); + } + // Tracing + bool _shouldTrace = ServiceClientTracing.IsEnabled; + string _invocationId = null; + if (_shouldTrace) + { + _invocationId = ServiceClientTracing.NextInvocationId.ToString(); + Dictionary tracingParameters = new Dictionary(); + tracingParameters.Add("nextPageLink", nextPageLink); + tracingParameters.Add("cancellationToken", cancellationToken); + ServiceClientTracing.Enter(_invocationId, this, "ListNext", tracingParameters); + } + // Construct URL + string _url = "{nextLink}"; + _url = _url.Replace("{nextLink}", nextPageLink); + List _queryParameters = new List(); + if (_queryParameters.Count > 0) + { + _url += (_url.Contains("?") ? "&" : "?") + string.Join("&", _queryParameters); + } + // Create HTTP transport objects + var _httpRequest = new HttpRequestMessage(); + HttpResponseMessage _httpResponse = null; + _httpRequest.Method = new HttpMethod("GET"); + _httpRequest.RequestUri = new System.Uri(_url); + // Set Headers + if (Client.GenerateClientRequestId != null && Client.GenerateClientRequestId.Value) + { + _httpRequest.Headers.TryAddWithoutValidation("x-ms-client-request-id", System.Guid.NewGuid().ToString()); + } + if (Client.AcceptLanguage != null) + { + if (_httpRequest.Headers.Contains("accept-language")) + { + _httpRequest.Headers.Remove("accept-language"); + } + _httpRequest.Headers.TryAddWithoutValidation("accept-language", Client.AcceptLanguage); + } + + + if (customHeaders != null) + { + foreach(var _header in customHeaders) + { + if (_httpRequest.Headers.Contains(_header.Key)) + { + _httpRequest.Headers.Remove(_header.Key); + } + _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + } + } + + // Serialize Request + string _requestContent = null; + // Set Credentials + if (Client.Credentials != null) + { + cancellationToken.ThrowIfCancellationRequested(); + await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + } + // Send Request + if (_shouldTrace) + { + ServiceClientTracing.SendRequest(_invocationId, _httpRequest); + } + cancellationToken.ThrowIfCancellationRequested(); + _httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + if (_shouldTrace) + { + ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); + } + HttpStatusCode _statusCode = _httpResponse.StatusCode; + cancellationToken.ThrowIfCancellationRequested(); + string _responseContent = null; + if ((int)_statusCode != 200) + { + var ex = new CloudException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); + try + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + CloudError _errorBody = Microsoft.Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); + if (_errorBody != null) + { + ex = new CloudException(_errorBody.Message); + ex.Body = _errorBody; + } + } + catch (JsonException) + { + // Ignore the exception + } + ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); + ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); + if (_httpResponse.Headers.Contains("x-ms-request-id")) + { + ex.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); + } + if (_shouldTrace) + { + ServiceClientTracing.Error(_invocationId, ex); + } + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw ex; + } + // Create Result + var _result = new AzureOperationResponse>(); + _result.Request = _httpRequest; + _result.Response = _httpResponse; + if (_httpResponse.Headers.Contains("x-ms-request-id")) + { + _result.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); + } + // Deserialize Response + if ((int)_statusCode == 200) + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + _result.Body = Microsoft.Rest.Serialization.SafeJsonConvert.DeserializeObject>(_responseContent, Client.DeserializationSettings); + } + catch (JsonException ex) + { + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + } + } + if (_shouldTrace) + { + ServiceClientTracing.Exit(_invocationId, _result); + } + return _result; + } + + } +} diff --git a/src/Azure/Azure.Quantum.Client/generated/JobsOperationsExtensions.cs b/src/Azure/Azure.Quantum.Client/generated/JobsOperationsExtensions.cs new file mode 100644 index 00000000000..bfec9488a12 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/generated/JobsOperationsExtensions.cs @@ -0,0 +1,193 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. +// + +namespace Microsoft.Azure.Quantum.Client +{ + using Microsoft.Rest; + using Microsoft.Rest.Azure; + using Models; + using System.Threading; + using System.Threading.Tasks; + + /// + /// Extension methods for JobsOperations. + /// + public static partial class JobsOperationsExtensions + { + /// + /// List jobs. + /// + /// + /// The operations group for this extension method. + /// + public static IPage List(this IJobsOperations operations) + { + return operations.ListAsync().GetAwaiter().GetResult(); + } + + /// + /// List jobs. + /// + /// + /// The operations group for this extension method. + /// + /// + /// The cancellation token. + /// + public static async Task> ListAsync(this IJobsOperations operations, CancellationToken cancellationToken = default(CancellationToken)) + { + using (var _result = await operations.ListWithHttpMessagesAsync(null, cancellationToken).ConfigureAwait(false)) + { + return _result.Body; + } + } + + /// + /// Get job by id + /// + /// + /// The operations group for this extension method. + /// + /// + /// Id of the job. + /// + public static JobDetails Get(this IJobsOperations operations, string jobId) + { + return operations.GetAsync(jobId).GetAwaiter().GetResult(); + } + + /// + /// Get job by id + /// + /// + /// The operations group for this extension method. + /// + /// + /// Id of the job. + /// + /// + /// The cancellation token. + /// + public static async Task GetAsync(this IJobsOperations operations, string jobId, CancellationToken cancellationToken = default(CancellationToken)) + { + using (var _result = await operations.GetWithHttpMessagesAsync(jobId, null, cancellationToken).ConfigureAwait(false)) + { + return _result.Body; + } + } + + /// + /// Create a job. + /// + /// + /// The operations group for this extension method. + /// + /// + /// Id of the job. + /// + /// + /// + public static JobDetails Put(this IJobsOperations operations, string jobId, JobDetails jobDefinition) + { + return operations.PutAsync(jobId, jobDefinition).GetAwaiter().GetResult(); + } + + /// + /// Create a job. + /// + /// + /// The operations group for this extension method. + /// + /// + /// Id of the job. + /// + /// + /// + /// + /// The cancellation token. + /// + public static async Task PutAsync(this IJobsOperations operations, string jobId, JobDetails jobDefinition, CancellationToken cancellationToken = default(CancellationToken)) + { + using (var _result = await operations.PutWithHttpMessagesAsync(jobId, jobDefinition, null, cancellationToken).ConfigureAwait(false)) + { + return _result.Body; + } + } + + /// + /// Delete a job. + /// + /// + /// The operations group for this extension method. + /// + /// + /// Id of the job. + /// + public static JobDetails Delete(this IJobsOperations operations, string jobId) + { + return operations.DeleteAsync(jobId).GetAwaiter().GetResult(); + } + + /// + /// Delete a job. + /// + /// + /// The operations group for this extension method. + /// + /// + /// Id of the job. + /// + /// + /// The cancellation token. + /// + public static async Task DeleteAsync(this IJobsOperations operations, string jobId, CancellationToken cancellationToken = default(CancellationToken)) + { + using (var _result = await operations.DeleteWithHttpMessagesAsync(jobId, null, cancellationToken).ConfigureAwait(false)) + { + return _result.Body; + } + } + + /// + /// List jobs. + /// + /// + /// The operations group for this extension method. + /// + /// + /// The NextLink from the previous successful call to List operation. + /// + public static IPage ListNext(this IJobsOperations operations, string nextPageLink) + { + return operations.ListNextAsync(nextPageLink).GetAwaiter().GetResult(); + } + + /// + /// List jobs. + /// + /// + /// The operations group for this extension method. + /// + /// + /// The NextLink from the previous successful call to List operation. + /// + /// + /// The cancellation token. + /// + public static async Task> ListNextAsync(this IJobsOperations operations, string nextPageLink, CancellationToken cancellationToken = default(CancellationToken)) + { + using (var _result = await operations.ListNextWithHttpMessagesAsync(nextPageLink, null, cancellationToken).ConfigureAwait(false)) + { + return _result.Body; + } + } + + } +} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/JobDetails.cs b/src/Azure/Azure.Quantum.Client/generated/Models/JobDetails.cs new file mode 100644 index 00000000000..82627baecd0 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/generated/Models/JobDetails.cs @@ -0,0 +1,233 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. +// + +namespace Microsoft.Azure.Quantum.Client.Models +{ + using Microsoft.Rest; + using Newtonsoft.Json; + using System.Collections; + using System.Collections.Generic; + using System.Linq; + + /// + /// Job details. + /// + public partial class JobDetails + { + /// + /// Initializes a new instance of the JobDetails class. + /// + public JobDetails() + { + CustomInit(); + } + + /// + /// Initializes a new instance of the JobDetails class. + /// + /// The blob container SAS uri, the + /// container is used to host job data. + /// The format of the input data. + /// The unique identifier for the + /// provider. + /// The target identifier to run the job. + /// The job id. + /// The job name. Is not required for the name to be + /// unique and it's only used for display purposes. + /// The input blob SAS uri, if specified, it + /// will override the default input blob in the container. + /// The input parameters for the job. JSON + /// object used by the target solver. It is expected that the size of + /// this object is small and only used to specify parameters for the + /// execution target, not the input data. + /// The job metadata. Metadata provides client + /// the ability to store client-specific information + /// The output blob SAS uri. When a job + /// finishes successfully, results will be uploaded to this + /// blob. + /// The format of the output + /// data. + /// The job status. Possible values include: + /// 'Waiting', 'Executing', 'Succeeded', 'Failed', 'Cancelled' + /// The creation time of the job. + /// The time when the job began + /// execution. + /// The time when the job finished + /// execution. + /// The time when a job was successfully + /// cancelled. + /// The error data for the job. This is + /// expected only when Status 'Failed'. + public JobDetails(string containerUri, string inputDataFormat, string providerId, string target, string id = default(string), string name = default(string), string inputDataUri = default(string), object inputParams = default(object), IDictionary metadata = default(IDictionary), string outputDataUri = default(string), string outputDataFormat = default(string), string status = default(string), string creationTime = default(string), string beginExecutionTime = default(string), string endExecutionTime = default(string), string cancellationTime = default(string), RestError errorData = default(RestError)) + { + Id = id; + Name = name; + ContainerUri = containerUri; + InputDataUri = inputDataUri; + InputDataFormat = inputDataFormat; + InputParams = inputParams; + ProviderId = providerId; + Target = target; + Metadata = metadata; + OutputDataUri = outputDataUri; + OutputDataFormat = outputDataFormat; + Status = status; + CreationTime = creationTime; + BeginExecutionTime = beginExecutionTime; + EndExecutionTime = endExecutionTime; + CancellationTime = cancellationTime; + ErrorData = errorData; + CustomInit(); + } + + /// + /// An initialization method that performs custom operations like setting defaults + /// + partial void CustomInit(); + + /// + /// Gets or sets the job id. + /// + [JsonProperty(PropertyName = "id")] + public string Id { get; set; } + + /// + /// Gets or sets the job name. Is not required for the name to be + /// unique and it's only used for display purposes. + /// + [JsonProperty(PropertyName = "name")] + public string Name { get; set; } + + /// + /// Gets or sets the blob container SAS uri, the container is used to + /// host job data. + /// + [JsonProperty(PropertyName = "containerUri")] + public string ContainerUri { get; set; } + + /// + /// Gets or sets the input blob SAS uri, if specified, it will override + /// the default input blob in the container. + /// + [JsonProperty(PropertyName = "inputDataUri")] + public string InputDataUri { get; set; } + + /// + /// Gets or sets the format of the input data. + /// + [JsonProperty(PropertyName = "inputDataFormat")] + public string InputDataFormat { get; set; } + + /// + /// Gets or sets the input parameters for the job. JSON object used by + /// the target solver. It is expected that the size of this object is + /// small and only used to specify parameters for the execution target, + /// not the input data. + /// + [JsonProperty(PropertyName = "inputParams")] + public object InputParams { get; set; } + + /// + /// Gets or sets the unique identifier for the provider. + /// + [JsonProperty(PropertyName = "providerId")] + public string ProviderId { get; set; } + + /// + /// Gets or sets the target identifier to run the job. + /// + [JsonProperty(PropertyName = "target")] + public string Target { get; set; } + + /// + /// Gets or sets the job metadata. Metadata provides client the ability + /// to store client-specific information + /// + [JsonProperty(PropertyName = "metadata")] + public IDictionary Metadata { get; set; } + + /// + /// Gets or sets the output blob SAS uri. When a job finishes + /// successfully, results will be uploaded to this blob. + /// + [JsonProperty(PropertyName = "outputDataUri")] + public string OutputDataUri { get; set; } + + /// + /// Gets or sets the format of the output data. + /// + [JsonProperty(PropertyName = "outputDataFormat")] + public string OutputDataFormat { get; set; } + + /// + /// Gets the job status. Possible values include: 'Waiting', + /// 'Executing', 'Succeeded', 'Failed', 'Cancelled' + /// + [JsonProperty(PropertyName = "status")] + public string Status { get; private set; } + + /// + /// Gets the creation time of the job. + /// + [JsonProperty(PropertyName = "creationTime")] + public string CreationTime { get; private set; } + + /// + /// Gets the time when the job began execution. + /// + [JsonProperty(PropertyName = "beginExecutionTime")] + public string BeginExecutionTime { get; private set; } + + /// + /// Gets the time when the job finished execution. + /// + [JsonProperty(PropertyName = "endExecutionTime")] + public string EndExecutionTime { get; private set; } + + /// + /// Gets the time when a job was successfully cancelled. + /// + [JsonProperty(PropertyName = "cancellationTime")] + public string CancellationTime { get; private set; } + + /// + /// Gets the error data for the job. This is expected only when Status + /// 'Failed'. + /// + [JsonProperty(PropertyName = "errorData")] + public RestError ErrorData { get; private set; } + + /// + /// Validate the object. + /// + /// + /// Thrown if validation fails + /// + public virtual void Validate() + { + if (ContainerUri == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "ContainerUri"); + } + if (InputDataFormat == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "InputDataFormat"); + } + if (ProviderId == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "ProviderId"); + } + if (Target == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "Target"); + } + } + } +} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/Page.cs b/src/Azure/Azure.Quantum.Client/generated/Models/Page.cs new file mode 100644 index 00000000000..7ab5d320626 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/generated/Models/Page.cs @@ -0,0 +1,53 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. +// + +namespace Microsoft.Azure.Quantum.Client.Models +{ + using Microsoft.Rest; + using Microsoft.Rest.Azure; + using Newtonsoft.Json; + using System.Collections; + using System.Collections.Generic; + + /// + /// Defines a page in Azure responses. + /// + /// Type of the page content items + [JsonObject] + public class Page : IPage + { + /// + /// Gets the link to the next page. + /// + [JsonProperty("nextLink")] + public string NextPageLink { get; private set; } + + [JsonProperty("value")] + internal IList Items { get; set; } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// A an enumerator that can be used to iterate through the collection. + public IEnumerator GetEnumerator() + { + return Items == null ? System.Linq.Enumerable.Empty().GetEnumerator() : Items.GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// A an enumerator that can be used to iterate through the collection. + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/ProviderStatus.cs b/src/Azure/Azure.Quantum.Client/generated/Models/ProviderStatus.cs new file mode 100644 index 00000000000..43baf39b445 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/generated/Models/ProviderStatus.cs @@ -0,0 +1,69 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. +// + +namespace Microsoft.Azure.Quantum.Client.Models +{ + using Newtonsoft.Json; + using System.Collections; + using System.Collections.Generic; + using System.Linq; + + /// + /// Providers status. + /// + public partial class ProviderStatus + { + /// + /// Initializes a new instance of the ProviderStatus class. + /// + public ProviderStatus() + { + CustomInit(); + } + + /// + /// Initializes a new instance of the ProviderStatus class. + /// + /// Provider id. + /// Provider availability. Possible + /// values include: 'Available', 'Degraded', 'Unavailable' + public ProviderStatus(string id = default(string), string currentAvailability = default(string), IList targets = default(IList)) + { + Id = id; + CurrentAvailability = currentAvailability; + Targets = targets; + CustomInit(); + } + + /// + /// An initialization method that performs custom operations like setting defaults + /// + partial void CustomInit(); + + /// + /// Gets provider id. + /// + [JsonProperty(PropertyName = "id")] + public string Id { get; private set; } + + /// + /// Gets provider availability. Possible values include: 'Available', + /// 'Degraded', 'Unavailable' + /// + [JsonProperty(PropertyName = "currentAvailability")] + public string CurrentAvailability { get; private set; } + + /// + /// + [JsonProperty(PropertyName = "targets")] + public IList Targets { get; private set; } + + } +} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/RestError.cs b/src/Azure/Azure.Quantum.Client/generated/Models/RestError.cs new file mode 100644 index 00000000000..4917d841e8e --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/generated/Models/RestError.cs @@ -0,0 +1,66 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. +// + +namespace Microsoft.Azure.Quantum.Client.Models +{ + using Microsoft.Rest; + using Microsoft.Rest.Serialization; + using Newtonsoft.Json; + using System.Linq; + + /// + /// An Error response. + /// + [JsonTransformation] + public partial class RestError + { + /// + /// Initializes a new instance of the RestError class. + /// + public RestError() + { + CustomInit(); + } + + /// + /// Initializes a new instance of the RestError class. + /// + /// An identifier for the error. Codes are invariant + /// and are intended to be consumed programmatically. + /// A message describing the error, intended to + /// be suitable for displaying in a user interface. + public RestError(string code = default(string), string message = default(string)) + { + Code = code; + Message = message; + CustomInit(); + } + + /// + /// An initialization method that performs custom operations like setting defaults + /// + partial void CustomInit(); + + /// + /// Gets or sets an identifier for the error. Codes are invariant and + /// are intended to be consumed programmatically. + /// + [JsonProperty(PropertyName = "error.code")] + public string Code { get; set; } + + /// + /// Gets or sets a message describing the error, intended to be + /// suitable for displaying in a user interface. + /// + [JsonProperty(PropertyName = "error.message")] + public string Message { get; set; } + + } +} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/RestErrorException.cs b/src/Azure/Azure.Quantum.Client/generated/Models/RestErrorException.cs new file mode 100644 index 00000000000..cb5804185a0 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/generated/Models/RestErrorException.cs @@ -0,0 +1,61 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. +// + +namespace Microsoft.Azure.Quantum.Client.Models +{ + using Microsoft.Rest; + + /// + /// Exception thrown for an invalid response with RestError information. + /// + public partial class RestErrorException : RestException + { + /// + /// Gets information about the associated HTTP request. + /// + public HttpRequestMessageWrapper Request { get; set; } + + /// + /// Gets information about the associated HTTP response. + /// + public HttpResponseMessageWrapper Response { get; set; } + + /// + /// Gets or sets the body object. + /// + public RestError Body { get; set; } + + /// + /// Initializes a new instance of the RestErrorException class. + /// + public RestErrorException() + { + } + + /// + /// Initializes a new instance of the RestErrorException class. + /// + /// The exception message. + public RestErrorException(string message) + : this(message, null) + { + } + + /// + /// Initializes a new instance of the RestErrorException class. + /// + /// The exception message. + /// Inner exception. + public RestErrorException(string message, System.Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/TargetStatus.cs b/src/Azure/Azure.Quantum.Client/generated/Models/TargetStatus.cs new file mode 100644 index 00000000000..78c150b3c95 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/generated/Models/TargetStatus.cs @@ -0,0 +1,79 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. +// + +namespace Microsoft.Azure.Quantum.Client.Models +{ + using Newtonsoft.Json; + using System.Linq; + + /// + /// Target status. + /// + public partial class TargetStatus + { + /// + /// Initializes a new instance of the TargetStatus class. + /// + public TargetStatus() + { + CustomInit(); + } + + /// + /// Initializes a new instance of the TargetStatus class. + /// + /// Target id. + /// Target availability. Possible + /// values include: 'Available', 'Degraded', 'Unavailable' + /// Average queue time in + /// seconds. + /// A page with detailed status of the + /// provider. + public TargetStatus(string id = default(string), string currentAvailability = default(string), long? averageQueueTime = default(long?), string statusPage = default(string)) + { + Id = id; + CurrentAvailability = currentAvailability; + AverageQueueTime = averageQueueTime; + StatusPage = statusPage; + CustomInit(); + } + + /// + /// An initialization method that performs custom operations like setting defaults + /// + partial void CustomInit(); + + /// + /// Gets target id. + /// + [JsonProperty(PropertyName = "id")] + public string Id { get; private set; } + + /// + /// Gets target availability. Possible values include: 'Available', + /// 'Degraded', 'Unavailable' + /// + [JsonProperty(PropertyName = "currentAvailability")] + public string CurrentAvailability { get; private set; } + + /// + /// Gets average queue time in seconds. + /// + [JsonProperty(PropertyName = "averageQueueTime")] + public long? AverageQueueTime { get; private set; } + + /// + /// Gets a page with detailed status of the provider. + /// + [JsonProperty(PropertyName = "statusPage")] + public string StatusPage { get; private set; } + + } +} diff --git a/src/Azure/Azure.Quantum.Client/generated/ProvidersOperations.cs b/src/Azure/Azure.Quantum.Client/generated/ProvidersOperations.cs new file mode 100644 index 00000000000..625e3d0457b --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/generated/ProvidersOperations.cs @@ -0,0 +1,397 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. +// + +namespace Microsoft.Azure.Quantum.Client +{ + using Microsoft.Rest; + using Microsoft.Rest.Azure; + using Models; + using Newtonsoft.Json; + using System.Collections; + using System.Collections.Generic; + using System.Linq; + using System.Net; + using System.Net.Http; + using System.Threading; + using System.Threading.Tasks; + + /// + /// ProvidersOperations operations. + /// + internal partial class ProvidersOperations : IServiceOperations, IProvidersOperations + { + /// + /// Initializes a new instance of the ProvidersOperations class. + /// + /// + /// Reference to the service client. + /// + /// + /// Thrown when a required parameter is null + /// + internal ProvidersOperations(QuantumClient client) + { + if (client == null) + { + throw new System.ArgumentNullException("client"); + } + Client = client; + } + + /// + /// Gets a reference to the QuantumClient + /// + public QuantumClient Client { get; private set; } + + /// + /// Get provider status. + /// + /// + /// Headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// Thrown when the operation returned an invalid status code + /// + /// + /// Thrown when unable to deserialize the response + /// + /// + /// Thrown when a required parameter is null + /// + /// + /// Thrown when a required parameter is null + /// + /// + /// A response object containing the response body and response headers. + /// + public async Task>> GetStatusWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) + { + if (Client.SubscriptionId == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.SubscriptionId"); + } + if (Client.ResourceGroupName == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.ResourceGroupName"); + } + if (Client.WorkspaceName == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.WorkspaceName"); + } + // Tracing + bool _shouldTrace = ServiceClientTracing.IsEnabled; + string _invocationId = null; + if (_shouldTrace) + { + _invocationId = ServiceClientTracing.NextInvocationId.ToString(); + Dictionary tracingParameters = new Dictionary(); + tracingParameters.Add("cancellationToken", cancellationToken); + ServiceClientTracing.Enter(_invocationId, this, "GetStatus", tracingParameters); + } + // Construct URL + var _baseUrl = Client.BaseUri.AbsoluteUri; + var _url = new System.Uri(new System.Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "v1.0/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Quantum/workspaces/{workspaceName}/providerStatus").ToString(); + _url = _url.Replace("{subscriptionId}", System.Uri.EscapeDataString(Client.SubscriptionId)); + _url = _url.Replace("{resourceGroupName}", System.Uri.EscapeDataString(Client.ResourceGroupName)); + _url = _url.Replace("{workspaceName}", System.Uri.EscapeDataString(Client.WorkspaceName)); + List _queryParameters = new List(); + if (_queryParameters.Count > 0) + { + _url += (_url.Contains("?") ? "&" : "?") + string.Join("&", _queryParameters); + } + // Create HTTP transport objects + var _httpRequest = new HttpRequestMessage(); + HttpResponseMessage _httpResponse = null; + _httpRequest.Method = new HttpMethod("GET"); + _httpRequest.RequestUri = new System.Uri(_url); + // Set Headers + if (Client.GenerateClientRequestId != null && Client.GenerateClientRequestId.Value) + { + _httpRequest.Headers.TryAddWithoutValidation("x-ms-client-request-id", System.Guid.NewGuid().ToString()); + } + if (Client.AcceptLanguage != null) + { + if (_httpRequest.Headers.Contains("accept-language")) + { + _httpRequest.Headers.Remove("accept-language"); + } + _httpRequest.Headers.TryAddWithoutValidation("accept-language", Client.AcceptLanguage); + } + + + if (customHeaders != null) + { + foreach(var _header in customHeaders) + { + if (_httpRequest.Headers.Contains(_header.Key)) + { + _httpRequest.Headers.Remove(_header.Key); + } + _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + } + } + + // Serialize Request + string _requestContent = null; + // Set Credentials + if (Client.Credentials != null) + { + cancellationToken.ThrowIfCancellationRequested(); + await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + } + // Send Request + if (_shouldTrace) + { + ServiceClientTracing.SendRequest(_invocationId, _httpRequest); + } + cancellationToken.ThrowIfCancellationRequested(); + _httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + if (_shouldTrace) + { + ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); + } + HttpStatusCode _statusCode = _httpResponse.StatusCode; + cancellationToken.ThrowIfCancellationRequested(); + string _responseContent = null; + if ((int)_statusCode != 200) + { + var ex = new RestErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); + try + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + RestError _errorBody = Microsoft.Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); + if (_errorBody != null) + { + ex.Body = _errorBody; + } + } + catch (JsonException) + { + // Ignore the exception + } + ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); + ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); + if (_shouldTrace) + { + ServiceClientTracing.Error(_invocationId, ex); + } + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw ex; + } + // Create Result + var _result = new AzureOperationResponse>(); + _result.Request = _httpRequest; + _result.Response = _httpResponse; + if (_httpResponse.Headers.Contains("x-ms-request-id")) + { + _result.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); + } + // Deserialize Response + if ((int)_statusCode == 200) + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + _result.Body = Microsoft.Rest.Serialization.SafeJsonConvert.DeserializeObject>(_responseContent, Client.DeserializationSettings); + } + catch (JsonException ex) + { + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + } + } + if (_shouldTrace) + { + ServiceClientTracing.Exit(_invocationId, _result); + } + return _result; + } + + /// + /// Get provider status. + /// + /// + /// The NextLink from the previous successful call to List operation. + /// + /// + /// Headers that will be added to request. + /// + /// + /// The cancellation token. + /// + /// + /// Thrown when the operation returned an invalid status code + /// + /// + /// Thrown when unable to deserialize the response + /// + /// + /// Thrown when a required parameter is null + /// + /// + /// Thrown when a required parameter is null + /// + /// + /// A response object containing the response body and response headers. + /// + public async Task>> GetStatusNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) + { + if (nextPageLink == null) + { + throw new ValidationException(ValidationRules.CannotBeNull, "nextPageLink"); + } + // Tracing + bool _shouldTrace = ServiceClientTracing.IsEnabled; + string _invocationId = null; + if (_shouldTrace) + { + _invocationId = ServiceClientTracing.NextInvocationId.ToString(); + Dictionary tracingParameters = new Dictionary(); + tracingParameters.Add("nextPageLink", nextPageLink); + tracingParameters.Add("cancellationToken", cancellationToken); + ServiceClientTracing.Enter(_invocationId, this, "GetStatusNext", tracingParameters); + } + // Construct URL + string _url = "{nextLink}"; + _url = _url.Replace("{nextLink}", nextPageLink); + List _queryParameters = new List(); + if (_queryParameters.Count > 0) + { + _url += (_url.Contains("?") ? "&" : "?") + string.Join("&", _queryParameters); + } + // Create HTTP transport objects + var _httpRequest = new HttpRequestMessage(); + HttpResponseMessage _httpResponse = null; + _httpRequest.Method = new HttpMethod("GET"); + _httpRequest.RequestUri = new System.Uri(_url); + // Set Headers + if (Client.GenerateClientRequestId != null && Client.GenerateClientRequestId.Value) + { + _httpRequest.Headers.TryAddWithoutValidation("x-ms-client-request-id", System.Guid.NewGuid().ToString()); + } + if (Client.AcceptLanguage != null) + { + if (_httpRequest.Headers.Contains("accept-language")) + { + _httpRequest.Headers.Remove("accept-language"); + } + _httpRequest.Headers.TryAddWithoutValidation("accept-language", Client.AcceptLanguage); + } + + + if (customHeaders != null) + { + foreach(var _header in customHeaders) + { + if (_httpRequest.Headers.Contains(_header.Key)) + { + _httpRequest.Headers.Remove(_header.Key); + } + _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); + } + } + + // Serialize Request + string _requestContent = null; + // Set Credentials + if (Client.Credentials != null) + { + cancellationToken.ThrowIfCancellationRequested(); + await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + } + // Send Request + if (_shouldTrace) + { + ServiceClientTracing.SendRequest(_invocationId, _httpRequest); + } + cancellationToken.ThrowIfCancellationRequested(); + _httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); + if (_shouldTrace) + { + ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); + } + HttpStatusCode _statusCode = _httpResponse.StatusCode; + cancellationToken.ThrowIfCancellationRequested(); + string _responseContent = null; + if ((int)_statusCode != 200) + { + var ex = new RestErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); + try + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + RestError _errorBody = Microsoft.Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); + if (_errorBody != null) + { + ex.Body = _errorBody; + } + } + catch (JsonException) + { + // Ignore the exception + } + ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); + ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); + if (_shouldTrace) + { + ServiceClientTracing.Error(_invocationId, ex); + } + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw ex; + } + // Create Result + var _result = new AzureOperationResponse>(); + _result.Request = _httpRequest; + _result.Response = _httpResponse; + if (_httpResponse.Headers.Contains("x-ms-request-id")) + { + _result.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); + } + // Deserialize Response + if ((int)_statusCode == 200) + { + _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + _result.Body = Microsoft.Rest.Serialization.SafeJsonConvert.DeserializeObject>(_responseContent, Client.DeserializationSettings); + } + catch (JsonException ex) + { + _httpRequest.Dispose(); + if (_httpResponse != null) + { + _httpResponse.Dispose(); + } + throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); + } + } + if (_shouldTrace) + { + ServiceClientTracing.Exit(_invocationId, _result); + } + return _result; + } + + } +} diff --git a/src/Azure/Azure.Quantum.Client/generated/ProvidersOperationsExtensions.cs b/src/Azure/Azure.Quantum.Client/generated/ProvidersOperationsExtensions.cs new file mode 100644 index 00000000000..649d483f64a --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/generated/ProvidersOperationsExtensions.cs @@ -0,0 +1,87 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. +// + +namespace Microsoft.Azure.Quantum.Client +{ + using Microsoft.Rest; + using Microsoft.Rest.Azure; + using Models; + using System.Threading; + using System.Threading.Tasks; + + /// + /// Extension methods for ProvidersOperations. + /// + public static partial class ProvidersOperationsExtensions + { + /// + /// Get provider status. + /// + /// + /// The operations group for this extension method. + /// + public static IPage GetStatus(this IProvidersOperations operations) + { + return operations.GetStatusAsync().GetAwaiter().GetResult(); + } + + /// + /// Get provider status. + /// + /// + /// The operations group for this extension method. + /// + /// + /// The cancellation token. + /// + public static async Task> GetStatusAsync(this IProvidersOperations operations, CancellationToken cancellationToken = default(CancellationToken)) + { + using (var _result = await operations.GetStatusWithHttpMessagesAsync(null, cancellationToken).ConfigureAwait(false)) + { + return _result.Body; + } + } + + /// + /// Get provider status. + /// + /// + /// The operations group for this extension method. + /// + /// + /// The NextLink from the previous successful call to List operation. + /// + public static IPage GetStatusNext(this IProvidersOperations operations, string nextPageLink) + { + return operations.GetStatusNextAsync(nextPageLink).GetAwaiter().GetResult(); + } + + /// + /// Get provider status. + /// + /// + /// The operations group for this extension method. + /// + /// + /// The NextLink from the previous successful call to List operation. + /// + /// + /// The cancellation token. + /// + public static async Task> GetStatusNextAsync(this IProvidersOperations operations, string nextPageLink, CancellationToken cancellationToken = default(CancellationToken)) + { + using (var _result = await operations.GetStatusNextWithHttpMessagesAsync(nextPageLink, null, cancellationToken).ConfigureAwait(false)) + { + return _result.Body; + } + } + + } +} diff --git a/src/Azure/Azure.Quantum.Client/generated/QuantumClient.cs b/src/Azure/Azure.Quantum.Client/generated/QuantumClient.cs new file mode 100644 index 00000000000..3d193cee37a --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/generated/QuantumClient.cs @@ -0,0 +1,371 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. +// +// Code generated by Microsoft (R) AutoRest Code Generator. +// Changes may cause incorrect behavior and will be lost if the code is +// regenerated. +// + +namespace Microsoft.Azure.Quantum.Client +{ + using Microsoft.Rest; + using Microsoft.Rest.Azure; + using Microsoft.Rest.Serialization; + using Models; + using Newtonsoft.Json; + using System.Collections; + using System.Collections.Generic; + using System.Linq; + using System.Net; + using System.Net.Http; + + /// + /// Azure Quantum REST API client + /// + public partial class QuantumClient : ServiceClient, IQuantumClient, IAzureClient + { + /// + /// The base URI of the service. + /// + public System.Uri BaseUri { get; set; } + + /// + /// Gets or sets json serialization settings. + /// + public JsonSerializerSettings SerializationSettings { get; private set; } + + /// + /// Gets or sets json deserialization settings. + /// + public JsonSerializerSettings DeserializationSettings { get; private set; } + + /// + /// Credentials needed for the client to connect to Azure. + /// + public ServiceClientCredentials Credentials { get; private set; } + + /// + /// The Azure subscription ID. This is a GUID-formatted string (e.g. + /// 00000000-0000-0000-0000-000000000000) + /// + public string SubscriptionId { get; set; } + + /// + /// Name of an Azure resource group. + /// + public string ResourceGroupName { get; set; } + + /// + /// Name of the workspace. + /// + public string WorkspaceName { get; set; } + + /// + /// The preferred language for the response. + /// + public string AcceptLanguage { get; set; } + + /// + /// The retry timeout in seconds for Long Running Operations. Default value is + /// 30. + /// + public int? LongRunningOperationRetryTimeout { get; set; } + + /// + /// Whether a unique x-ms-client-request-id should be generated. When set to + /// true a unique x-ms-client-request-id value is generated and included in + /// each request. Default is true. + /// + public bool? GenerateClientRequestId { get; set; } + + /// + /// Gets the IJobsOperations. + /// + public virtual IJobsOperations Jobs { get; private set; } + + /// + /// Gets the IProvidersOperations. + /// + public virtual IProvidersOperations Providers { get; private set; } + + /// + /// Initializes a new instance of the QuantumClient class. + /// + /// + /// HttpClient to be used + /// + /// + /// True: will dispose the provided httpClient on calling QuantumClient.Dispose(). False: will not dispose provided httpClient + internal QuantumClient(HttpClient httpClient, bool disposeHttpClient) : base(httpClient, disposeHttpClient) + { + Initialize(); + } + + /// + /// Initializes a new instance of the QuantumClient class. + /// + /// + /// Optional. The delegating handlers to add to the http client pipeline. + /// + protected QuantumClient(params DelegatingHandler[] handlers) : base(handlers) + { + Initialize(); + } + + /// + /// Initializes a new instance of the QuantumClient class. + /// + /// + /// Optional. The http client handler used to handle http transport. + /// + /// + /// Optional. The delegating handlers to add to the http client pipeline. + /// + internal QuantumClient(HttpClientHandler rootHandler, params DelegatingHandler[] handlers) : base(rootHandler, handlers) + { + Initialize(); + } + + /// + /// Initializes a new instance of the QuantumClient class. + /// + /// + /// Optional. The base URI of the service. + /// + /// + /// Optional. The delegating handlers to add to the http client pipeline. + /// + /// + /// Thrown when a required parameter is null + /// + protected QuantumClient(System.Uri baseUri, params DelegatingHandler[] handlers) : this(handlers) + { + if (baseUri == null) + { + throw new System.ArgumentNullException("baseUri"); + } + BaseUri = baseUri; + } + + /// + /// Initializes a new instance of the QuantumClient class. + /// + /// + /// Optional. The base URI of the service. + /// + /// + /// Optional. The http client handler used to handle http transport. + /// + /// + /// Optional. The delegating handlers to add to the http client pipeline. + /// + /// + /// Thrown when a required parameter is null + /// + protected QuantumClient(System.Uri baseUri, HttpClientHandler rootHandler, params DelegatingHandler[] handlers) : this(rootHandler, handlers) + { + if (baseUri == null) + { + throw new System.ArgumentNullException("baseUri"); + } + BaseUri = baseUri; + } + + /// + /// Initializes a new instance of the QuantumClient class. + /// + /// + /// Required. Credentials needed for the client to connect to Azure. + /// + /// + /// Optional. The delegating handlers to add to the http client pipeline. + /// + /// + /// Thrown when a required parameter is null + /// + public QuantumClient(ServiceClientCredentials credentials, params DelegatingHandler[] handlers) : this(handlers) + { + if (credentials == null) + { + throw new System.ArgumentNullException("credentials"); + } + Credentials = credentials; + if (Credentials != null) + { + Credentials.InitializeServiceClient(this); + } + } + + /// + /// Initializes a new instance of the QuantumClient class. + /// + /// + /// Required. Credentials needed for the client to connect to Azure. + /// + /// + /// HttpClient to be used + /// + /// + /// True: will dispose the provided httpClient on calling QuantumClient.Dispose(). False: will not dispose provided httpClient + /// + /// Thrown when a required parameter is null + /// + public QuantumClient(ServiceClientCredentials credentials, HttpClient httpClient, bool disposeHttpClient) : this(httpClient, disposeHttpClient) + { + if (credentials == null) + { + throw new System.ArgumentNullException("credentials"); + } + Credentials = credentials; + if (Credentials != null) + { + Credentials.InitializeServiceClient(this); + } + } + + /// + /// Initializes a new instance of the QuantumClient class. + /// + /// + /// Required. Credentials needed for the client to connect to Azure. + /// + /// + /// Optional. The http client handler used to handle http transport. + /// + /// + /// Optional. The delegating handlers to add to the http client pipeline. + /// + /// + /// Thrown when a required parameter is null + /// + public QuantumClient(ServiceClientCredentials credentials, HttpClientHandler rootHandler, params DelegatingHandler[] handlers) : this(rootHandler, handlers) + { + if (credentials == null) + { + throw new System.ArgumentNullException("credentials"); + } + Credentials = credentials; + if (Credentials != null) + { + Credentials.InitializeServiceClient(this); + } + } + + /// + /// Initializes a new instance of the QuantumClient class. + /// + /// + /// Optional. The base URI of the service. + /// + /// + /// Required. Credentials needed for the client to connect to Azure. + /// + /// + /// Optional. The delegating handlers to add to the http client pipeline. + /// + /// + /// Thrown when a required parameter is null + /// + public QuantumClient(System.Uri baseUri, ServiceClientCredentials credentials, params DelegatingHandler[] handlers) : this(handlers) + { + if (baseUri == null) + { + throw new System.ArgumentNullException("baseUri"); + } + if (credentials == null) + { + throw new System.ArgumentNullException("credentials"); + } + BaseUri = baseUri; + Credentials = credentials; + if (Credentials != null) + { + Credentials.InitializeServiceClient(this); + } + } + + /// + /// Initializes a new instance of the QuantumClient class. + /// + /// + /// Optional. The base URI of the service. + /// + /// + /// Required. Credentials needed for the client to connect to Azure. + /// + /// + /// Optional. The http client handler used to handle http transport. + /// + /// + /// Optional. The delegating handlers to add to the http client pipeline. + /// + /// + /// Thrown when a required parameter is null + /// + public QuantumClient(System.Uri baseUri, ServiceClientCredentials credentials, HttpClientHandler rootHandler, params DelegatingHandler[] handlers) : this(rootHandler, handlers) + { + if (baseUri == null) + { + throw new System.ArgumentNullException("baseUri"); + } + if (credentials == null) + { + throw new System.ArgumentNullException("credentials"); + } + BaseUri = baseUri; + Credentials = credentials; + if (Credentials != null) + { + Credentials.InitializeServiceClient(this); + } + } + + /// + /// An optional partial-method to perform custom initialization. + /// + partial void CustomInitialize(); + /// + /// Initializes client properties. + /// + private void Initialize() + { + Jobs = new JobsOperations(this); + Providers = new ProvidersOperations(this); + BaseUri = new System.Uri("https://app-jobscheduler-prod.azurewebsites.net"); + AcceptLanguage = "en-US"; + LongRunningOperationRetryTimeout = 30; + GenerateClientRequestId = true; + SerializationSettings = new JsonSerializerSettings + { + Formatting = Newtonsoft.Json.Formatting.Indented, + DateFormatHandling = Newtonsoft.Json.DateFormatHandling.IsoDateFormat, + DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc, + NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore, + ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize, + ContractResolver = new ReadOnlyJsonContractResolver(), + Converters = new List + { + new Iso8601TimeSpanConverter() + } + }; + SerializationSettings.Converters.Add(new TransformationJsonConverter()); + DeserializationSettings = new JsonSerializerSettings + { + DateFormatHandling = Newtonsoft.Json.DateFormatHandling.IsoDateFormat, + DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc, + NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore, + ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize, + ContractResolver = new ReadOnlyJsonContractResolver(), + Converters = new List + { + new Iso8601TimeSpanConverter() + } + }; + CustomInitialize(); + DeserializationSettings.Converters.Add(new TransformationJsonConverter()); + DeserializationSettings.Converters.Add(new CloudErrorJsonConverter()); + } + } +} diff --git a/src/Azure/Common/AssemblyCommon.props b/src/Azure/Common/AssemblyCommon.props new file mode 100644 index 00000000000..79fb4f744d9 --- /dev/null +++ b/src/Azure/Common/AssemblyCommon.props @@ -0,0 +1,23 @@ + + + + Microsoft + Microsoft + Microsoft Quantum Development Kit Preview + © Microsoft Corporation. All rights reserved. + + + + + + + + + \ No newline at end of file diff --git a/src/Azure/Common/DelaySign.cs b/src/Azure/Common/DelaySign.cs new file mode 100644 index 00000000000..4d6d06f28ef --- /dev/null +++ b/src/Azure/Common/DelaySign.cs @@ -0,0 +1,29 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +using System.Reflection; + +// Attributes for delay-signing +#if SIGNED +[assembly:AssemblyKeyFile("..\\..\\..\\Build\\267DevDivSNKey2048.snk")] +[assembly:AssemblyDelaySign(true)] +#endif + +internal static class DelaySign +{ +#pragma warning disable SA1310 // Fields should not have underscore. +#if SIGNED + public const string PUBLIC_KEY = ", PublicKey=" + + "002400000c800000140100000602000000240000525341310008000001000100613399aff18ef1" + + "a2c2514a273a42d9042b72321f1757102df9ebada69923e2738406c21e5b801552ab8d200a65a2" + + "35e001ac9adc25f2d811eb09496a4c6a59d4619589c69f5baf0c4179a47311d92555cd006acc8b" + + "5959f2bd6e10e360c34537a1d266da8085856583c85d81da7f3ec01ed9564c58d93d713cd0172c" + + "8e23a10f0239b80c96b07736f5d8b022542a4e74251a5f432824318b3539a5a087f8e53d2f135f" + + "9ca47f3bb2e10aff0af0849504fb7cea3ff192dc8de0edad64c68efde34c56d302ad55fd6e80f3" + + "02d5efcdeae953658d3452561b5f36c542efdbdd9f888538d374cef106acf7d93a4445c3c73cd9" + + "11f0571aaf3d54da12b11ddec375b3"; +#else + public const string PUBLIC_KEY = ""; +#endif +} diff --git a/src/Azure/Common/StyleCop.ruleset b/src/Azure/Common/StyleCop.ruleset new file mode 100644 index 00000000000..24532383c6b --- /dev/null +++ b/src/Azure/Common/StyleCop.ruleset @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Azure/Common/stylecop.json b/src/Azure/Common/stylecop.json new file mode 100644 index 00000000000..79ab17994dd --- /dev/null +++ b/src/Azure/Common/stylecop.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "companyName": "Microsoft Corporation" + }, + "orderingRules": { + "usingDirectivesPlacement": "outsideNamespace" + } + } +}