mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-13 17:23:02 -05:00
Port Azure code from SSDT to the tools service (#477)
Porting of the vast majority of Azure-related code from SSDT. This is very large, so I want to put this out as one large "lift and shift" PR before I do the tools-service specific JSON-RPC service handlers, connect a new account handler (as the code to get necessary info from accounts and subscriptions isn't fully complete) and add tests over these **What's in this PR**: - Created 3 new projects: - Microsoft.SqlTools.ResourceProvider will host the executable that accepts requests for Azure-related actions over the JSON-RPC protocol. This must be separate from other DLLs since a direct dependency on the Azure SDK DLLs fails (they're NetStandard 1.4 and you can't reference them if you have RuntimeIdentifiers in your .csproj file) - Microsoft.SqlTools.ResourceProvider.Core is where all the main business logic is, including definitions and logic on how to navigate over resources and create firewall rules, etc. - Microsoft.SqlTools.ResourceProvider.DefaultImpl is the actual Azure implementation of the resource provider APIs. The reason for separating this is to support eventual integration back into other tools (since their Azure and Identity services will be different). - Implemented the AzureResourceManager that connects to Azure via ARM APIs and handles creating firewall rule and querying databases. The dependent DLLs have had major breaking changes, so will need additional verification to ensure this works as expected - Ported the unit tests for all code that was not a viewmodel. Viewmodel test code will be ported in a future update as we plumb through a service-equivalent to these. Also, the DependencyManager code which has overlap with our service provider code is commented out. Will work to uncomment in a future update as it has value to test some scenarios **What's not in this PR**: - Identity Services. We currently just have a stub for the interface, and even that will likely change a little - anything JSON-RPC or registered service related. These will be adapted from the viewmodels and added in a separate PR
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26510.0
|
||||
VisualStudioVersion = 15.0.26730.16
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2BBD7364-054F-4693-97CD-1C395E3E84A9}"
|
||||
EndProject
|
||||
@@ -72,6 +72,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.SqlTools.ServiceL
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.SqlTools.Serialization", "src\Microsoft.SqlTools.Serialization\Microsoft.SqlTools.Serialization.csproj", "{75E1A89F-9DF6-4DA3-9EF1-5FD966331E06}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.SqlTools.ResourceProvider", "src\Microsoft.SqlTools.ResourceProvider\Microsoft.SqlTools.ResourceProvider.csproj", "{6FEE7E14-8A1D-454E-8F7C-B63597801787}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.SqlTools.ResourceProvider.Core", "src\Microsoft.SqlTools.ResourceProvider.Core\Microsoft.SqlTools.ResourceProvider.Core.csproj", "{70E63BC1-2C82-41C0-89D6-272FD3C7B0C9}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.SqlTools.ResourceProvider.DefaultImpl", "src\Microsoft.SqlTools.ResourceProvider.DefaultImpl\Microsoft.SqlTools.ResourceProvider.DefaultImpl.csproj", "{EFB39C03-F7D2-4E8D-BE51-09121CD71973}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -145,6 +151,24 @@ Global
|
||||
{75E1A89F-9DF6-4DA3-9EF1-5FD966331E06}.Integration|Any CPU.Build.0 = Debug|Any CPU
|
||||
{75E1A89F-9DF6-4DA3-9EF1-5FD966331E06}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{75E1A89F-9DF6-4DA3-9EF1-5FD966331E06}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6FEE7E14-8A1D-454E-8F7C-B63597801787}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6FEE7E14-8A1D-454E-8F7C-B63597801787}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6FEE7E14-8A1D-454E-8F7C-B63597801787}.Integration|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6FEE7E14-8A1D-454E-8F7C-B63597801787}.Integration|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6FEE7E14-8A1D-454E-8F7C-B63597801787}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6FEE7E14-8A1D-454E-8F7C-B63597801787}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{70E63BC1-2C82-41C0-89D6-272FD3C7B0C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{70E63BC1-2C82-41C0-89D6-272FD3C7B0C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{70E63BC1-2C82-41C0-89D6-272FD3C7B0C9}.Integration|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{70E63BC1-2C82-41C0-89D6-272FD3C7B0C9}.Integration|Any CPU.Build.0 = Debug|Any CPU
|
||||
{70E63BC1-2C82-41C0-89D6-272FD3C7B0C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{70E63BC1-2C82-41C0-89D6-272FD3C7B0C9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EFB39C03-F7D2-4E8D-BE51-09121CD71973}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EFB39C03-F7D2-4E8D-BE51-09121CD71973}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EFB39C03-F7D2-4E8D-BE51-09121CD71973}.Integration|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EFB39C03-F7D2-4E8D-BE51-09121CD71973}.Integration|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EFB39C03-F7D2-4E8D-BE51-09121CD71973}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EFB39C03-F7D2-4E8D-BE51-09121CD71973}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -163,6 +187,9 @@ Global
|
||||
{501DB3B2-AF92-41CF-82F6-780F9C37C219} = {AB9CA2B8-6F70-431C-8A1D-67479D8A7BE4}
|
||||
{2C290C58-C98D-46B2-BCED-44D9B67F6D31} = {AB9CA2B8-6F70-431C-8A1D-67479D8A7BE4}
|
||||
{75E1A89F-9DF6-4DA3-9EF1-5FD966331E06} = {2BBD7364-054F-4693-97CD-1C395E3E84A9}
|
||||
{6FEE7E14-8A1D-454E-8F7C-B63597801787} = {2BBD7364-054F-4693-97CD-1C395E3E84A9}
|
||||
{70E63BC1-2C82-41C0-89D6-272FD3C7B0C9} = {2BBD7364-054F-4693-97CD-1C395E3E84A9}
|
||||
{EFB39C03-F7D2-4E8D-BE51-09121CD71973} = {2BBD7364-054F-4693-97CD-1C395E3E84A9}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {B31CDF4B-2851-45E5-8C5F-BE97125D9DD8}
|
||||
|
||||
@@ -26,13 +26,14 @@
|
||||
<PackageReference Include="System.Composition" Version="1.1.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="**\*.cs" />
|
||||
<Compile Include="**\*.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../Microsoft.SqlTools.Hosting/Microsoft.SqlTools.Hosting.csproj" />
|
||||
<ProjectReference Include="../Microsoft.SqlTools.Hosting/Microsoft.SqlTools.Hosting.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Localization\sr.resx" />
|
||||
<None Include="Localization\sr.strings" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Utility\" />
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Composition;
|
||||
|
||||
namespace Microsoft.SqlTools.Extensibility
|
||||
{
|
||||
/// <summary>
|
||||
/// Base attribute class for all export definitions.
|
||||
/// </summary>
|
||||
[MetadataAttribute]
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
||||
public abstract class ExportStandardMetadataAttribute : ExportAttribute, IStandardMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for DAC extensibility exports
|
||||
/// </summary>
|
||||
protected ExportStandardMetadataAttribute(Type contractType, string id, string displayName = null)
|
||||
: base(contractType)
|
||||
{
|
||||
Id = id;
|
||||
DisplayName = displayName;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The version of this extension
|
||||
/// </summary>
|
||||
public string Version { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The id of the extension
|
||||
/// </summary>
|
||||
public string Id { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The display name for the extension
|
||||
/// </summary>
|
||||
public virtual string DisplayName { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.Extensibility
|
||||
{
|
||||
/// <summary>
|
||||
/// Standard Metadata needed for extensions.
|
||||
/// </summary>
|
||||
public interface IStandardMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension version. Should be in the format "1.0.0.0" or similar
|
||||
/// </summary>
|
||||
string Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Unique Id used to identify the export.
|
||||
/// </summary>
|
||||
string Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional Display name describing the export type
|
||||
/// </summary>
|
||||
string DisplayName { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<EnableDefaultItems>false</EnableDefaultItems>
|
||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
|
||||
@@ -10,11 +10,9 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
<DebugType>portable</DebugType>
|
||||
<RuntimeIdentifiers>win7-x64;win7-x86;ubuntu.14.04-x64;ubuntu.16.04-x64;centos.7-x64;rhel.7.2-x64;debian.8-x64;fedora.23-x64;opensuse.13.2-x64;osx.10.11-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Data.SqlClient" Version="4.4.0" />
|
||||
<PackageReference Include="Microsoft.SqlServer.Smo" Version="140.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="2.0.0" />
|
||||
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
|
||||
@@ -25,6 +23,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Localization\sr.resx" />
|
||||
<None Include="Localization\sr.strings" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
||||
55
src/Microsoft.SqlTools.Hosting/Utility/AutoLock.cs
Normal file
55
src/Microsoft.SqlTools.Hosting/Utility/AutoLock.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.SqlTools.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// A wrapper around the ReaderWriterLock to make sure the locks are released even if the action fails
|
||||
/// </summary>
|
||||
public class AutoLock
|
||||
{
|
||||
private readonly ReaderWriterLock _lock;
|
||||
private readonly bool _isWriteLocked;
|
||||
|
||||
/// <summary>
|
||||
/// Creates new lock given type of lock and timeout
|
||||
/// </summary>
|
||||
public AutoLock(ReaderWriterLock lockObj, bool isWriteLock, TimeSpan timeOut, Action action, out Exception exception)
|
||||
{
|
||||
exception = null;
|
||||
try
|
||||
{
|
||||
_lock = lockObj;
|
||||
_isWriteLocked = isWriteLock;
|
||||
if (_isWriteLocked)
|
||||
{
|
||||
_lock.AcquireWriterLock(timeOut);
|
||||
}
|
||||
else
|
||||
{
|
||||
_lock.AcquireReaderLock(timeOut);
|
||||
}
|
||||
action();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_isWriteLocked && _lock.IsWriterLockHeld)
|
||||
{
|
||||
_lock.ReleaseWriterLock();
|
||||
}
|
||||
else if (!_isWriteLocked && _lock.IsReaderLockHeld)
|
||||
{
|
||||
_lock.ReleaseReaderLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ namespace Microsoft.SqlTools.Hosting.Utility
|
||||
ShouldExit = true;
|
||||
return;
|
||||
default:
|
||||
ErrorMessage += String.Format("Unknown argument \"{0}\"" + Environment.NewLine, argName);
|
||||
ErrorMessage += string.Format("Unknown argument \"{0}\"" + Environment.NewLine, argName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
82
src/Microsoft.SqlTools.Hosting/Utility/ConcurrentCache.cs
Normal file
82
src/Microsoft.SqlTools.Hosting/Utility/ConcurrentCache.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Microsoft.SqlTools.Utility
|
||||
{
|
||||
public class ConcurrentCache<T>
|
||||
{
|
||||
private readonly Dictionary<string, T> _cache = new Dictionary<string, T>();
|
||||
private readonly ReaderWriterLock _readerWriterLock = new ReaderWriterLock();
|
||||
private readonly TimeSpan _timeout = TimeSpan.FromHours(1);
|
||||
|
||||
public void ClearCache(IEnumerable<string> keys)
|
||||
{
|
||||
Exception exception;
|
||||
new AutoLock(_readerWriterLock, true, _timeout, () =>
|
||||
{
|
||||
{
|
||||
foreach (var key in keys)
|
||||
{
|
||||
if (_cache.ContainsKey(key))
|
||||
{
|
||||
_cache.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, out exception);
|
||||
if (exception != null)
|
||||
{
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
public T Get(string key)
|
||||
{
|
||||
T result = default(T);
|
||||
Exception exception;
|
||||
new AutoLock(_readerWriterLock, false, _timeout, () =>
|
||||
{
|
||||
if (_cache.ContainsKey(key))
|
||||
{
|
||||
result = _cache[key];
|
||||
}
|
||||
}, out exception);
|
||||
if (exception != null)
|
||||
{
|
||||
throw exception;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public T UpdateCache(string key, T newValue)
|
||||
{
|
||||
T result = newValue;
|
||||
|
||||
Exception exception;
|
||||
new AutoLock(_readerWriterLock, true, _timeout, () =>
|
||||
{
|
||||
bool isDefined = _cache.ContainsKey(key);
|
||||
if (!isDefined)
|
||||
{
|
||||
_cache.Add(key, newValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = _cache[key];
|
||||
}
|
||||
}, out exception);
|
||||
if (exception != null)
|
||||
{
|
||||
throw exception;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="System.Data.SqlClient" version="4.4.0-preview1-25305-02" />
|
||||
<package id="Microsoft.SqlServer.Smo" version="140.17054.0" />
|
||||
<package id="Microsoft.SqlServer.Management.SqlScriptPublishModel" version="140.17050.0" />
|
||||
<package id="Newtonsoft.Json" version="10.0.2" />
|
||||
<package id="Microsoft.Extensions.DependencyModel" version="2.0.0-preview1-002111" />
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Extensibility;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// An account manager has the information of currently logged in user and can authenticate the user
|
||||
/// Implementing classes must add a <see cref="ExportableAttribute" />
|
||||
/// to the class in order to be found by the extension manager,
|
||||
/// and to define the type and category supported
|
||||
/// </summary>
|
||||
public interface IAccountManager : IExportable
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns true is user needs reauthentication
|
||||
/// </summary>
|
||||
Task<bool> GetUserNeedsReauthenticationAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Authenticates the user
|
||||
/// </summary>
|
||||
Task<IUserAccount> AuthenticateAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Prompt the login dialog to login to a new use that has not been cached
|
||||
/// </summary>
|
||||
Task<IUserAccount> AddUserAccountAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Set the current loaged in user in the cache
|
||||
/// </summary>
|
||||
Task<IUserAccount> SetCurrentAccountAsync(object account);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current logged in user
|
||||
/// </summary>
|
||||
Task<IUserAccount> GetCurrentAccountAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the API supports a login control
|
||||
/// </summary>
|
||||
bool HasLoginDialog
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event to raise when the current logged in user changed
|
||||
/// </summary>
|
||||
event EventHandler CurrentAccountChanged;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides functionality to authenticate to Azure and discover associated accounts and subscriptions
|
||||
/// </summary>
|
||||
public interface IAzureAuthenticationManager : IAccountManager
|
||||
{
|
||||
/// <summary>
|
||||
/// User accounts associated to the logged in user
|
||||
/// </summary>
|
||||
IEnumerable<IAzureUserAccount> UserAccounts
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Azure subscriptions associated to the logged in user
|
||||
/// </summary>
|
||||
Task<IEnumerable<IAzureUserAccountSubscriptionContext>> GetSubscriptionsAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Returns user's azure subscriptions
|
||||
/// </summary>
|
||||
Task<IEnumerable<IAzureUserAccountSubscriptionContext>> GetSelectedSubscriptionsAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Finds a subscription given subscription id
|
||||
/// </summary>
|
||||
bool TryParseSubscriptionIdentifier(string value, out IAzureSubscriptionIdentifier subscription);
|
||||
|
||||
/// <summary>
|
||||
/// Stores the selected subscriptions given the ids
|
||||
/// </summary>
|
||||
Task<bool> SetSelectedSubscriptionsAsync(IEnumerable<string> subscriptionIds);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.Authentication
|
||||
{
|
||||
/// Contains information about an Azure subscription
|
||||
public interface IAzureSubscriptionContext : IEquatable<IAzureSubscriptionContext>
|
||||
{
|
||||
/// <summary>
|
||||
/// Subscription Identifier
|
||||
/// </summary>
|
||||
IAzureSubscriptionIdentifier Subscription
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscription name
|
||||
/// </summary>
|
||||
string SubscriptionName
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about an azure subscription identifier
|
||||
/// </summary>
|
||||
public interface IAzureSubscriptionIdentifier : IEquatable<IAzureSubscriptionIdentifier>
|
||||
{
|
||||
IAzureUserAccount UserAccount
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Service endpoint
|
||||
/// </summary>
|
||||
Uri ServiceManagementEndpoint
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscription id
|
||||
/// </summary>
|
||||
string SubscriptionId
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about an Azure user account
|
||||
/// </summary>
|
||||
public interface IAzureUserAccount : IEquatable<IAzureUserAccount>, IUserAccount
|
||||
{
|
||||
/// <summary>
|
||||
/// User Account Display Info
|
||||
/// </summary>
|
||||
IAzureUserAccountDisplayInfo DisplayInfo
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tenant Id
|
||||
/// </summary>
|
||||
string TenantId
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about an Azure user display
|
||||
/// </summary>
|
||||
public interface IAzureUserAccountDisplayInfo : IEquatable<IAzureUserAccountDisplayInfo>
|
||||
{
|
||||
/// <summary>
|
||||
/// Account Display Name
|
||||
/// </summary>
|
||||
string AccountDisplayName
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Account Logo
|
||||
/// </summary>
|
||||
byte[] AccountLogo
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provider Dislay Name
|
||||
/// </summary>
|
||||
string ProviderDisplayName
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provider Logo
|
||||
/// </summary>
|
||||
byte[] ProviderLogo
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// User Name
|
||||
/// </summary>
|
||||
string UserName
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about an Azure user account subscription
|
||||
/// </summary>
|
||||
public interface IAzureUserAccountSubscriptionContext :
|
||||
IAzureSubscriptionContext, IEquatable<IAzureUserAccountSubscriptionContext>
|
||||
{
|
||||
/// <summary>
|
||||
/// User Account
|
||||
/// </summary>
|
||||
IAzureUserAccount UserAccount
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// User account authentication information to be used by <see cref="IAccountManager" />
|
||||
/// </summary>
|
||||
public interface IUserAccount
|
||||
{
|
||||
/// <summary>
|
||||
/// The unique Id for the user
|
||||
/// </summary>
|
||||
string UniqueId
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if user needs reauthentication
|
||||
/// </summary>
|
||||
bool NeedsReauthentication
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
|
||||
using System;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Authentication;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Utilities used by resource provider related code
|
||||
/// </summary>
|
||||
public static class CommonUtil
|
||||
{
|
||||
private const int KeyValueNameLength = 1024; // 1024 should be enough for registry key value name.
|
||||
|
||||
//********************************************************************************************
|
||||
/// <summary>
|
||||
/// Throw an exception if the object is null.
|
||||
/// </summary>
|
||||
/// <param name="var">the object to check</param>
|
||||
/// <param name="varName">the variable or parameter name to display</param>
|
||||
//********************************************************************************************
|
||||
public static void CheckForNull(Object var, string varName)
|
||||
{
|
||||
if (var == null)
|
||||
{
|
||||
throw new ArgumentNullException(varName);
|
||||
}
|
||||
}
|
||||
|
||||
//********************************************************************************************
|
||||
/// <summary>
|
||||
/// Throw an exception if a string is null or empty.
|
||||
/// </summary>
|
||||
/// <param name="stringVar">string to check</param>
|
||||
/// <param name="stringVarName">the variable or parameter name to display</param>
|
||||
//********************************************************************************************
|
||||
public static void CheckStringForNullOrEmpty(string stringVar, string stringVarName)
|
||||
{
|
||||
CheckStringForNullOrEmpty(stringVar, stringVarName, false);
|
||||
}
|
||||
|
||||
//********************************************************************************************
|
||||
/// <summary>
|
||||
/// Throw an exception if a string is null or empty.
|
||||
/// </summary>
|
||||
/// <param name="stringVar">string to check</param>
|
||||
/// <param name="stringVarName">the variable or parameter name to display</param>
|
||||
/// <param name="trim">If true, will trim the string after it is determined not to be null</param>
|
||||
//********************************************************************************************
|
||||
public static void CheckStringForNullOrEmpty(string stringVar, string stringVarName, bool trim)
|
||||
{
|
||||
CheckForNull(stringVar, stringVarName);
|
||||
if (trim == true)
|
||||
{
|
||||
stringVar = stringVar.Trim();
|
||||
}
|
||||
if (stringVar.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("EmptyStringNotAllowed", stringVarName);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SameString(string value1, string value2)
|
||||
{
|
||||
return (value1 == null && value2 == null) || (value2 != null && value2.Equals(value1));
|
||||
}
|
||||
|
||||
public static bool SameUri(Uri value1, Uri value2)
|
||||
{
|
||||
return (value1 == null && value2 == null) || (value2 != null && value2.Equals(value1));
|
||||
}
|
||||
|
||||
public static bool SameSubscriptionIdentifier(IAzureSubscriptionIdentifier value1,
|
||||
IAzureSubscriptionIdentifier value2)
|
||||
{
|
||||
return (value1 == null && value2 == null) || (value2 != null && value2.Equals(value1));
|
||||
}
|
||||
|
||||
public static bool SameUserAccount(IAzureUserAccount value1, IAzureUserAccount value2)
|
||||
{
|
||||
return (value1 == null && value2 == null) || (value2 != null && value2.Equals(value1));
|
||||
}
|
||||
|
||||
public static string GetExceptionMessage(Exception e)
|
||||
{
|
||||
string message;
|
||||
|
||||
#if DEBUG
|
||||
string nl2 = Environment.NewLine + Environment.NewLine;
|
||||
message = e.Message + nl2 + "DEBUG ONLY:" + nl2 + e.ToString();
|
||||
#else
|
||||
message = e.Message;
|
||||
#endif
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Telemetry constants for Connection Dialog
|
||||
/// </summary>
|
||||
internal static class ConnectionConstants
|
||||
{
|
||||
#region connection value constants
|
||||
public static int SqlAzureEngineEditionId = 5;
|
||||
/// <summary>
|
||||
/// Constant value for master database name
|
||||
/// </summary>
|
||||
public const string MasterDatabaseName = "master";
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Data.Common;
|
||||
using System.Data.SqlClient;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods and utils for exceptions
|
||||
/// </summary>
|
||||
internal static class ExceptionUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns true if given exception if any of the inner exceptions is UserNeedsAuthenticationException
|
||||
/// </summary>
|
||||
internal static bool IsUserNeedsReauthenticateException(this Exception ex)
|
||||
{
|
||||
return ex.IsExceptionType(typeof(UserNeedsAuthenticationException));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if given exception if any of the inner exceptions is sql exception
|
||||
/// </summary>
|
||||
internal static bool IsDbException(this Exception ex)
|
||||
{
|
||||
return ex.IsExceptionType(typeof (DbException));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if given exception if any of the inner exceptions is same type of given type
|
||||
/// </summary>
|
||||
internal static bool IsExceptionType(this Exception ex, Type type)
|
||||
{
|
||||
if (ex == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (ex is AggregateException)
|
||||
{
|
||||
var aggregateException = (AggregateException)ex;
|
||||
return aggregateException.InnerExceptions != null &&
|
||||
aggregateException.InnerExceptions.Any(inner => inner.IsExceptionType(type));
|
||||
}
|
||||
else if (type.IsAssignableFrom(ex.GetType()) || (ex.InnerException != null && ex.InnerException.IsExceptionType(type)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static string GetExceptionMessage(this Exception ex)
|
||||
{
|
||||
string errorMessage = string.Empty;
|
||||
if (ex != null)
|
||||
{
|
||||
errorMessage = ex.Message;
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
errorMessage += " " + ex.InnerException.Message;
|
||||
}
|
||||
}
|
||||
return errorMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for service exceptions
|
||||
/// </summary>
|
||||
public abstract class ServiceExceptionBase : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AuthenticationFailedException class.
|
||||
/// </summary>
|
||||
protected ServiceExceptionBase()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AuthenticationFailedException class with a specified error message.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception. </param>
|
||||
protected ServiceExceptionBase(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AuthenticationFailedException class with a specified error message.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception. </param>
|
||||
/// <param name="httpStatusCode">The Http error code. </param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference
|
||||
/// (Nothing in Visual Basic) if no inner exception is specified</param>
|
||||
public ServiceExceptionBase(string message, HttpStatusCode httpStatusCode, Exception innerException = null)
|
||||
: this(message, (int)httpStatusCode, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AuthenticationFailedException class with a specified error message.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception. </param>
|
||||
/// <param name="httpStatusCode">The Http error code. </param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference
|
||||
/// (Nothing in Visual Basic) if no inner exception is specified</param>
|
||||
public ServiceExceptionBase(string message, int httpStatusCode, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
HttpStatusCode = (HttpStatusCode)httpStatusCode;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AuthenticationFailedException class with a specified error message
|
||||
/// and a reference to the inner exception that is the cause of this exception.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception. </param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference
|
||||
/// (Nothing in Visual Basic) if no inner exception is specified</param>
|
||||
protected ServiceExceptionBase(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AuthenticationFailedException class with serialized data.
|
||||
/// </summary>
|
||||
/// <param name="info">The SerializationInfo that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param>
|
||||
protected ServiceExceptionBase(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Http status code included in the exception
|
||||
/// </summary>
|
||||
public HttpStatusCode HttpStatusCode
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// The exception is used if any operation inside a service or provider fails
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ServiceFailedException : ServiceExceptionBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ServiceFailedException class.
|
||||
/// </summary>
|
||||
public ServiceFailedException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ServiceFailedException class with a specified error message.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception. </param>
|
||||
public ServiceFailedException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ServiceFailedException class with a specified error message
|
||||
/// and a reference to the inner exception that is the cause of this exception.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception. </param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference
|
||||
/// (Nothing in Visual Basic) if no inner exception is specified</param>
|
||||
public ServiceFailedException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ServiceFailedException class with serialized data.
|
||||
/// </summary>
|
||||
/// <param name="info">The SerializationInfo that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param>
|
||||
public ServiceFailedException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of ServiceFailedException by adding the server definition info to the given message
|
||||
/// </summary>
|
||||
internal static ServiceFailedException CreateException(string message, ServerDefinition serverDefinition, Exception innerException)
|
||||
{
|
||||
return new ServiceFailedException(
|
||||
string.Format(CultureInfo.CurrentCulture, message,
|
||||
serverDefinition != null ? serverDefinition.ServerType : string.Empty,
|
||||
serverDefinition != null ? serverDefinition.Category : string.Empty), innerException);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// The exception is used if any operation fails becauase user needs to reauthenticate
|
||||
/// </summary>
|
||||
public class UserNeedsAuthenticationException : ServiceExceptionBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ServiceFailedException class.
|
||||
/// </summary>
|
||||
public UserNeedsAuthenticationException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ServiceFailedException class with a specified error message.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception. </param>
|
||||
public UserNeedsAuthenticationException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ServiceFailedException class with a specified error message
|
||||
/// and a reference to the inner exception that is the cause of this exception.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception. </param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference
|
||||
/// (Nothing in Visual Basic) if no inner exception is specified</param>
|
||||
public UserNeedsAuthenticationException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the ServiceFailedException class with serialized data.
|
||||
/// </summary>
|
||||
/// <param name="info">The SerializationInfo that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param>
|
||||
public UserNeedsAuthenticationException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Authentication;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Extensibility;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation for <see cref="IDatabaseDiscoveryProvider"/> for Azure Sql databases.
|
||||
/// A discovery provider capable of finding Sql Azure databases for a specific Azure user account.
|
||||
/// </summary>
|
||||
|
||||
[Exportable(
|
||||
ServerTypes.SqlServer,
|
||||
Categories.Azure,
|
||||
typeof(IDatabaseDiscoveryProvider),
|
||||
"Microsoft.SqlServer.ConnectionServices.Azure.AzureDatabaseDiscoveryProvider")]
|
||||
public class AzureDatabaseDiscoveryProvider : ExportableBase, IDatabaseDiscoveryProvider, ISecureService, ICacheable<ServiceResponse<DatabaseInstanceInfo>>
|
||||
{
|
||||
private IAzureResourceManager _azureResourceManagerWrapper;
|
||||
private IAzureAuthenticationManager _azureAccountManager;
|
||||
private IDatabaseDiscoveryProvider _defaultDatabaseDiscoveryProvider;
|
||||
private readonly ConcurrentCache<ServiceResponse<DatabaseInstanceInfo>> _cache = new ConcurrentCache<ServiceResponse<DatabaseInstanceInfo>>();
|
||||
|
||||
public AzureDatabaseDiscoveryProvider()
|
||||
{
|
||||
// Duplicate the exportable attribute as at present we do not support filtering using extensiondescriptor.
|
||||
// The attribute is preserved in order to simplify ability to backport into existing tools
|
||||
Metadata = new ExportableMetadata(
|
||||
ServerTypes.SqlServer,
|
||||
Categories.Azure,
|
||||
"Microsoft.SqlServer.ConnectionServices.Azure.AzureDatabaseDiscoveryProvider");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// the event to raise when a database is found
|
||||
/// </summary>
|
||||
public event EventHandler<DatabaseInfoEventArgs> DatabaseFound;
|
||||
|
||||
/// <summary>
|
||||
/// Updates the cache for current selected subscriptions
|
||||
/// </summary>
|
||||
/// <returns>The new cached data</returns>
|
||||
public async Task<ServiceResponse<DatabaseInstanceInfo>> RefreshCacheAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
ServiceResponse<DatabaseInstanceInfo> result = new ServiceResponse<DatabaseInstanceInfo>();
|
||||
|
||||
if (await ClearCacheAsync())
|
||||
{
|
||||
result = await GetDatabaseInstancesAsync(serverName: null, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the cache for current selected subscriptions
|
||||
/// </summary>
|
||||
/// <returns>True if cache refreshed successfully. Otherwise returns false</returns>
|
||||
public async Task<bool> ClearCacheAsync()
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
if (AzureResourceManager != null && AccountManager != null && AzureAccountManager != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
IEnumerable<IAzureUserAccountSubscriptionContext> subscriptions = await GetSubscriptionsAsync();
|
||||
_cache.ClearCache(subscriptions.Select(x => x.Subscription.SubscriptionId));
|
||||
result = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TraceException(TraceEventType.Error, TraceId.AzureResource, ex, "Failed to refresh the cache");
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the databases for given connection info.
|
||||
/// The connection info should be used to make the connection for getting databases not the account manager
|
||||
/// </summary>
|
||||
//public async Task<ServiceResponse<DatabaseInstanceInfo>> GetDatabaseInstancesAsync(UIConnectionInfo uiConnectionInfo, CancellationToken cancellationToken)
|
||||
//{
|
||||
// ServiceResponse<DatabaseInstanceInfo> result = null;
|
||||
// if (DefaultDatabaseDiscoveryProvider != null && DefaultDatabaseDiscoveryProvider != this)
|
||||
// {
|
||||
// result = await DefaultDatabaseDiscoveryProvider.GetDatabaseInstancesAsync(uiConnectionInfo, cancellationToken);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// result = new ServiceResponse<DatabaseInstanceInfo>(); //TODO: add error that we couldn't find any default database provider
|
||||
// }
|
||||
// return result;
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the databases for given server name. Using the account manager to get the databases
|
||||
/// </summary>
|
||||
public async Task<ServiceResponse<DatabaseInstanceInfo>> GetDatabaseInstancesAsync(string serverName, CancellationToken cancellationToken)
|
||||
{
|
||||
ServiceResponse<DatabaseInstanceInfo> result = null;
|
||||
if (AzureResourceManager != null && AccountManager != null && AzureAccountManager != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
//if connection is passed, we need to search all subscriptions not selected ones
|
||||
IEnumerable<IAzureUserAccountSubscriptionContext> subscriptions = await GetSubscriptionsAsync(string.IsNullOrEmpty(serverName));
|
||||
if (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
result = await AzureUtil.ExecuteGetAzureResourceAsParallel(null, subscriptions, serverName, cancellationToken,
|
||||
GetDatabaseForSubscriptionAsync);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result = new ServiceResponse<DatabaseInstanceInfo>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
result = result ?? new ServiceResponse<DatabaseInstanceInfo>();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the resource manager that has same metadata as this class
|
||||
/// </summary>
|
||||
public IAzureResourceManager AzureResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_azureResourceManagerWrapper = _azureResourceManagerWrapper ?? GetService<IAzureResourceManager>());
|
||||
}
|
||||
set
|
||||
{
|
||||
_azureResourceManagerWrapper = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the account manager that has same metadata as this class
|
||||
/// </summary>
|
||||
public IAzureAuthenticationManager AzureAccountManager
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_azureAccountManager = _azureAccountManager ?? GetService<IAzureAuthenticationManager>());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the account manager that has same metadata as this class
|
||||
/// </summary>
|
||||
public IDatabaseDiscoveryProvider DefaultDatabaseDiscoveryProvider
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_defaultDatabaseDiscoveryProvider = _defaultDatabaseDiscoveryProvider ?? GetService<IDatabaseDiscoveryProvider>(null));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Account Manager
|
||||
/// </summary>
|
||||
public IAccountManager AccountManager
|
||||
{
|
||||
get
|
||||
{
|
||||
return AzureAccountManager;
|
||||
|
||||
}
|
||||
set
|
||||
{
|
||||
_azureAccountManager = value as IAzureAuthenticationManager;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns azure subscriptions.
|
||||
/// </summary>
|
||||
private async Task<IEnumerable<IAzureUserAccountSubscriptionContext>> GetSubscriptionsAsync(bool selectedOnly = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
return selectedOnly ? await AzureAccountManager.GetSelectedSubscriptionsAsync() : await AzureAccountManager.GetSubscriptionsAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ServiceFailedException(string.Format(CultureInfo.CurrentCulture, SR.AzureSubscriptionFailedErrorMessage, ex));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// There was a wired nullReferencedException was running the tasks parallel. It only got fixed when I put the getting from cache insed an async method
|
||||
/// </summary>
|
||||
private Task<ServiceResponse<DatabaseInstanceInfo>> GetFromCacheAsync(string key)
|
||||
{
|
||||
return Task.Factory.StartNew(() => _cache.Get(key));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of Azure sql databases for given subscription
|
||||
/// </summary>
|
||||
private async Task<ServiceResponse<DatabaseInstanceInfo>> GetDatabaseForSubscriptionAsync(IAzureResourceManagementSession parentSession,
|
||||
IAzureUserAccountSubscriptionContext input, string serverName,
|
||||
CancellationToken cancellationToken, CancellationToken internalCancellationToken)
|
||||
{
|
||||
ServiceResponse<DatabaseInstanceInfo> result = null;
|
||||
bool shouldFilter = !string.IsNullOrEmpty(serverName);
|
||||
try
|
||||
{
|
||||
string key = input.Subscription.SubscriptionId;
|
||||
|
||||
//when the data was coming from cache and no async mthod was called the parallel tasks running crashed so I had to call this line async to fix it
|
||||
result = await GetFromCacheAsync(key);
|
||||
if (result == null)
|
||||
{
|
||||
//this will only get the databases for the given server name
|
||||
result = await GetDatabaseForSubscriptionFromServiceAsync(input, serverName, cancellationToken, internalCancellationToken);
|
||||
}
|
||||
else if (shouldFilter)
|
||||
{
|
||||
//we should filter the result because the cached data includes databases for all servers
|
||||
result = new ServiceResponse<DatabaseInstanceInfo>(result.Data.Where(x => x.ServerInstanceInfo.FullyQualifiedDomainName == serverName),
|
||||
result.Errors);
|
||||
}
|
||||
|
||||
//only update the cache if server name is not passes so the result is not filtered. The cache data supposed to be the data for all server
|
||||
if (!shouldFilter && !cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
result = _cache.UpdateCache(key, result);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result = new ServiceResponse<DatabaseInstanceInfo>(ex);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of Azure sql databases for given subscription
|
||||
/// </summary>
|
||||
private async Task<ServiceResponse<DatabaseInstanceInfo>> GetDatabaseForSubscriptionFromServiceAsync(
|
||||
IAzureUserAccountSubscriptionContext input, string serverName,
|
||||
CancellationToken cancellationToken, CancellationToken internalCancellationToken)
|
||||
{
|
||||
ServiceResponse<DatabaseInstanceInfo> result = null;
|
||||
|
||||
try
|
||||
{
|
||||
if (!cancellationToken.IsCancellationRequested && !internalCancellationToken.IsCancellationRequested)
|
||||
{
|
||||
using (IAzureResourceManagementSession session = await AzureResourceManager.CreateSessionAsync(input))
|
||||
{
|
||||
//find the server matches with the given servername which should be only one
|
||||
bool shouldFilter = !string.IsNullOrEmpty(serverName);
|
||||
IEnumerable<IAzureSqlServerResource> sqlAzureServers = await AzureResourceManager.GetSqlServerAzureResourcesAsync(session);
|
||||
IEnumerable<IAzureSqlServerResource> filteredServers = !shouldFilter ? sqlAzureServers : sqlAzureServers.Where(x =>
|
||||
x.FullyQualifiedDomainName != null &&
|
||||
x.FullyQualifiedDomainName.Equals(serverName,
|
||||
StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
IList<IAzureSqlServerResource> filteredServersList = filteredServers.ToList();
|
||||
result = await GetDatabasesForSubscriptionServersAsync(session, filteredServersList.ToList(), cancellationToken);
|
||||
|
||||
//Set response Found to true to notify the other tasks to cancel
|
||||
if (shouldFilter && filteredServersList.Any())
|
||||
{
|
||||
result.Found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result = new ServiceResponse<DatabaseInstanceInfo>(ex);
|
||||
}
|
||||
|
||||
return result ?? new ServiceResponse<DatabaseInstanceInfo>();
|
||||
}
|
||||
|
||||
private async Task<ServiceResponse<DatabaseInstanceInfo>> GetDatabasesForSubscriptionServersAsync(IAzureResourceManagementSession session,
|
||||
IList<IAzureSqlServerResource> filteredServersList, CancellationToken cancellationToken)
|
||||
{
|
||||
ServiceResponse<DatabaseInstanceInfo> result = null;
|
||||
AzureServerDatabaseDiscoveryProvider azureServerDatabaseDiscoveryProvider = new AzureServerDatabaseDiscoveryProvider(AzureResourceManager, session, ServerDefinition);
|
||||
azureServerDatabaseDiscoveryProvider.DatabaseFound += AzureServerDatabaseDiscoveryProviderOnDatabaseFound;
|
||||
if (filteredServersList.Any())
|
||||
{
|
||||
result = await azureServerDatabaseDiscoveryProvider.GetDatabasesForServers(filteredServersList, cancellationToken);
|
||||
}
|
||||
|
||||
return result ?? new ServiceResponse<DatabaseInstanceInfo>();
|
||||
}
|
||||
|
||||
private void AzureServerDatabaseDiscoveryProviderOnDatabaseFound(object sender, DatabaseInfoEventArgs databaseInfoEventArgs)
|
||||
{
|
||||
OnDatabaseFound(databaseInfoEventArgs.Database);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises DatabaseFound event with the given databases info
|
||||
/// </summary>
|
||||
private void OnDatabaseFound(DatabaseInstanceInfo databaseInfo)
|
||||
{
|
||||
if (DatabaseFound != null)
|
||||
{
|
||||
DatabaseFound(this, new DatabaseInfoEventArgs() { Database = databaseInfo });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
internal class AzureServerDatabaseDiscoveryProvider
|
||||
{
|
||||
private readonly IAzureResourceManagementSession _session;
|
||||
/// <summary>
|
||||
/// the event to raise when a database is found
|
||||
/// </summary>
|
||||
public event EventHandler<DatabaseInfoEventArgs> DatabaseFound;
|
||||
|
||||
public AzureServerDatabaseDiscoveryProvider(IAzureResourceManager azureResourceManager, IAzureResourceManagementSession session, ServerDefinition serverDefinition)
|
||||
{
|
||||
CommonUtil.CheckForNull(session, "session");
|
||||
CommonUtil.CheckForNull(azureResourceManager, "azureResourceManager");
|
||||
_session = session;
|
||||
AzureResourceManager = azureResourceManager;
|
||||
|
||||
ServerDefinition = serverDefinition ?? ServerDefinition.Default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the resource manager that has same metadata as this class
|
||||
/// </summary>
|
||||
internal IAzureResourceManager AzureResourceManager { get; set; }
|
||||
|
||||
private ServerDefinition ServerDefinition { get; set; }
|
||||
|
||||
public async Task<ServiceResponse<DatabaseInstanceInfo>> GetDatabasesForServers(IList<IAzureSqlServerResource> serverResources, CancellationToken cancellationToken)
|
||||
{
|
||||
ServiceResponse < DatabaseInstanceInfo > result = new ServiceResponse<DatabaseInstanceInfo>();
|
||||
if (serverResources != null)
|
||||
{
|
||||
result = await AzureUtil.ExecuteGetAzureResourceAsParallel(_session, serverResources, null, cancellationToken, GetDatabasesForServerFromService);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<ServiceResponse<DatabaseInstanceInfo>> GetDatabasesForServerFromService(
|
||||
IAzureResourceManagementSession session,
|
||||
IAzureSqlServerResource azureSqlServer,
|
||||
string serverName,
|
||||
CancellationToken cancellationToken,
|
||||
CancellationToken internalCancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return new ServiceResponse<DatabaseInstanceInfo>();
|
||||
}
|
||||
ServerInstanceInfo serverInstanceInfo = new ServerInstanceInfo(ServerDefinition)
|
||||
{
|
||||
Name = azureSqlServer.Name,
|
||||
FullyQualifiedDomainName = azureSqlServer.FullyQualifiedDomainName,
|
||||
AdministratorLogin = azureSqlServer.AdministratorLogin
|
||||
};
|
||||
OnDatabaseFound(new DatabaseInstanceInfo(serverInstanceInfo));
|
||||
IEnumerable<IAzureResource> databases = await AzureResourceManager.GetAzureDatabasesAsync(
|
||||
session,
|
||||
azureSqlServer.ResourceGroupName,
|
||||
azureSqlServer.Name);
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return new ServiceResponse<DatabaseInstanceInfo>();
|
||||
}
|
||||
else
|
||||
{
|
||||
IEnumerable<DatabaseInstanceInfo> data = databases.Select(x => ConvertToModel(serverInstanceInfo, x));
|
||||
ServiceResponse<DatabaseInstanceInfo> result = new ServiceResponse<DatabaseInstanceInfo>(data);
|
||||
foreach (var databaseInstance in result.Data)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
break;
|
||||
}
|
||||
OnDatabaseFound(databaseInstance);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ServiceResponse<DatabaseInstanceInfo>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises DatabaseFound event with the given databases info
|
||||
/// </summary>
|
||||
private void OnDatabaseFound(DatabaseInstanceInfo databaseInfo)
|
||||
{
|
||||
if (DatabaseFound != null)
|
||||
{
|
||||
DatabaseFound(this, new DatabaseInfoEventArgs() { Database = databaseInfo });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the resource to DatabaseInstanceInfo
|
||||
/// </summary>
|
||||
private DatabaseInstanceInfo ConvertToModel(ServerInstanceInfo serverInstanceInfo, IAzureResource azureResource)
|
||||
{
|
||||
DatabaseInstanceInfo databaseInstance = new DatabaseInstanceInfo(serverInstanceInfo)
|
||||
{
|
||||
Name = azureResource.Name.Replace(serverInstanceInfo.Name + "/", "")
|
||||
};
|
||||
|
||||
return databaseInstance;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Authentication;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Extensibility;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation for <see cref="IServerDiscoveryProvider"/> for Azure Sql servers.
|
||||
/// A discovery provider capable of finding Sql Azure servers for a specific Azure user account.
|
||||
/// </summary>
|
||||
[Exportable(
|
||||
ServerTypes.SqlServer,
|
||||
Categories.Azure,
|
||||
typeof(IServerDiscoveryProvider),
|
||||
"Microsoft.SqlServer.ConnectionServices.Azure.AzureServerDiscoveryProvider")]
|
||||
internal class AzureSqlServerDiscoveryProvider : ExportableBase, IServerDiscoveryProvider, ISecureService
|
||||
{
|
||||
private IAzureResourceManager _azureResourceManagerWrapper;
|
||||
private IAzureAuthenticationManager _azureAccountManager;
|
||||
|
||||
public AzureSqlServerDiscoveryProvider()
|
||||
{
|
||||
// Duplicate the exportable attribute as at present we do not support filtering using extensiondescriptor.
|
||||
// The attribute is preserved in order to simplify ability to backport into existing tools
|
||||
Metadata = new ExportableMetadata(
|
||||
ServerTypes.SqlServer,
|
||||
Categories.Azure,
|
||||
"Microsoft.SqlServer.ConnectionServices.Azure.AzureServerDiscoveryProvider");
|
||||
}
|
||||
|
||||
public async Task<ServiceResponse<ServerInstanceInfo>> GetServerInstancesAsync()
|
||||
{
|
||||
ServiceResponse<ServerInstanceInfo> result = new ServiceResponse<ServerInstanceInfo>();
|
||||
List<ServerInstanceInfo> serverInstances = new List<ServerInstanceInfo>();
|
||||
|
||||
if (AccountManager != null && AzureAccountManager != null && AzureResourceManager != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
IEnumerable<IAzureUserAccountSubscriptionContext> subscriptions =
|
||||
await AzureAccountManager.GetSelectedSubscriptionsAsync();
|
||||
if (subscriptions != null)
|
||||
{
|
||||
foreach (IAzureUserAccountSubscriptionContext subscription in subscriptions)
|
||||
{
|
||||
using (IAzureResourceManagementSession session = await AzureResourceManager.CreateSessionAsync(subscription))
|
||||
{
|
||||
IEnumerable<IAzureSqlServerResource> azureResources =
|
||||
await AzureResourceManager.GetSqlServerAzureResourcesAsync(session);
|
||||
serverInstances.AddRange(
|
||||
azureResources.Select(x =>
|
||||
new ServerInstanceInfo(ServerDefinition)
|
||||
{
|
||||
Name = x.Name,
|
||||
FullyQualifiedDomainName = x.FullyQualifiedDomainName,
|
||||
AdministratorLogin = x.AdministratorLogin
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
result = new ServiceResponse<ServerInstanceInfo>(serverInstances);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result = new ServiceResponse<ServerInstanceInfo>(serverInstances, new List<Exception>() {ex});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the resource manager that has same metadata as this class
|
||||
/// </summary>
|
||||
public IAzureResourceManager AzureResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_azureResourceManagerWrapper =
|
||||
_azureResourceManagerWrapper ??
|
||||
GetService<IAzureResourceManager>());
|
||||
}
|
||||
internal set
|
||||
{
|
||||
_azureResourceManagerWrapper = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the account manager that has same metadata as this class
|
||||
/// </summary>
|
||||
public IAzureAuthenticationManager AzureAccountManager
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_azureAccountManager =
|
||||
_azureAccountManager ??
|
||||
GetService<IAzureAuthenticationManager>());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Account Manager
|
||||
/// </summary>
|
||||
public IAccountManager AccountManager
|
||||
{
|
||||
get
|
||||
{
|
||||
return AzureAccountManager;
|
||||
|
||||
}
|
||||
internal set
|
||||
{
|
||||
_azureAccountManager = value as IAzureAuthenticationManager;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
internal static class AzureUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Execute an async action for each input in the a list of input in parallel.
|
||||
/// If any task fails, adds the exeption message to the response errors
|
||||
/// If cancellation token is set to cancel, returns empty response
|
||||
/// </summary>
|
||||
/// <param name="session">Resource management session to use to call the resource manager</param>
|
||||
/// <param name="inputs">List of inputs</param>
|
||||
/// <param name="serverName">server name to filter the result</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <param name="asyncAction">Async action</param>
|
||||
/// <returns>ServiceResponse including the list of data and errors</returns>
|
||||
public static async Task<ServiceResponse<TResult>> ExecuteGetAzureResourceAsParallel<TInput, TResult>(
|
||||
IAzureResourceManagementSession session,
|
||||
IEnumerable<TInput> inputs,
|
||||
string serverName,
|
||||
CancellationToken cancellationToken,
|
||||
Func<IAzureResourceManagementSession,
|
||||
TInput,
|
||||
string,
|
||||
CancellationToken,
|
||||
CancellationToken,
|
||||
Task<ServiceResponse<TResult>>> asyncAction
|
||||
)
|
||||
{
|
||||
List<TResult> mergedResult = new List<TResult>();
|
||||
List<Exception> mergedErrors = new List<Exception>();
|
||||
try
|
||||
{
|
||||
if (inputs == null)
|
||||
{
|
||||
return new ServiceResponse<TResult>(mergedResult);
|
||||
}
|
||||
List<TInput> inputList = inputs.ToList();
|
||||
|
||||
ServiceResponse<TResult>[] resultList = new ServiceResponse<TResult>[inputList.Count];
|
||||
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
|
||||
var tasks = Enumerable.Range(0, inputList.Count())
|
||||
.Select(async i =>
|
||||
{
|
||||
ServiceResponse<TResult> result = await GetResult(session, inputList[i], serverName, cancellationToken,
|
||||
cancellationTokenSource.Token, asyncAction);
|
||||
//server name is used to filter the result and if the data is already found not, we need to cancel the other tasks
|
||||
if (!string.IsNullOrEmpty(serverName) && result.Found)
|
||||
{
|
||||
cancellationTokenSource.Cancel();
|
||||
}
|
||||
resultList[i] = result;
|
||||
return result;
|
||||
}
|
||||
);
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
|
||||
if (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
foreach (ServiceResponse<TResult> resultForEachInput in resultList)
|
||||
{
|
||||
mergedResult.AddRange(resultForEachInput.Data);
|
||||
mergedErrors.AddRange(resultForEachInput.Errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
mergedErrors.Add(ex);
|
||||
return new ServiceResponse<TResult>(mergedResult, mergedErrors);
|
||||
}
|
||||
return new ServiceResponse<TResult>(mergedResult, mergedErrors);
|
||||
}
|
||||
|
||||
private static async Task<ServiceResponse<TResult>> GetResult<TInput, TResult>(
|
||||
IAzureResourceManagementSession session,
|
||||
TInput input,
|
||||
string serverName,
|
||||
CancellationToken cancellationToken,
|
||||
CancellationToken internalCancellationToken,
|
||||
Func<IAzureResourceManagementSession,
|
||||
TInput,
|
||||
string,
|
||||
CancellationToken,
|
||||
CancellationToken,
|
||||
Task<ServiceResponse<TResult>>> asyncAction
|
||||
)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested || internalCancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return new ServiceResponse<TResult>();
|
||||
}
|
||||
try
|
||||
{
|
||||
return await asyncAction(session, input, serverName, cancellationToken, internalCancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ServiceResponse<TResult>(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Event arguments to use for database info events
|
||||
/// </summary>
|
||||
public class DatabaseInfoEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Database Info
|
||||
/// </summary>
|
||||
public DatabaseInstanceInfo Database
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Includes the data for a discovered database
|
||||
/// </summary>
|
||||
public class DatabaseInstanceInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Default constructor to initialize the instance
|
||||
/// </summary>
|
||||
/// <param name="serverInstanceInfo"></param>
|
||||
public DatabaseInstanceInfo(ServerInstanceInfo serverInstanceInfo)
|
||||
{
|
||||
ServerInstanceInfo = serverInstanceInfo;
|
||||
}
|
||||
|
||||
public DatabaseInstanceInfo(ServerDefinition serverDefinition, string serverName, string databaseName)
|
||||
{
|
||||
ServerInstanceInfo = new ServerInstanceInfo(serverDefinition)
|
||||
{
|
||||
Name = serverName
|
||||
};
|
||||
Name = databaseName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Server instance info associated to the database instance
|
||||
/// </summary>
|
||||
public ServerInstanceInfo ServerInstanceInfo
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Database Name
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the database is the master database
|
||||
/// </summary>
|
||||
public bool IsMaster
|
||||
{
|
||||
get { return ConnectionConstants.MasterDatabaseName.Equals(Name, StringComparison.OrdinalIgnoreCase); }
|
||||
}
|
||||
|
||||
public bool IsDefaultDatabase { get; set; }
|
||||
|
||||
public bool IsSystemDatabase { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for any implementation of azure resource
|
||||
/// </summary>
|
||||
public interface IAzureResource
|
||||
{
|
||||
/// <summary>
|
||||
/// Azure Resource Name
|
||||
/// </summary>
|
||||
string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Azure Resource Type
|
||||
/// </summary>
|
||||
string Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Azure Resource Id
|
||||
/// </summary>
|
||||
string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Resource Group Name
|
||||
/// </summary>
|
||||
string ResourceGroupName
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resource Location
|
||||
/// </summary>
|
||||
string Location { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Authentication;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A session used by <see cref="IAzureResourceManager" />. Includes all the clients that the resource management needs to get ther resources
|
||||
/// </summary>
|
||||
public interface IAzureResourceManagementSession : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Closes the session
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
bool CloseSession();
|
||||
|
||||
/// <summary>
|
||||
/// Teh subscription for the current session
|
||||
/// </summary>
|
||||
IAzureUserAccountSubscriptionContext SubscriptionContext
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Authentication;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.FirewallRule;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Extensibility;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides functionality to get azure resources by making Http request to the Azure REST API
|
||||
/// </summary>
|
||||
public interface IAzureResourceManager : IExportable
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a list of azure databases given subscription resource group name and server name
|
||||
/// </summary>
|
||||
/// <param name="azureResourceManagementSession">Subscription Context which includes credentials to use in the resource manager</param>
|
||||
/// <param name="resourceGroupName">Resource Group Name</param>
|
||||
/// <param name="serverName">Server name</param>
|
||||
/// <returns>The list of databases</returns>
|
||||
Task<IEnumerable<IAzureResource>> GetAzureDatabasesAsync(
|
||||
IAzureResourceManagementSession azureResourceManagementSession,
|
||||
string resourceGroupName,
|
||||
string serverName);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of azure servers given subscription
|
||||
/// </summary>
|
||||
/// <param name="azureResourceManagementSession">Subscription Context which includes credentials to use in the resource manager</param>
|
||||
/// <returns>The list of Sql server resources</returns>
|
||||
Task<IEnumerable<IAzureSqlServerResource>> GetSqlServerAzureResourcesAsync(
|
||||
IAzureResourceManagementSession azureResourceManagementSession);
|
||||
|
||||
/// <summary>
|
||||
/// Create new firewall rule given user subscription, Sql server resource and the firewall rule request
|
||||
/// </summary>
|
||||
/// <param name="azureResourceManagementSession">Subscription Context which includes credentials to use in the resource manager</param>
|
||||
/// <param name="azureSqlServer">Sql server resource to create firewall rule for</param>
|
||||
/// <param name="firewallRuleRequest">Firewall rule request including the name and IP address range</param>
|
||||
/// <returns></returns>
|
||||
Task<FirewallRuleResponse> CreateFirewallRuleAsync(
|
||||
IAzureResourceManagementSession azureResourceManagementSession,
|
||||
IAzureSqlServerResource azureSqlServer,
|
||||
FirewallRuleRequest firewallRuleRequest
|
||||
);
|
||||
|
||||
Task<IAzureResourceManagementSession> CreateSessionAsync(IAzureUserAccountSubscriptionContext subscriptionContext);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// An Azure Sql server resource
|
||||
/// </summary>
|
||||
public interface IAzureSqlServerResource : IAzureResource
|
||||
{
|
||||
/// <summary>
|
||||
/// Fully qualified domain name
|
||||
/// </summary>
|
||||
string FullyQualifiedDomainName
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Administrator Login
|
||||
/// </summary>
|
||||
string AdministratorLogin
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a class as cachable
|
||||
/// </summary>
|
||||
public interface ICacheable<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Clears the cache for current user
|
||||
/// </summary>
|
||||
/// <returns>True if cache refreshed successfully. Otherwise returns false</returns>
|
||||
Task<bool> ClearCacheAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Updates the cache for current selected subscriptions
|
||||
/// </summary>
|
||||
/// <returns>The new cached data</returns>
|
||||
Task<T> RefreshCacheAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Extensibility;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A discovery provider capable of finding databases for a given server type and category.
|
||||
/// For example: finding SQL Server databases in Azure, or on the local network.
|
||||
/// Implementing classes must add a <see cref="ExportableAttribute" />
|
||||
/// to the class in order to be found by the extension manager,
|
||||
/// and to define the type and category supported
|
||||
/// </summary>
|
||||
public interface IDatabaseDiscoveryProvider : IExportable
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the databases for given server name.
|
||||
/// </summary>
|
||||
Task<ServiceResponse<DatabaseInstanceInfo>> GetDatabaseInstancesAsync(string serverName, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the databases for given connection info.
|
||||
/// The connection info should be used to make the connection for getting databases not the account manager
|
||||
/// </summary>
|
||||
//Task<ServiceResponse<DatabaseInstanceInfo>> GetDatabaseInstancesAsync(UIConnectionInfo uiConnectionInfo, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// the event to raise when a database is found
|
||||
/// </summary>
|
||||
event EventHandler<DatabaseInfoEventArgs> DatabaseFound;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Authentication;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a class as secure which requires an account to function
|
||||
/// </summary>
|
||||
public interface ISecureService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the account manager instance
|
||||
/// </summary>
|
||||
IAccountManager AccountManager
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Extensibility;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A discovery provider capable of finding servers for a given server type and category.
|
||||
/// For example: finding SQL Servers in Azure, or on the local network.
|
||||
/// Implementing classes must add a <see cref="ExportableAttribute" />
|
||||
/// to the class in order to be found by the extension manager,
|
||||
/// and to define the type and category supported
|
||||
/// </summary>
|
||||
public interface IServerDiscoveryProvider : IExportable
|
||||
{
|
||||
/// <summary>
|
||||
/// Discovers the server instances
|
||||
/// </summary>
|
||||
Task<ServiceResponse<ServerInstanceInfo>> GetServerInstancesAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a server grouping based on the type of server connection supported (SQL Server, Reporting Server, Analysis Server)
|
||||
/// and a Category by which these should be shown to the user.
|
||||
/// Built in examples of categories include Local, Network, and Azure and additional categories can be defined as needed.
|
||||
/// Note that the Connection Dialog UI may require Category to be set for some resource types such as<see cref="IServerDiscoveryProvider" />.
|
||||
/// In addition a UI section matching that category may be required, or else the provider will not be used by any UI part and never be called.
|
||||
/// </summary>
|
||||
public interface IServerDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// Category by which resources can be grouped. Built in examples of categories include Local, Network, and Azure and additional categories can be defined as needed.
|
||||
/// </summary>
|
||||
string Category
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type of server connection supported. Examples include SQL Server, Reporting Server, Analysis Server.
|
||||
/// </summary>
|
||||
string ServerType
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The implementation of the server definition that implements the properties
|
||||
/// </summary>
|
||||
public sealed class ServerDefinition : IServerDefinition
|
||||
{
|
||||
private static ServerDefinition _default = new ServerDefinition(ServerTypes.SqlServer, string.Empty);
|
||||
|
||||
public ServerDefinition(string serverType, string category)
|
||||
{
|
||||
ServerType = serverType;
|
||||
Category = category;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="IServerDefinition.ServerType"/>
|
||||
/// </summary>
|
||||
public string ServerType { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="IServerDefinition.Category"/>
|
||||
/// </summary>
|
||||
public string Category { private set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// Default value for the server definition
|
||||
/// </summary>
|
||||
public static ServerDefinition Default
|
||||
{
|
||||
get { return _default; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about a server discovered by <see cref="IServerDiscoveryProvider" />
|
||||
/// </summary>
|
||||
public class ServerInstanceInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes the new instance with server and location
|
||||
/// </summary>
|
||||
public ServerInstanceInfo(IServerDefinition serverDefinition)
|
||||
{
|
||||
ServerDefinition = serverDefinition;
|
||||
}
|
||||
|
||||
public ServerInstanceInfo()
|
||||
{
|
||||
}
|
||||
|
||||
public IServerDefinition ServerDefinition
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Server Name
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fully qualified domain name
|
||||
/// </summary>
|
||||
public string FullyQualifiedDomainName
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Administrator Login
|
||||
/// </summary>
|
||||
public string AdministratorLogin
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// List of built-in server types used in <see cref="ExportableAttribute" />.
|
||||
/// Defines a server grouping based on the type of server connection supported (SQL Server, Reporting Server, Analysis Server)
|
||||
/// Additional server types can be defined as needed.
|
||||
/// Note that the Connection Dialog UI may require server type to be set for some resource types such as<see cref="IServerDiscoveryProvider" />.
|
||||
/// In addition a UI section matching that category may be required, or else the provider will not be used by any UI part and never be called.
|
||||
/// </summary>
|
||||
public static class ServerTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// Sql server
|
||||
/// </summary>
|
||||
public const string SqlServer = "SqlServer";
|
||||
|
||||
/// <summary>
|
||||
/// Reporting server
|
||||
/// </summary>
|
||||
public const string SqlReportingServer = "SqlReportingServer";
|
||||
|
||||
/// <summary>
|
||||
/// Integration server
|
||||
/// </summary>
|
||||
public const string SqlIntegrationServer = "SqlIntegrationServer";
|
||||
|
||||
/// <summary>
|
||||
/// Analysis server
|
||||
/// </summary>
|
||||
public const string SqlAnalysisServer = "SqlAnalysisServer";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of built-in categories used in <see cref="ExportableAttribute" />
|
||||
/// Defines a server grouping based on the category of server connection supported (Network, Local, Azure)
|
||||
/// Additional categories can be defined as needed.
|
||||
/// Note that the Connection Dialog UI may require Category to be set for some resource types such as<see cref="IServerDiscoveryProvider" />.
|
||||
/// In addition a UI section matching that category may be required, or else the provider will not be used by any UI part and never be called.
|
||||
/// </summary>
|
||||
public static class Categories
|
||||
{
|
||||
/// <summary>
|
||||
/// Network category
|
||||
/// </summary>
|
||||
public const string Network = "network";
|
||||
|
||||
/// <summary>
|
||||
/// Azure category
|
||||
/// </summary>
|
||||
public const string Azure = "azure";
|
||||
|
||||
/// <summary>
|
||||
/// local category
|
||||
/// </summary>
|
||||
public const string Local = "local";
|
||||
|
||||
/// <summary>
|
||||
/// local db category
|
||||
/// </summary>
|
||||
public const string LocalDb = "localdb";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the data that a service wants to returns plus the errors happened during getting some of the data
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class ServiceResponse<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates new instance given data and errors
|
||||
/// </summary>
|
||||
public ServiceResponse(IEnumerable<T> data, IEnumerable<Exception> errors)
|
||||
{
|
||||
Data = data;
|
||||
Errors = errors;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates new instance given errors
|
||||
/// </summary>
|
||||
public ServiceResponse(IEnumerable<Exception> errors) : this(Enumerable.Empty<T>(), errors)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates new instance given data
|
||||
/// </summary>
|
||||
public ServiceResponse(IEnumerable<T> data) : this(data, Enumerable.Empty<Exception>())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creating new empry instance
|
||||
/// </summary>
|
||||
public ServiceResponse() : this(Enumerable.Empty<T>(), Enumerable.Empty<Exception>())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates new instance given exception to create the error list
|
||||
/// </summary>
|
||||
public ServiceResponse(Exception ex) : this(Enumerable.Empty<T>(), new List<Exception> {ex})
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Information a service wants to returns
|
||||
/// </summary>
|
||||
public IEnumerable<T> Data { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The errors that heppend during retrieving data
|
||||
/// </summary>
|
||||
public IEnumerable<Exception> Errors { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Return true if the response includes errors
|
||||
/// </summary>
|
||||
public bool HasError
|
||||
{
|
||||
get { return Errors != null && Errors.Any(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Concatenates the messages into one error message
|
||||
/// </summary>
|
||||
public string ErrorMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
string message = string.Empty;
|
||||
if (HasError)
|
||||
{
|
||||
message = string.Join(Environment.NewLine, Errors.Select(x => x.GetExceptionMessage()));
|
||||
}
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if a response already found. it's used when we need to filter the responses
|
||||
/// </summary>
|
||||
public bool Found { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Composition;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.Extensibility
|
||||
{
|
||||
/// <summary>
|
||||
/// Attribute defining a service export, and the metadata about that service. Implements IServiceMetadata,
|
||||
/// which should be used on the importer side to ensure type consistency. Services and providers have to add this property
|
||||
/// in order to be found by the extension manager
|
||||
/// </summary>
|
||||
[MetadataAttribute]
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
||||
public class ExportableAttribute : ExportAttribute, IExportableMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// Default constructor to initialize the metadata of the exportable
|
||||
/// </summary>
|
||||
/// <param name="serverType">The server type supported by the exportable. If not set, exportable supports all server types</param>
|
||||
/// <param name="category">The category supported by the exportable. If not set, exportable supports all categories </param>
|
||||
/// <param name="type">The type of the exportable to be used by the extension manager to find the exportable</param>
|
||||
/// <param name="id">The unique id of the exportable. Used by the extension manager to pick only one from exportables with same id in the same assembly</param>
|
||||
/// <param name="priority">The priority of the exportable. The extension manager will pick the exportable with the highest priority if multiple found</param>
|
||||
/// <param name="displayName">The display name of the exportable. This field is optional</param>
|
||||
public ExportableAttribute(
|
||||
string serverType,
|
||||
string category,
|
||||
Type type,
|
||||
string id,
|
||||
int priority = 0,
|
||||
string displayName = null) : base(type)
|
||||
{
|
||||
Category = category;
|
||||
ServerType = serverType;
|
||||
Id = id;
|
||||
DisplayName = displayName;
|
||||
Priority = priority;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The constructor to define an exportable by type, id and priority only. To be used by the exportables that support all server types and categories.
|
||||
/// For example: the implementation of <see cref="ITrace" /> can be used for all server types and categories.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of the exportable to be used by the extension manager to find the exportable</param>
|
||||
/// <param name="id">The unique id of the exportable. Used by the extension manager to pick only one from exportables with same id in the same assembly</param>
|
||||
/// <param name="priority">The priority of the exportable. The extension manager will pick the exportable with the highest priority if multiple found</param>
|
||||
/// <param name="displayName">The display name of the exportable. This field is optional</param>
|
||||
public ExportableAttribute(Type type, string id, int priority = 0,
|
||||
string displayName = null) :
|
||||
this(String.Empty, String.Empty, type, id, priority, displayName)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thye category of the service
|
||||
/// </summary>
|
||||
public string Category
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The server type of that the service supports
|
||||
/// </summary>
|
||||
public string ServerType
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The version of this extension
|
||||
/// </summary>
|
||||
public string Version
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The id of the extension
|
||||
/// </summary>
|
||||
public string Id
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The display name for the extension
|
||||
/// </summary>
|
||||
public string DisplayName
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// priority of the extension. Can be used to filter the extensions if multiple found
|
||||
/// </summary>
|
||||
public int Priority
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Linq;
|
||||
using Microsoft.SqlTools.Extensibility;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.Extensibility
|
||||
{
|
||||
/// <summary>
|
||||
/// The base class for all exportable classes.
|
||||
/// </summary>
|
||||
public abstract class ExportableBase : TraceableBase, IExportable, IComposableService
|
||||
{
|
||||
private ITrace _trace;
|
||||
private ExportableStatus _exportableStatus = new ExportableStatus();
|
||||
|
||||
public void SetServiceProvider(IMultiServiceProvider provider)
|
||||
{
|
||||
ServiceProvider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exportable metadata
|
||||
/// </summary>
|
||||
public IExportableMetadata Metadata
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the dependency manager to provider the dependencies of the class
|
||||
/// </summary>
|
||||
public IMultiServiceProvider ServiceProvider
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public virtual ExportableStatus Status
|
||||
{
|
||||
get { return _exportableStatus; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a service of specific type which has the same metadata as class using the dependency manager.
|
||||
/// If multiple services found, the one with the highest priority will be returned
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the service</typeparam>
|
||||
/// <returns>A service of type T or null if not found</returns>
|
||||
protected T GetService<T>()
|
||||
where T : IExportable
|
||||
{
|
||||
return GetService<T>(Metadata);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a service of specific type which has the same metadata as class using the dependency manager.
|
||||
/// If multiple services found, the one with the highest priority will be returned
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the service</typeparam>
|
||||
/// <returns>A service of type T or null if not found</returns>
|
||||
protected T GetService<T>(IExportableMetadata metadata)
|
||||
where T : IExportable
|
||||
{
|
||||
//Don't try to find the service if it's the same service as current one with same metadata
|
||||
if (ServiceProvider != null && (!(this is T) || metadata != Metadata))
|
||||
{
|
||||
return ServiceProvider.GetService<T>(metadata);
|
||||
}
|
||||
return default(T);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An instance of ITrace which is exported to the extension manager
|
||||
/// </summary>
|
||||
public override ITrace Trace
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_trace = _trace ?? GetService<ITrace>());
|
||||
}
|
||||
set
|
||||
{
|
||||
_trace = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ServerDefinition created from the metadata
|
||||
/// </summary>
|
||||
protected ServerDefinition ServerDefinition
|
||||
{
|
||||
get
|
||||
{
|
||||
return Metadata != null ? new ServerDefinition(Metadata.ServerType, Metadata.Category) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.Extensibility
|
||||
{
|
||||
/// <summary>
|
||||
/// Includes the status of the exportable - whether it failed to load and any error message
|
||||
/// returned during loading
|
||||
/// </summary>
|
||||
public class ExportableStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns true if the loading of the exportable failed
|
||||
/// </summary>
|
||||
public bool LoadingFailed
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An error message if the loading failed
|
||||
/// </summary>
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// An info link to navigate to
|
||||
/// </summary>
|
||||
public string InfoLink { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Microsoft.SqlTools.Extensibility;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.Extensibility
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for exportable and service
|
||||
/// </summary>
|
||||
public static class ExtensionUtils
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Finds a service of specific type which has the same metadata as class using the service provider.
|
||||
/// If multiple services found, the one with the highest priority will be returned
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the service</typeparam>
|
||||
/// <returns>A service of type T or null if not found</returns>
|
||||
public static T GetService<T>(this IMultiServiceProvider provider, IServerDefinition serverDefinition)
|
||||
where T : IExportable
|
||||
{
|
||||
return provider.GetServices<T>()
|
||||
.FilterExportables(serverDefinition)
|
||||
.OrderByDescending(s => SortOrder(s)).
|
||||
FirstOrDefault();
|
||||
}
|
||||
|
||||
private static int SortOrder<T>(T service)
|
||||
{
|
||||
IExportable exportable = service as IExportable;
|
||||
if (exportable != null)
|
||||
{
|
||||
return exportable.Metadata.Priority;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> FilterExportables<T>(this IEnumerable<T> exportables, IServerDefinition serverDefinition = null)
|
||||
where T : IExportable
|
||||
{
|
||||
if (exportables == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
//Get all the possible matches
|
||||
IEnumerable<T> allMatched = serverDefinition != null ?
|
||||
exportables.Where(x => Match(x.Metadata, serverDefinition)).ToList() : exportables;
|
||||
IList<T> list = allMatched.ToList();
|
||||
|
||||
//If specific server type requested and the list has any item with that server type remove the others.
|
||||
//for instance is there's server for all server types and one specifically for sql and give metadata is asking for sql then
|
||||
//we should return the sql one even if the other service has higher priority
|
||||
|
||||
IList<T> withSameServerType = list.Where(x => serverDefinition.HasSameServerName(x.Metadata)).ToList();
|
||||
if (withSameServerType.Any())
|
||||
{
|
||||
list = withSameServerType;
|
||||
}
|
||||
IList<T> withSameCategory = list.Where(x => serverDefinition.HasSameCategory(x.Metadata)).ToList();
|
||||
if (withSameCategory.Any())
|
||||
{
|
||||
list = withSameCategory;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static bool HasSameServerName(this IServerDefinition serverDefinition, IServerDefinition metadata)
|
||||
{
|
||||
if (serverDefinition != null && metadata != null)
|
||||
{
|
||||
// Note: this does not handle null <-> string.Empty equivalence. For now ignoring this as it should not matter
|
||||
return string.Equals(serverDefinition.ServerType, metadata.ServerType, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool HasSameCategory(this IServerDefinition serverDefinition, IServerDefinition metadata)
|
||||
{
|
||||
if (serverDefinition != null && metadata != null)
|
||||
{
|
||||
// Note: this does not handle null <-> string.Empty equivalence. For now ignoring this as it should not matter
|
||||
return string.Equals(serverDefinition.Category, metadata.Category, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given server definition is secure type. (i.e. Azure)
|
||||
/// </summary>
|
||||
internal static bool IsSecure(this IServerDefinition serverDefinition)
|
||||
{
|
||||
if (serverDefinition != null && serverDefinition.Category != null)
|
||||
{
|
||||
return serverDefinition.Category.Equals(Categories.Azure, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static string GetServerDefinitionKey(this IServerDefinition serverDefinition)
|
||||
{
|
||||
string key = string.Empty;
|
||||
if (serverDefinition != null)
|
||||
{
|
||||
key = string.Format(CultureInfo.InvariantCulture, "{0}", GetKey(serverDefinition.Category));
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
internal static bool EqualsServerDefinition(this IServerDefinition serverDefinition, IServerDefinition otherServerDefinition)
|
||||
{
|
||||
if (serverDefinition == null && otherServerDefinition == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (serverDefinition != null && otherServerDefinition != null)
|
||||
{
|
||||
return (((string.IsNullOrEmpty(serverDefinition.Category) && string.IsNullOrEmpty(otherServerDefinition.Category)) || serverDefinition.HasSameCategory(otherServerDefinition)) &&
|
||||
((string.IsNullOrEmpty(serverDefinition.ServerType) && string.IsNullOrEmpty(otherServerDefinition.ServerType)) || serverDefinition.HasSameServerName(otherServerDefinition)));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool EmptyOrEqual(this string value1, string value2)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value1) && string.IsNullOrEmpty(value2))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return value1 == value2;
|
||||
}
|
||||
|
||||
private static string GetKey(string name)
|
||||
{
|
||||
return string.IsNullOrEmpty(name) ? string.Empty : name.ToUpperInvariant();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the metadata matches the given server definition
|
||||
/// </summary>
|
||||
public static bool Match(this IServerDefinition first, IServerDefinition other)
|
||||
{
|
||||
if (first == null)
|
||||
{
|
||||
// TODO should we handle this differently?
|
||||
return false;
|
||||
}
|
||||
if (other == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return MatchMetaData(first.ServerType, other.ServerType)
|
||||
&& MatchMetaData(first.Category, other.Category);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the metadata value matches the given value
|
||||
/// </summary>
|
||||
private static bool MatchMetaData(string metaData, string requestedMetaData)
|
||||
{
|
||||
if (string.IsNullOrEmpty(metaData) || string.IsNullOrEmpty(requestedMetaData))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return (metaData.Equals(requestedMetaData, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.SqlTools.Extensibility;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.Extensibility
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface to be implemented by any class that needs to be exportable
|
||||
/// </summary>
|
||||
public interface IExportable : IComposableService
|
||||
{
|
||||
/// <summary>
|
||||
/// The metadata assigned to the exportable
|
||||
/// </summary>
|
||||
IExportableMetadata Metadata
|
||||
{
|
||||
set; get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the status of the exportable
|
||||
/// </summary>
|
||||
ExportableStatus Status
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.SqlTools.Extensibility;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.Extensibility
|
||||
{
|
||||
/// <summary>
|
||||
/// The metadata describing an extension
|
||||
/// </summary>
|
||||
public interface IExportableMetadata : IStandardMetadata, IServerDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// Exportable priority tobe used when multiple of same type found
|
||||
/// </summary>
|
||||
int Priority
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
|
||||
public class ExportableMetadata : IExportableMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// Default constructor to initialize the metadata of the exportable
|
||||
/// </summary>
|
||||
/// <param name="serverType">The server type supported by the exportable. If not set, exportable supports all server types</param>
|
||||
/// <param name="category">The category supported by the exportable. If not set, exportable supports all categories </param>
|
||||
/// <param name="id">The unique id of the exportable. Used by the extension manager to pick only one from exportables with same id in the same assembly</param>
|
||||
/// <param name="priority">The priority of the exportable. The extension manager will pick the exportable with the highest priority if multiple found</param>
|
||||
/// <param name="displayName">The display name of the exportable. This field is optional</param>
|
||||
public ExportableMetadata(
|
||||
string serverType,
|
||||
string category,
|
||||
string id,
|
||||
int priority = 0,
|
||||
string displayName = null)
|
||||
{
|
||||
Category = category;
|
||||
ServerType = serverType;
|
||||
Id = id;
|
||||
DisplayName = displayName;
|
||||
Priority = priority;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The constructor to define an exportable by type, id and priority only. To be used by the exportables that support all server types and categories.
|
||||
/// For example: the implementation of <see cref="ITrace" /> can be used for all server types and categories.
|
||||
/// </summary>
|
||||
/// <param name="id">The unique id of the exportable. Used by the extension manager to pick only one from exportables with same id in the same assembly</param>
|
||||
/// <param name="priority">The priority of the exportable. The extension manager will pick the exportable with the highest priority if multiple found</param>
|
||||
/// <param name="displayName">The display name of the exportable. This field is optional</param>
|
||||
public ExportableMetadata(string id, int priority = 0,
|
||||
string displayName = null) :
|
||||
this(string.Empty, string.Empty, id, priority, displayName)
|
||||
{
|
||||
}
|
||||
|
||||
public int Priority
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string Version
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string Id
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string DisplayName
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string Category
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string ServerType
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.Extensibility
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides facility to trace code execution through calls to Trace* methods.
|
||||
/// Implementing classes must add a <see cref="ExportableAttribute" />
|
||||
/// to the class in order to be found by the extension manager
|
||||
/// </summary>
|
||||
public interface ITrace : IExportable
|
||||
{
|
||||
/// <summary>
|
||||
/// Write a formatted trace event message to the underlying trace source.
|
||||
/// </summary>
|
||||
/// <param name="eventType">Event type that specifies the verbosity level of the trace.</param>
|
||||
/// <param name="traceId">The category of the caller's product feature.</param>
|
||||
/// <param name="message">Format string of the message to be traced along with the event.</param>
|
||||
/// <param name="args">Object array containing zero or more objects to format.</param>
|
||||
/// <returns>True if event was successfully written</returns>
|
||||
bool TraceEvent(TraceEventType eventType, int traceId, string message, params object[] args);
|
||||
|
||||
/// <summary>
|
||||
/// Write a trace event with a message and exception details to the underlying trace source.
|
||||
/// </summary>
|
||||
/// <param name="eventType">Event type that specifies the verbosity level of the trace.</param>
|
||||
/// <param name="traceId">The category of the caller's product feature.</param>
|
||||
/// <param name="exception">The exception to be logged.</param>
|
||||
/// <param name="message">String message to be traced along with the event.</param>
|
||||
/// <param name="lineNumber">Compile time property to trace the line number of the calling code. Used to trace location error occurred</param>
|
||||
/// <param name="fileName">Compile time property to trace the fileName of the calling code. Used to trace location error occurred</param>
|
||||
/// <param name="memberName">Compile time property to trace the name of the calling method. Used to trace location error occurred</param>
|
||||
/// <returns>True if event was successfully written</returns>
|
||||
bool TraceException(TraceEventType eventType, int traceId, Exception exception, string message,
|
||||
[CallerLineNumber] int lineNumber = 0, [CallerFilePath] string fileName = "",
|
||||
[CallerMemberName] string memberName = "");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.Extensibility
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration of values used as trace event identifiers that semantically represent the major categories of product features.
|
||||
/// </summary>
|
||||
public enum TraceId : int
|
||||
{
|
||||
/// <summary>
|
||||
/// Trace Id for Azure Authentication traces
|
||||
/// </summary>
|
||||
AzureAuthentication = 0,
|
||||
/// <summary>
|
||||
/// Trace Id for azure resource traces
|
||||
/// </summary>
|
||||
AzureResource = 1,
|
||||
/// <summary>
|
||||
/// Trace Id for UI sections traces
|
||||
/// </summary>
|
||||
Sections = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Trace Id for connection traces
|
||||
/// </summary>
|
||||
Connection = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Trace Id for firewall rule traces
|
||||
/// </summary>
|
||||
FirewallRule = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Trace Id for Azure browse traces
|
||||
/// </summary>
|
||||
AzureSection = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Trace Id for network servers
|
||||
/// </summary>
|
||||
NetworkServers = 6,
|
||||
|
||||
/// <summary>
|
||||
/// Trace Id for local servers
|
||||
/// </summary>
|
||||
LocalServers = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Trace Id for sql database discovery
|
||||
/// </summary>
|
||||
SqlDatabase = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Trace Id for local browse traces
|
||||
/// </summary>
|
||||
LocalSection = 9,
|
||||
|
||||
/// <summary>
|
||||
/// Trace Id for account picker traces
|
||||
/// </summary>
|
||||
AccountPicker = 10,
|
||||
|
||||
/// <summary>
|
||||
/// Trace Id for network browse traces
|
||||
/// </summary>
|
||||
NetworkSection = 11,
|
||||
|
||||
/// <summary>
|
||||
/// Trace Id for main dialog traces
|
||||
/// </summary>
|
||||
UiInfra = 12,
|
||||
|
||||
/// <summary>
|
||||
/// Trace Id for hostory page traces
|
||||
/// </summary>
|
||||
HistoryPage = 13,
|
||||
|
||||
/// <summary>
|
||||
/// TraceId for Telemetry
|
||||
/// </summary>
|
||||
Telemetry = 14,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using Microsoft.SqlTools.Extensibility;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.Extensibility
|
||||
{
|
||||
/// <summary>
|
||||
/// Enables tracing feature for classes
|
||||
/// </summary>
|
||||
internal class Traceable : TraceableBase, IComposableService
|
||||
{
|
||||
private IMultiServiceProvider _serviceProvider;
|
||||
private ITrace _trace;
|
||||
|
||||
public Traceable()
|
||||
{
|
||||
}
|
||||
|
||||
public Traceable(ITrace trace)
|
||||
{
|
||||
_trace = trace;
|
||||
}
|
||||
|
||||
public override ITrace Trace
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_trace == null)
|
||||
{
|
||||
if (_serviceProvider != null)
|
||||
{
|
||||
_trace = _serviceProvider.GetService<ITrace>();
|
||||
}
|
||||
}
|
||||
return _trace;
|
||||
}
|
||||
set
|
||||
{
|
||||
_trace = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetServiceProvider(IMultiServiceProvider provider)
|
||||
{
|
||||
_serviceProvider = provider;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.Extensibility
|
||||
{
|
||||
/// <summary>
|
||||
/// An abstract class to be used for classes that need to have trace feature
|
||||
/// </summary>
|
||||
public abstract class TraceableBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the first implementation of trace in the catalog that has highest priority
|
||||
/// </summary>
|
||||
public abstract ITrace Trace
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a trace event message to the underlying trace source.
|
||||
/// </summary>
|
||||
public bool TraceEvent(TraceEventType eventType, TraceId traceId, string format, params object[] args)
|
||||
{
|
||||
return TraceEvent(eventType, (int)traceId, format, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a trace event message to the underlying trace source.
|
||||
/// </summary>
|
||||
public bool TraceEvent(TraceEventType eventType, int traceId, string format, params object[] args)
|
||||
{
|
||||
return SafeTrace(eventType, traceId, format, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a formatted trace event message to the underlying trace source and issue a Debug.Fail() call
|
||||
/// if condition is false.
|
||||
/// </summary>
|
||||
public bool AssertTraceEvent(bool condition, TraceEventType eventType, TraceId traceId, string message)
|
||||
{
|
||||
return AssertTraceEvent(condition, eventType, (int)traceId, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a formatted trace event message to the underlying trace source and issue a Debug.Fail() call
|
||||
/// if condition is false.
|
||||
/// </summary>
|
||||
public bool AssertTraceEvent(bool condition, TraceEventType eventType, int traceId, string message)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
return DebugTraceEvent(eventType, traceId, message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a trace event with a message and exception details to the underlying trace source and issue a
|
||||
/// Debug.Fail() call if the condition is false.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note: often the fact that an exception has been thrown by itself is enough to determine that the message
|
||||
/// should be logged. If so please use DebugTraceException() instead. This method is for if the exception should
|
||||
/// only be logged if some additional condition is also false.
|
||||
/// </remarks>
|
||||
/// <param name="condition">Must be false for Debug or Trace event to be issued.</param>
|
||||
/// <param name="eventType">Event type that specifies the verbosity level of the trace.</param>
|
||||
/// <param name="traceId">The category of the caller's product feature.</param>
|
||||
/// <param name="exception">The exception to be logged.</param>
|
||||
/// <param name="message">Message to be traced along with the event.</param>
|
||||
/// <returns>True if event was successfully written or the condition was true</returns>
|
||||
public bool AssertTraceException(bool condition, TraceEventType eventType, TraceId traceId, Exception exception, string message)
|
||||
{
|
||||
return AssertTraceException(condition, eventType, (int) traceId, exception, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a trace event with a message and exception details to the underlying trace source and issue a
|
||||
/// Debug.Fail() call if the condition is false.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note: often the fact that an exception has been thrown by itself is enough to determine that the message
|
||||
/// should be logged. If so please use DebugTraceException() instead. This method is for if the exception should
|
||||
/// only be logged if some additional condition is also false.
|
||||
/// </remarks>
|
||||
/// <param name="condition">Must be false for Debug or Trace event to be issued.</param>
|
||||
/// <param name="eventType">Event type that specifies the verbosity level of the trace.</param>
|
||||
/// <param name="traceId">The category of the caller's product feature.</param>
|
||||
/// <param name="exception">The exception to be logged.</param>
|
||||
/// <param name="message">Message to be traced along with the event.</param>
|
||||
/// <returns>True if event was successfully written or the condition was true</returns>
|
||||
public bool AssertTraceException2(bool condition, TraceEventType eventType, TraceId traceId, Exception exception, string message,
|
||||
[CallerLineNumber] int lineNumber = 0, [CallerFilePath] string fileName = "",
|
||||
[CallerMemberName] string memberName = "")
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
return DebugTraceException2(eventType, (int) traceId, exception, message, lineNumber, fileName, memberName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a trace event with a message and exception details to the underlying trace source and issue a
|
||||
/// Debug.Fail() call if the condition is false.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note: often the fact that an exception has been thrown by itself is enough to determine that the message
|
||||
/// should be logged. If so please use DebugTraceException() instead. This method is for if the exception should
|
||||
/// only be logged if some additional condition is also false.
|
||||
/// </remarks>
|
||||
/// <param name="condition">Must be false for Debug or Trace event to be issued.</param>
|
||||
/// <param name="eventType">Event type that specifies the verbosity level of the trace.</param>
|
||||
/// <param name="traceId">The category of the caller's product feature.</param>
|
||||
/// <param name="exception">The exception to be logged.</param>
|
||||
/// <param name="message">Message to be traced along with the event.</param>
|
||||
/// <returns>True if event was successfully written or the condition was true</returns>
|
||||
public bool AssertTraceException(bool condition, TraceEventType eventType, int traceId, Exception exception, string message)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
return DebugTraceException(eventType, traceId, exception, message);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a trace event with a message and exception details to the underlying trace source and issue a
|
||||
/// Debug.Fail() call with the same message.
|
||||
/// </summary>
|
||||
/// <param name="eventType">Event type that specifies the verbosity level of the trace.</param>
|
||||
/// <param name="traceId">The category of the caller's product feature.</param>
|
||||
/// <param name="exception">The exception to be logged.</param>
|
||||
/// <param name="message">Message to be traced along with the event.</param>
|
||||
/// <returns>True if event was successfully written</returns>
|
||||
public bool DebugTraceException(TraceEventType eventType, int traceId, Exception exception, string message)
|
||||
{
|
||||
// Avoiding breaking change by not overloading this method. Passing default values to TraceException so this isn't
|
||||
// reported as the callsite for majority of exceptions
|
||||
bool success = TraceException(eventType, traceId, exception, message, 0, string.Empty, string.Empty);
|
||||
Debug.Fail(message);
|
||||
return success;
|
||||
}
|
||||
|
||||
public bool DebugTraceException2(TraceEventType eventType, int traceId, Exception exception, string message,
|
||||
[CallerLineNumber] int lineNumber = 0, [CallerFilePath] string fileName = "",
|
||||
[CallerMemberName] string memberName = "")
|
||||
{
|
||||
bool success = TraceException(eventType, traceId, exception, message, lineNumber, fileName, memberName);
|
||||
Debug.Fail(message);
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a trace event with a message and exception details to the underlying trace source.
|
||||
/// </summary>
|
||||
public bool TraceException(TraceEventType eventType, TraceId traceId, Exception exception, string message,
|
||||
[CallerLineNumber] int lineNumber = 0, [CallerFilePath] string fileName = "",
|
||||
[CallerMemberName] string memberName = "")
|
||||
{
|
||||
return TraceException(eventType, (int)traceId, exception, message, lineNumber, fileName, memberName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a trace event with a message and exception details to the underlying trace source.
|
||||
/// </summary>
|
||||
public bool TraceException(TraceEventType eventType, int traceId, Exception exception, string message,
|
||||
[CallerLineNumber] int lineNumber = 0, [CallerFilePath] string fileName = "",
|
||||
[CallerMemberName] string memberName = "")
|
||||
{
|
||||
return SafeTraceException(eventType, traceId, exception, message, lineNumber, fileName, memberName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a formatted trace event message to the underlying trace source and issue a Debug.Fail() with
|
||||
/// the same message.
|
||||
/// </summary>
|
||||
/// <param name="eventType">Event type that specifies the verbosity level of the trace.</param>
|
||||
/// <param name="traceId">The category of the caller's product feature.</param>
|
||||
/// <param name="message">Message to be output via Debug.Fail() and traced along with the event.</param>
|
||||
/// <returns>True if event was successfully written</returns>
|
||||
public bool DebugTraceEvent(TraceEventType eventType, int traceId, string message)
|
||||
{
|
||||
bool success = TraceEvent(eventType, traceId, message);
|
||||
Debug.Fail(message);
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies ITrace instance is not null before tracing
|
||||
/// </summary>
|
||||
private bool SafeTrace(TraceEventType eventType, int traceId, string format, params object[] args)
|
||||
{
|
||||
if (Trace != null)
|
||||
{
|
||||
return Trace.TraceEvent(eventType, traceId, format, args);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies ITrace instance is not null before tracing the exception
|
||||
/// </summary>
|
||||
private bool SafeTraceException(TraceEventType eventType, int traceId, Exception exception, string message,
|
||||
[CallerLineNumber] int lineNumber = 0, [CallerFilePath] string fileName = "",
|
||||
[CallerMemberName] string memberName = "")
|
||||
{
|
||||
if (Trace != null)
|
||||
{
|
||||
return Trace.TraceException(eventType, traceId, exception, message, lineNumber, fileName, memberName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Data.SqlClient;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.FirewallRule
|
||||
{
|
||||
internal interface IFirewallErrorParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses given error message and error code to see if it's firewall rule error
|
||||
/// and finds the blocked ip address
|
||||
/// </summary>
|
||||
FirewallParserResponse ParseErrorMessage(string errorMessage, int errorCode);
|
||||
|
||||
/// <summary>
|
||||
/// Parses given error message and error code to see if it's firewall rule error
|
||||
/// and finds the blocked ip address
|
||||
/// </summary>
|
||||
FirewallParserResponse ParseException(SqlException sqlException);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses an error to check for firewall rule error. Will include the blocked ip address if firewall rule error is detected
|
||||
/// </summary>
|
||||
public class FirewallErrorParser : IFirewallErrorParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses given error message and error code to see if it's firewall rule error
|
||||
/// and finds the blocked ip address
|
||||
/// </summary>
|
||||
public FirewallParserResponse ParseException(SqlException sqlException)
|
||||
{
|
||||
CommonUtil.CheckForNull(sqlException, "sqlException");
|
||||
return ParseErrorMessage(sqlException.Message, sqlException.Number);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses given error message and error code to see if it's firewall rule error
|
||||
/// and finds the blocked ip address
|
||||
/// </summary>
|
||||
public FirewallParserResponse ParseErrorMessage(string errorMessage, int errorCode)
|
||||
{
|
||||
CommonUtil.CheckForNull(errorMessage, "errorMessage");
|
||||
|
||||
FirewallParserResponse response = new FirewallParserResponse();
|
||||
if (IsSqlAzureFirewallBlocked(errorCode))
|
||||
{
|
||||
// Connection failed due to blocked client IP
|
||||
IPAddress clientIp;
|
||||
if (TryParseClientIp(errorMessage, out clientIp))
|
||||
{
|
||||
response = new FirewallParserResponse(true, clientIp);
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the given message to find the blocked ip address
|
||||
/// </summary>
|
||||
private static bool TryParseClientIp(string message, out IPAddress clientIp)
|
||||
{
|
||||
clientIp = null;
|
||||
try
|
||||
{
|
||||
Regex regex =
|
||||
new Regex(
|
||||
@"\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b",
|
||||
RegexOptions.IgnoreCase);
|
||||
Match match = regex.Match(message);
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
string clientIpValue = match.Value;
|
||||
return IPAddress.TryParse(clientIpValue, out clientIp);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//TODO: trace?
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if given error code is firewall rule blocked error code
|
||||
/// </summary>
|
||||
private bool IsSqlAzureFirewallBlocked(int errorCode)
|
||||
{
|
||||
return errorCode == SqlAzureFirewallBlockedErrorNumber;
|
||||
}
|
||||
|
||||
private const int SqlAzureFirewallBlockedErrorNumber = 40615; // http://msdn.microsoft.com/en-us/library/windowsazure/ff394106.aspx
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Net;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.FirewallRule
|
||||
{
|
||||
/// <summary>
|
||||
/// The response that's created by firewall rule parser
|
||||
/// </summary>
|
||||
public class FirewallParserResponse
|
||||
{
|
||||
public FirewallParserResponse(bool firewallRuleErrorDetected, IPAddress blockedIpAddress)
|
||||
{
|
||||
FirewallRuleErrorDetected = firewallRuleErrorDetected;
|
||||
BlockedIpAddress = blockedIpAddress;
|
||||
}
|
||||
|
||||
public FirewallParserResponse()
|
||||
{
|
||||
FirewallRuleErrorDetected = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if firewall rule is detected, otherwise returns false.
|
||||
/// </summary>
|
||||
public bool FirewallRuleErrorDetected
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the blocked ip address if firewall rule is detected
|
||||
/// </summary>
|
||||
public IPAddress BlockedIpAddress
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.FirewallRule
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception used by firewall service to indicate when firewall rule operation fails
|
||||
/// </summary>
|
||||
public class FirewallRuleException : ServiceExceptionBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AuthenticationFailedException class.
|
||||
/// </summary>
|
||||
public FirewallRuleException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AuthenticationFailedException class with a specified error message.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception. </param>
|
||||
public FirewallRuleException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AuthenticationFailedException class with a specified error message.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception. </param>
|
||||
/// <param name="httpStatusCode">The Http error code. </param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference
|
||||
/// (Nothing in Visual Basic) if no inner exception is specified</param>
|
||||
public FirewallRuleException(string message, HttpStatusCode httpStatusCode, Exception innerException = null)
|
||||
: base(message, httpStatusCode, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AuthenticationFailedException class with a specified error message.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception. </param>
|
||||
/// <param name="httpStatusCode">The Http error code. </param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference
|
||||
/// (Nothing in Visual Basic) if no inner exception is specified</param>
|
||||
public FirewallRuleException(string message, int httpStatusCode, Exception innerException = null)
|
||||
: base(message, httpStatusCode, innerException)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AuthenticationFailedException class with a specified error message
|
||||
/// and a reference to the inner exception that is the cause of this exception.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception. </param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference
|
||||
/// (Nothing in Visual Basic) if no inner exception is specified</param>
|
||||
public FirewallRuleException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AuthenticationFailedException class with serialized data.
|
||||
/// </summary>
|
||||
/// <param name="info">The SerializationInfo that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param>
|
||||
public FirewallRuleException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.FirewallRule
|
||||
{
|
||||
/// <summary>
|
||||
/// Includes all the information needed to create a firewall rule
|
||||
/// </summary>
|
||||
public class FirewallRuleRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Start IP address
|
||||
/// </summary>
|
||||
public IPAddress StartIpAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// End IP address
|
||||
/// </summary>
|
||||
public IPAddress EndIpAddress { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Firewall rule name
|
||||
/// </summary>
|
||||
public string FirewallRuleName
|
||||
{
|
||||
get
|
||||
{
|
||||
DateTime now = DateTime.UtcNow;
|
||||
|
||||
return string.Format(CultureInfo.InvariantCulture, "ClientIPAddress_{0}",
|
||||
now.ToString("yyyy-MM-dd_hh:mm:ss", CultureInfo.CurrentCulture));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Authentication;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.FirewallRule
|
||||
{
|
||||
/// <summary>
|
||||
/// Includes azure resource and subscription needed to create firewall rule
|
||||
/// </summary>
|
||||
public class FirewallRuleResource
|
||||
{
|
||||
/// <summary>
|
||||
/// Azure resource
|
||||
/// </summary>
|
||||
public IAzureSqlServerResource AzureResource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Azure Subscription
|
||||
/// </summary>
|
||||
public IAzureUserAccountSubscriptionContext SubscriptionContext { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the resource and subscription are not null
|
||||
/// </summary>
|
||||
public bool IsValid
|
||||
{
|
||||
get
|
||||
{
|
||||
return AzureResource != null && SubscriptionContext != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.FirewallRule
|
||||
{
|
||||
/// <summary>
|
||||
/// The response that's created when the firewall rule creation request is complete
|
||||
/// </summary>
|
||||
public class FirewallRuleResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// End IP address
|
||||
/// </summary>
|
||||
public string EndIpAddress
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start IP address
|
||||
/// </summary>
|
||||
public string StartIpAddress
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the firewall rule created successfully or not
|
||||
/// </summary>
|
||||
public bool Created { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Authentication;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core.FirewallRule
|
||||
{
|
||||
|
||||
public interface IFirewallRuleService
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates firewall rule for given server name and IP address range. Throws exception if operation fails
|
||||
/// </summary>
|
||||
Task<FirewallRuleResponse> CreateFirewallRuleAsync(string serverName, string startIpAddressValue, string endIpAddressValue);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates firewall rule for given server name and IP address range. Throws exception if operation fails
|
||||
/// </summary>
|
||||
Task<FirewallRuleResponse> CreateFirewallRuleAsync(string serverName, IPAddress startIpAddress, IPAddress endIpAddress);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets and gets azure resource manager instance. can be used by unit tests
|
||||
/// </summary>
|
||||
IAzureResourceManager ResourceManager
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets and gets authentication manager instance. can be used by unit tests
|
||||
/// </summary>
|
||||
IAzureAuthenticationManager AuthenticationManager
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Service to be used by the controls to create firewall rule
|
||||
/// </summary>
|
||||
public class FirewallRuleService : IFirewallRuleService
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates firewall rule for given server name and IP address range. Throws exception if operation fails
|
||||
/// </summary>
|
||||
public async Task<FirewallRuleResponse> CreateFirewallRuleAsync(string serverName, string startIpAddressValue, string endIpAddressValue)
|
||||
{
|
||||
IPAddress startIpAddress = ConvertToIpAddress(startIpAddressValue);
|
||||
IPAddress endIpAddress = ConvertToIpAddress(endIpAddressValue);
|
||||
return await CreateFirewallRuleAsync(serverName, startIpAddress, endIpAddress);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates firewall rule for given server name and IP address range. Throws exception if operation fails
|
||||
/// </summary>
|
||||
public async Task<FirewallRuleResponse> CreateFirewallRuleAsync(string serverName, IPAddress startIpAddress, IPAddress endIpAddress)
|
||||
{
|
||||
try
|
||||
{
|
||||
FirewallRuleResponse firewallRuleResponse = new FirewallRuleResponse() { Created = false };
|
||||
CommonUtil.CheckStringForNullOrEmpty(serverName, "serverName");
|
||||
CommonUtil.CheckForNull(startIpAddress, "startIpAddress");
|
||||
CommonUtil.CheckForNull(endIpAddress, "endIpAddress");
|
||||
|
||||
IAzureAuthenticationManager authenticationManager = AuthenticationManager;
|
||||
|
||||
if (authenticationManager != null && !await authenticationManager.GetUserNeedsReauthenticationAsync())
|
||||
{
|
||||
FirewallRuleResource firewallRuleResource = await FindAzureResourceAsync(serverName);
|
||||
firewallRuleResponse = await CreateFirewallRule(firewallRuleResource, startIpAddress, endIpAddress);
|
||||
}
|
||||
if (firewallRuleResponse == null || !firewallRuleResponse.Created)
|
||||
{
|
||||
throw new FirewallRuleException(SR.FirewallRuleCreationFailed);
|
||||
}
|
||||
return firewallRuleResponse;
|
||||
}
|
||||
catch (ServiceExceptionBase)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new FirewallRuleException(SR.FirewallRuleCreationFailed, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets and gets azure resource manager instance. can be used by unit tests
|
||||
/// </summary>
|
||||
public IAzureResourceManager ResourceManager { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets and gets authentication manager instance. can be used by unit tests
|
||||
/// </summary>
|
||||
public IAzureAuthenticationManager AuthenticationManager { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates firewall rule for given subscription and IP address range
|
||||
/// </summary>
|
||||
private async Task<FirewallRuleResponse> CreateFirewallRule(FirewallRuleResource firewallRuleResource, IPAddress startIpAddress, IPAddress endIpAddress)
|
||||
{
|
||||
CommonUtil.CheckForNull(firewallRuleResource, "firewallRuleResource");
|
||||
|
||||
try
|
||||
{
|
||||
if (firewallRuleResource.IsValid)
|
||||
{
|
||||
|
||||
FirewallRuleRequest request = new FirewallRuleRequest()
|
||||
{
|
||||
StartIpAddress = startIpAddress,
|
||||
EndIpAddress = endIpAddress
|
||||
};
|
||||
using (IAzureResourceManagementSession session = await ResourceManager.CreateSessionAsync(firewallRuleResource.SubscriptionContext))
|
||||
{
|
||||
return await ResourceManager.CreateFirewallRuleAsync(
|
||||
session,
|
||||
firewallRuleResource.AzureResource,
|
||||
request);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ServiceExceptionBase)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new FirewallRuleException(SR.FirewallRuleCreationFailed, ex);
|
||||
}
|
||||
|
||||
return new FirewallRuleResponse()
|
||||
{
|
||||
Created = false
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds an azure resource for given server name under user's subscriptions
|
||||
/// </summary>
|
||||
private async Task<FirewallRuleResource> FindAzureResourceAsync(string serverName)
|
||||
{
|
||||
try
|
||||
{
|
||||
IEnumerable<IAzureUserAccountSubscriptionContext> subscriptions = await AuthenticationManager.GetSubscriptionsAsync();
|
||||
|
||||
if (subscriptions == null)
|
||||
{
|
||||
throw new FirewallRuleException(SR.FirewallRuleCreationFailed);
|
||||
}
|
||||
|
||||
foreach (IAzureUserAccountSubscriptionContext subscription in subscriptions)
|
||||
{
|
||||
using (IAzureResourceManagementSession session = await ResourceManager.CreateSessionAsync(subscription))
|
||||
{
|
||||
IAzureSqlServerResource azureSqlServer = await FindAzureResourceForSubscriptionAsync(serverName, session);
|
||||
|
||||
if (azureSqlServer != null)
|
||||
{
|
||||
return new FirewallRuleResource()
|
||||
{
|
||||
SubscriptionContext = subscription,
|
||||
AzureResource = azureSqlServer
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
var currentUser = await AuthenticationManager.GetCurrentAccountAsync();
|
||||
|
||||
throw new FirewallRuleException(string.Format(CultureInfo.CurrentCulture, SR.AzureServerNotFound, serverName, currentUser != null ? currentUser.UniqueId : string.Empty));
|
||||
}
|
||||
catch (ServiceExceptionBase)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new FirewallRuleException(SR.FirewallRuleCreationFailed, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws a firewallRule exception based on give status code
|
||||
/// </summary>
|
||||
private void HandleError(ServiceExceptionBase exception, string serverName,
|
||||
IAzureUserAccountSubscriptionContext subscription)
|
||||
{
|
||||
var accountName = subscription != null && subscription.UserAccount != null &&
|
||||
subscription.UserAccount.DisplayInfo != null
|
||||
? subscription.UserAccount.DisplayInfo.UserName
|
||||
: string.Empty;
|
||||
|
||||
switch (exception.HttpStatusCode)
|
||||
{
|
||||
case HttpStatusCode.NotFound:
|
||||
throw new FirewallRuleException(string.Format(CultureInfo.CurrentCulture, SR.AzureServerNotFound, serverName, accountName), exception);
|
||||
case HttpStatusCode.Forbidden:
|
||||
throw new FirewallRuleException(string.Format(CultureInfo.CurrentCulture, SR.FirewallRuleAccessForbidden, accountName), exception);
|
||||
default:
|
||||
throw new FirewallRuleException(SR.FirewallRuleCreationFailed, exception.HttpStatusCode, exception);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds Azure resource for the given subscription and server name
|
||||
/// </summary>
|
||||
private async Task<IAzureSqlServerResource> FindAzureResourceForSubscriptionAsync(
|
||||
string serverName,
|
||||
IAzureResourceManagementSession session)
|
||||
{
|
||||
try
|
||||
{
|
||||
IEnumerable<IAzureSqlServerResource> resources = await ResourceManager.GetSqlServerAzureResourcesAsync(session);
|
||||
|
||||
if (resources == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
foreach (IAzureSqlServerResource resource in resources)
|
||||
{
|
||||
if (serverName.Equals(resource.FullyQualifiedDomainName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ServiceExceptionBase ex)
|
||||
{
|
||||
HandleError(ex, serverName, session.SubscriptionContext);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new FirewallRuleException(SR.FirewallRuleCreationFailed, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private IPAddress ConvertToIpAddress(string ipAddressValue)
|
||||
{
|
||||
IPAddress ipAddress;
|
||||
if (!IPAddress.TryParse(ipAddressValue, out ipAddress))
|
||||
{
|
||||
throw new FirewallRuleException(SR.InvalidIpAddress);
|
||||
}
|
||||
return ipAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
150
src/Microsoft.SqlTools.ResourceProvider.Core/Localization/sr.cs
Normal file
150
src/Microsoft.SqlTools.ResourceProvider.Core/Localization/sr.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
// WARNING:
|
||||
// This file was generated by the Microsoft DataWarehouse String Resource Tool 1.37.0.0
|
||||
// from information in sr.strings
|
||||
// DO NOT MODIFY THIS FILE'S CONTENTS, THEY WILL BE OVERWRITTEN
|
||||
//
|
||||
namespace Microsoft.SqlTools.ResourceProvider.Core
|
||||
{
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Globalization;
|
||||
|
||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class SR
|
||||
{
|
||||
protected SR()
|
||||
{ }
|
||||
|
||||
public static CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.Culture;
|
||||
}
|
||||
set
|
||||
{
|
||||
Keys.Culture = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static string AzureServerNotFound
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.AzureServerNotFound);
|
||||
}
|
||||
}
|
||||
|
||||
public static string AzureSubscriptionFailedErrorMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.AzureSubscriptionFailedErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public static string DatabaseDiscoveryFailedErrorMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.DatabaseDiscoveryFailedErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public static string FirewallRuleAccessForbidden
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.FirewallRuleAccessForbidden);
|
||||
}
|
||||
}
|
||||
|
||||
public static string FirewallRuleCreationFailed
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.FirewallRuleCreationFailed);
|
||||
}
|
||||
}
|
||||
|
||||
public static string InvalidIpAddress
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.InvalidIpAddress);
|
||||
}
|
||||
}
|
||||
|
||||
public static string InvalidServerTypeErrorMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.InvalidServerTypeErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public static string LoadingExportableFailedGeneralErrorMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.LoadingExportableFailedGeneralErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class Keys
|
||||
{
|
||||
static ResourceManager resourceManager = new ResourceManager("Microsoft.SqlTools.ResourceProvider.Core.Localization.SR", typeof(SR).GetTypeInfo().Assembly);
|
||||
|
||||
static CultureInfo _culture = null;
|
||||
|
||||
|
||||
public const string AzureServerNotFound = "AzureServerNotFound";
|
||||
|
||||
|
||||
public const string AzureSubscriptionFailedErrorMessage = "AzureSubscriptionFailedErrorMessage";
|
||||
|
||||
|
||||
public const string DatabaseDiscoveryFailedErrorMessage = "DatabaseDiscoveryFailedErrorMessage";
|
||||
|
||||
|
||||
public const string FirewallRuleAccessForbidden = "FirewallRuleAccessForbidden";
|
||||
|
||||
|
||||
public const string FirewallRuleCreationFailed = "FirewallRuleCreationFailed";
|
||||
|
||||
|
||||
public const string InvalidIpAddress = "InvalidIpAddress";
|
||||
|
||||
|
||||
public const string InvalidServerTypeErrorMessage = "InvalidServerTypeErrorMessage";
|
||||
|
||||
|
||||
public const string LoadingExportableFailedGeneralErrorMessage = "LoadingExportableFailedGeneralErrorMessage";
|
||||
|
||||
|
||||
private Keys()
|
||||
{ }
|
||||
|
||||
public static CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return _culture;
|
||||
}
|
||||
set
|
||||
{
|
||||
_culture = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetString(string key)
|
||||
{
|
||||
return resourceManager.GetString(key, _culture);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype=">text/microsoft-resx</resheader>
|
||||
<resheader name="version=">2.0</resheader>
|
||||
<resheader name="reader=">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer=">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1="><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing=">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64=">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64=">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata=">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true=">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded=">
|
||||
<xsd:element name="metadata=">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly=">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data=">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader=">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="AzureServerNotFound" xml:space="preserve">
|
||||
<value>The server you specified {0} does not exist in any subscription in {1}. Either you have signed in with an incorrect account or your server was removed from subscription(s) in this account. Please check your account and try again.</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="AzureSubscriptionFailedErrorMessage" xml:space="preserve">
|
||||
<value>An error occurred while getting Azure subscriptions</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="DatabaseDiscoveryFailedErrorMessage" xml:space="preserve">
|
||||
<value>An error occurred while getting databases from servers of type {0} from {1}</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="FirewallRuleAccessForbidden" xml:space="preserve">
|
||||
<value>{0} does not have permission to change the server firewall rule. Try again with a different account that is an Owner or Contributor of the Azure subscription or the server.</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="FirewallRuleCreationFailed" xml:space="preserve">
|
||||
<value>An error occurred while creating a new firewall rule.</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="InvalidIpAddress" xml:space="preserve">
|
||||
<value>Invalid IP address</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="InvalidServerTypeErrorMessage" xml:space="preserve">
|
||||
<value>Server Type is invalid.</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="LoadingExportableFailedGeneralErrorMessage" xml:space="preserve">
|
||||
<value>A required dll cannot be loaded. Please repair your application.</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -0,0 +1,32 @@
|
||||
# String resource file
|
||||
#
|
||||
# When processed by the String Resource Tool, this file generates
|
||||
# both a .CS and a .RESX file with the same name as the file.
|
||||
# The .CS file contains a class which can be used to access these
|
||||
# string resources, including the ability to format in
|
||||
# parameters, which are identified with the .NET {x} format
|
||||
# (see String.Format help).
|
||||
#
|
||||
# Comments below assume the file name is SR.strings.
|
||||
#
|
||||
# Lines starting with a semicolon ";" are also treated as comments, but
|
||||
# in a future version they will be extracted and made available in LocStudio
|
||||
# Put your comments to localizers _before_ the string they apply to.
|
||||
#
|
||||
# SMO build specific comment
|
||||
# after generating the .resx file, run srgen on it and get the .resx file
|
||||
# please remember to also check that .resx in, along with the
|
||||
# .strings and .cs files
|
||||
|
||||
[strings]
|
||||
|
||||
############################################################################
|
||||
# Azure Core DLL
|
||||
AzureServerNotFound = The server you specified {0} does not exist in any subscription in {1}. Either you have signed in with an incorrect account or your server was removed from subscription(s) in this account. Please check your account and try again.
|
||||
AzureSubscriptionFailedErrorMessage = An error occurred while getting Azure subscriptions
|
||||
DatabaseDiscoveryFailedErrorMessage = An error occurred while getting databases from servers of type {0} from {1}
|
||||
FirewallRuleAccessForbidden = {0} does not have permission to change the server firewall rule. Try again with a different account that is an Owner or Contributor of the Azure subscription or the server.
|
||||
FirewallRuleCreationFailed = An error occurred while creating a new firewall rule.
|
||||
InvalidIpAddress = Invalid IP address
|
||||
InvalidServerTypeErrorMessage = Server Type is invalid.
|
||||
LoadingExportableFailedGeneralErrorMessage = A required dll cannot be loaded. Please repair your application.
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
|
||||
<file datatype="xml" original="sr.resx" source-language="en">
|
||||
<body>
|
||||
<trans-unit id="AzureServerNotFound">
|
||||
<source>The server you specified {0} does not exist in any subscription in {1}. Either you have signed in with an incorrect account or your server was removed from subscription(s) in this account. Please check your account and try again.</source>
|
||||
<target state="new">The server you specified {0} does not exist in any subscription in {1}. Either you have signed in with an incorrect account or your server was removed from subscription(s) in this account. Please check your account and try again.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="AzureSubscriptionFailedErrorMessage">
|
||||
<source>An error occurred while getting Azure subscriptions</source>
|
||||
<target state="new">An error occurred while getting Azure subscriptions</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="DatabaseDiscoveryFailedErrorMessage">
|
||||
<source>An error occurred while getting databases from servers of type {0} from {1}</source>
|
||||
<target state="new">An error occurred while getting databases from servers of type {0} from {1}</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="FirewallRuleAccessForbidden">
|
||||
<source>{0} does not have permission to change the server firewall rule. Try again with a different account that is an Owner or Contributor of the Azure subscription or the server.</source>
|
||||
<target state="new">{0} does not have permission to change the server firewall rule. Try again with a different account that is an Owner or Contributor of the Azure subscription or the server.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidIpAddress">
|
||||
<source>Invalid IP address</source>
|
||||
<target state="new">Invalid IP address</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="InvalidServerTypeErrorMessage">
|
||||
<source>Server Type is invalid.</source>
|
||||
<target state="new">Server Type is invalid.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="LoadingExportableFailedGeneralErrorMessage">
|
||||
<source>A required dll cannot be loaded. Please repair your application.</source>
|
||||
<target state="new">A required dll cannot be loaded. Please repair your application.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="FirewallRuleCreationFailed">
|
||||
<source>An error occurred while creating a new firewall rule.</source>
|
||||
<target state="new">An error occurred while creating a new firewall rule.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
@@ -0,0 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<PackageId>Microsoft.SqlTools.ResourceProvider.Core</PackageId>
|
||||
<AssemblyName>Microsoft.SqlTools.ResourceProvider.Core</AssemblyName>
|
||||
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
|
||||
<ApplicationIcon />
|
||||
<OutputType>Library</OutputType>
|
||||
<StartupObject />
|
||||
<Description>Provides Resource Provider and control plane services for SqlTools applications.</Description>
|
||||
<Copyright><EFBFBD> Microsoft Corporation. All rights reserved.</Copyright>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Data.SqlClient" Version="4.4.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="2.0.0" />
|
||||
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
|
||||
<PackageReference Include="System.Composition" Version="1.1.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.SqlTools.Hosting\Microsoft.SqlTools.Hosting.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Localization\sr.resx" />
|
||||
<None Include="Localization\sr.strings" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
//
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.SqlTools.ServiceLayer.UnitTests")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.SqlTools.ServiceLayer.IntegrationTests")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.SqlTools.ServiceLayer.Test.Common")]
|
||||
|
||||
// Allowing internals visible access to Moq library to help testing
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Runtime.Serialization;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.DefaultImpl
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception to be raised if azure resource manager operation fails
|
||||
/// </summary>
|
||||
public class AzureResourceFailedException : ServiceExceptionBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AuthenticationFailedException class.
|
||||
/// </summary>
|
||||
public AzureResourceFailedException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AuthenticationFailedException class with a specified error message.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception. </param>
|
||||
public AzureResourceFailedException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AuthenticationFailedException class with a specified error message.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception. </param>
|
||||
/// <param name="httpStatusCode">The Http error code. </param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference
|
||||
/// (Nothing in Visual Basic) if no inner exception is specified</param>
|
||||
public AzureResourceFailedException(string message, HttpStatusCode httpStatusCode, Exception innerException = null)
|
||||
: base(message, httpStatusCode, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AuthenticationFailedException class with a specified error message.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception. </param>
|
||||
/// <param name="httpStatusCode">The Http error code. </param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference
|
||||
/// (Nothing in Visual Basic) if no inner exception is specified</param>
|
||||
public AzureResourceFailedException(string message, int httpStatusCode, Exception innerException = null)
|
||||
: base(message, httpStatusCode, innerException)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AuthenticationFailedException class with a specified error message
|
||||
/// and a reference to the inner exception that is the cause of this exception.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception. </param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference
|
||||
/// (Nothing in Visual Basic) if no inner exception is specified</param>
|
||||
public AzureResourceFailedException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the AuthenticationFailedException class with serialized data.
|
||||
/// </summary>
|
||||
/// <param name="info">The SerializationInfo that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The StreamingContext that contains contextual information about the source or destination.</param>
|
||||
public AzureResourceFailedException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Azure.Management.ResourceManager;
|
||||
using Microsoft.Azure.Management.Sql;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Authentication;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.DefaultImpl
|
||||
{
|
||||
/// <summary>
|
||||
/// VS session used by <see cref="AzureResourceManager" />.
|
||||
/// Includes all the clients that the resource management needs to get ther resources
|
||||
/// </summary>
|
||||
public class AzureResourceManagementSession : IAzureResourceManagementSession
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates the new session for given clients
|
||||
/// </summary>
|
||||
/// <param name="sqlManagementClient">Sql Management Client</param>
|
||||
/// <param name="resourceManagementClient">Resource Management Client</param>
|
||||
/// <param name="subscriptionContext">Subscription Context</param>
|
||||
public AzureResourceManagementSession(SqlManagementClient sqlManagementClient,
|
||||
ResourceManagementClient resourceManagementClient,
|
||||
IAzureUserAccountSubscriptionContext subscriptionContext)
|
||||
{
|
||||
SqlManagementClient = sqlManagementClient;
|
||||
ResourceManagementClient = resourceManagementClient;
|
||||
SubscriptionContext = subscriptionContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the session
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
CloseSession();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the session by disposing the clients
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool CloseSession()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ResourceManagementClient != null)
|
||||
{
|
||||
ResourceManagementClient.Dispose();
|
||||
}
|
||||
|
||||
if (SqlManagementClient != null)
|
||||
{
|
||||
SqlManagementClient.Dispose();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//TODO: trace
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscription Context
|
||||
/// </summary>
|
||||
public IAzureUserAccountSubscriptionContext SubscriptionContext
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resource Management Client
|
||||
/// </summary>
|
||||
public ResourceManagementClient ResourceManagementClient
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sql Management Client
|
||||
/// </summary>
|
||||
public SqlManagementClient SqlManagementClient
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Management.ResourceManager;
|
||||
using Microsoft.Azure.Management.ResourceManager.Models;
|
||||
using Microsoft.Azure.Management.Sql;
|
||||
using Microsoft.Azure.Management.Sql.Models;
|
||||
using RestFirewallRule = Microsoft.Azure.Management.Sql.Models.FirewallRule;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Authentication;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.FirewallRule;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Extensibility;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
using Microsoft.Rest;
|
||||
using System.Globalization;
|
||||
using Microsoft.Rest.Azure;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.DefaultImpl
|
||||
{
|
||||
/// <summary>
|
||||
/// Default implementation for <see cref="IAzureResourceManager" />
|
||||
/// Provides functionality to get azure resources by making Http request to the Azure REST API
|
||||
/// </summary>
|
||||
[Exportable(
|
||||
ServerTypes.SqlServer,
|
||||
Categories.Azure,
|
||||
typeof(IAzureResourceManager),
|
||||
"Microsoft.SqlServer.ConnectionServices.Azure.Impl.VsAzureResourceManager",
|
||||
1)
|
||||
]
|
||||
public class AzureResourceManager : ExportableBase, IAzureResourceManager
|
||||
{
|
||||
private readonly Uri _resourceManagementUri = new Uri("https://management.azure.com/");
|
||||
|
||||
public AzureResourceManager()
|
||||
{
|
||||
// Duplicate the exportable attribute as at present we do not support filtering using extensiondescriptor.
|
||||
// The attribute is preserved in order to simplify ability to backport into existing tools
|
||||
Metadata = new ExportableMetadata(
|
||||
ServerTypes.SqlServer,
|
||||
Categories.Azure,
|
||||
"Microsoft.SqlServer.ConnectionServices.Azure.Impl.VsAzureResourceManager");
|
||||
}
|
||||
|
||||
public async Task<IAzureResourceManagementSession> CreateSessionAsync(IAzureUserAccountSubscriptionContext subscriptionContext)
|
||||
{
|
||||
CommonUtil.CheckForNull(subscriptionContext, "subscriptionContext");
|
||||
try
|
||||
{
|
||||
ServiceClientCredentials credentials = await CreateCredentialsAsync(subscriptionContext);
|
||||
SqlManagementClient sqlManagementClient = new SqlManagementClient(_resourceManagementUri, credentials);
|
||||
ResourceManagementClient resourceManagementClient = new ResourceManagementClient(_resourceManagementUri, credentials);
|
||||
return new AzureResourceManagementSession(sqlManagementClient, resourceManagementClient, subscriptionContext);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(LogLevel.Error, string.Format(CultureInfo.CurrentCulture, "Failed to get databases {0}", ex));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of azure databases given subscription resource group name and server name
|
||||
/// </summary>
|
||||
/// <param name="azureResourceManagementSession">Subscription Context which includes credentials to use in the resource manager</param>
|
||||
/// <param name="resourceGroupName">Resource Group Name</param>
|
||||
/// <param name="serverName">Server name</param>
|
||||
/// <returns>The list of databases</returns>
|
||||
public async Task<IEnumerable<IAzureResource>> GetAzureDatabasesAsync(
|
||||
IAzureResourceManagementSession azureResourceManagementSession,
|
||||
string resourceGroupName,
|
||||
string serverName)
|
||||
{
|
||||
CommonUtil.CheckForNull(azureResourceManagementSession, "azureResourceManagerSession");
|
||||
try
|
||||
{
|
||||
AzureResourceManagementSession vsAzureResourceManagementSession = azureResourceManagementSession as AzureResourceManagementSession;
|
||||
|
||||
if (vsAzureResourceManagementSession != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
IEnumerable<Database> databaseListResponse = await vsAzureResourceManagementSession.SqlManagementClient.Databases.ListByServerAsync(resourceGroupName, serverName);
|
||||
return databaseListResponse.Select(
|
||||
x => new AzureResourceWrapper(x) { ResourceGroupName = resourceGroupName });
|
||||
}
|
||||
catch(HttpOperationException ex)
|
||||
{
|
||||
throw new AzureResourceFailedException(SR.FailedToGetAzureDatabasesErrorMessage, ex.Response.StatusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Write(LogLevel.Error, string.Format(CultureInfo.CurrentCulture, "Failed to get databases {0}", ex.Message));
|
||||
throw;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of azure servers given subscription
|
||||
/// </summary>
|
||||
/// <param name="azureResourceManagementSession">Subscription Context which includes credentials to use in the resource manager</param>
|
||||
/// <returns>The list of Sql server resources</returns>
|
||||
public async Task<IEnumerable<IAzureSqlServerResource>> GetSqlServerAzureResourcesAsync(
|
||||
IAzureResourceManagementSession azureResourceManagementSession)
|
||||
{
|
||||
CommonUtil.CheckForNull(azureResourceManagementSession, "azureResourceManagerSession");
|
||||
List<IAzureSqlServerResource> sqlServers = new List<IAzureSqlServerResource>();
|
||||
try
|
||||
{
|
||||
AzureResourceManagementSession vsAzureResourceManagementSession = azureResourceManagementSession as AzureResourceManagementSession;
|
||||
if(vsAzureResourceManagementSession != null)
|
||||
{
|
||||
IEnumerable<ResourceGroup> resourceGroupNames = await GetResourceGroupsAsync(vsAzureResourceManagementSession);
|
||||
if (resourceGroupNames != null)
|
||||
{
|
||||
foreach (ResourceGroup resourceGroupExtended in resourceGroupNames)
|
||||
{
|
||||
try
|
||||
{
|
||||
IServersOperations serverOperations = vsAzureResourceManagementSession.SqlManagementClient.Servers;
|
||||
IPage<Server> servers = await serverOperations.ListByResourceGroupAsync(resourceGroupExtended.Name);
|
||||
if (servers != null)
|
||||
{
|
||||
sqlServers.AddRange(servers.Select(x =>
|
||||
new SqlAzureResource(x) { ResourceGroupName = resourceGroupExtended.Name }));
|
||||
}
|
||||
}
|
||||
catch (HttpOperationException ex)
|
||||
{
|
||||
throw new AzureResourceFailedException(SR.FailedToGetAzureSqlServersErrorMessage, ex.Response.StatusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
TraceException(TraceEventType.Error, (int) TraceId.AzureResource, ex, "Failed to get servers");
|
||||
throw;
|
||||
}
|
||||
|
||||
return sqlServers;
|
||||
}
|
||||
|
||||
public async Task<FirewallRuleResponse> CreateFirewallRuleAsync(
|
||||
IAzureResourceManagementSession azureResourceManagementSession,
|
||||
IAzureSqlServerResource azureSqlServer,
|
||||
FirewallRuleRequest firewallRuleRequest)
|
||||
{
|
||||
CommonUtil.CheckForNull(azureResourceManagementSession, "azureResourceManagerSession");
|
||||
CommonUtil.CheckForNull(firewallRuleRequest, "firewallRuleRequest");
|
||||
CommonUtil.CheckForNull(azureSqlServer, "azureSqlServer");
|
||||
|
||||
try
|
||||
{
|
||||
AzureResourceManagementSession vsAzureResourceManagementSession = azureResourceManagementSession as AzureResourceManagementSession;
|
||||
|
||||
if (vsAzureResourceManagementSession != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var firewallRule = new RestFirewallRule()
|
||||
{
|
||||
EndIpAddress = firewallRuleRequest.EndIpAddress.ToString(),
|
||||
StartIpAddress = firewallRuleRequest.StartIpAddress.ToString()
|
||||
};
|
||||
IFirewallRulesOperations firewallRuleOperations = vsAzureResourceManagementSession.SqlManagementClient.FirewallRules;
|
||||
var firewallRuleResponse = await firewallRuleOperations.CreateOrUpdateAsync(
|
||||
azureSqlServer.ResourceGroupName,
|
||||
azureSqlServer.Name,
|
||||
firewallRuleRequest.FirewallRuleName,
|
||||
firewallRule);
|
||||
return new FirewallRuleResponse()
|
||||
{
|
||||
StartIpAddress = firewallRuleResponse.StartIpAddress,
|
||||
EndIpAddress = firewallRuleResponse.EndIpAddress,
|
||||
Created = true
|
||||
};
|
||||
}
|
||||
catch (HttpOperationException ex)
|
||||
{
|
||||
throw new AzureResourceFailedException(SR.FirewallRuleCreationFailed, ex.Response.StatusCode);
|
||||
}
|
||||
}
|
||||
// else respond with failure case
|
||||
return new FirewallRuleResponse()
|
||||
{
|
||||
Created = false
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TraceException(TraceEventType.Error, (int) TraceId.AzureResource, ex, "Failed to get databases");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the azure resource groups for given subscription
|
||||
/// </summary>
|
||||
private async Task<IEnumerable<ResourceGroup>> GetResourceGroupsAsync(AzureResourceManagementSession vsAzureResourceManagementSession)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (vsAzureResourceManagementSession != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
IResourceGroupsOperations resourceGroupOperations = vsAzureResourceManagementSession.ResourceManagementClient.ResourceGroups;
|
||||
IPage<ResourceGroup> resourceGroupList = await resourceGroupOperations.ListAsync();
|
||||
if (resourceGroupList != null)
|
||||
{
|
||||
return resourceGroupList.AsEnumerable();
|
||||
}
|
||||
|
||||
}
|
||||
catch (HttpOperationException ex)
|
||||
{
|
||||
throw new AzureResourceFailedException(SR.FailedToGetAzureResourceGroupsErrorMessage, ex.Response.StatusCode);
|
||||
}
|
||||
}
|
||||
|
||||
return Enumerable.Empty<ResourceGroup>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TraceException(TraceEventType.Error, (int)TraceId.AzureResource, ex, "Failed to get azure resource groups");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates credential instance for given subscription
|
||||
/// </summary>
|
||||
private Task<ServiceClientCredentials> CreateCredentialsAsync(IAzureUserAccountSubscriptionContext subscriptionContext)
|
||||
{
|
||||
AzureUserAccountSubscriptionContext azureUserSubContext =
|
||||
subscriptionContext as AzureUserAccountSubscriptionContext;
|
||||
|
||||
if (azureUserSubContext != null)
|
||||
{
|
||||
return Task.FromResult(azureUserSubContext.Credentials);
|
||||
}
|
||||
throw new NotSupportedException("This uses an unknown subscription type");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Azure.Management.Sql.Models;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.DefaultImpl
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation for <see cref="IAzureResource" /> using VS services.
|
||||
/// Provides information about an Azure resource
|
||||
/// </summary>
|
||||
public class AzureResourceWrapper : IAzureResource
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes the resource
|
||||
/// </summary>
|
||||
public AzureResourceWrapper(TrackedResource azureResource)
|
||||
{
|
||||
CommonUtil.CheckForNull(azureResource, nameof(azureResource));
|
||||
AzureResource = azureResource;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resource name
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return AzureResource != null ? AzureResource.Name : string.Empty;
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resource type
|
||||
/// </summary>
|
||||
public string Type
|
||||
{
|
||||
get
|
||||
{
|
||||
return AzureResource != null ? AzureResource.Type : string.Empty;
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resource id
|
||||
/// </summary>
|
||||
public string Id
|
||||
{
|
||||
get
|
||||
{
|
||||
return AzureResource != null ? AzureResource.Id : string.Empty;
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resource Group Name
|
||||
/// </summary>
|
||||
public string ResourceGroupName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Resource Location
|
||||
/// </summary>
|
||||
public string Location
|
||||
{
|
||||
get
|
||||
{
|
||||
return AzureResource != null ? AzureResource.Location : string.Empty;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (AzureResource != null)
|
||||
{
|
||||
AzureResource.Location = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The resource wrapped by this class
|
||||
/// </summary>
|
||||
protected TrackedResource AzureResource
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Authentication;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.DefaultImpl
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation for <see cref="IAzureSubscriptionContext" /> using VS services
|
||||
/// Contains information about an Azure subscription
|
||||
/// </summary>
|
||||
public class AzureSubscriptionContext : IAzureSubscriptionContext
|
||||
{
|
||||
private readonly IAzureSubscriptionIdentifier _azureSubscriptionIdentifier;
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor to initialize the subscription
|
||||
/// </summary>
|
||||
public AzureSubscriptionContext(IAzureSubscriptionIdentifier azureSubscriptionIdentifier)
|
||||
{
|
||||
_azureSubscriptionIdentifier = azureSubscriptionIdentifier;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if given subscription equals this class
|
||||
/// </summary>
|
||||
public bool Equals(IAzureSubscriptionContext other)
|
||||
{
|
||||
return (other == null && Subscription == null) || (other != null && other.Subscription.Equals(Subscription));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the wraper for the subscription identifier
|
||||
/// </summary>
|
||||
public IAzureSubscriptionIdentifier Subscription
|
||||
{
|
||||
get
|
||||
{
|
||||
return _azureSubscriptionIdentifier;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns subscription name
|
||||
/// </summary>
|
||||
public string SubscriptionName
|
||||
{
|
||||
get
|
||||
{
|
||||
return _azureSubscriptionIdentifier != null ?
|
||||
_azureSubscriptionIdentifier.SubscriptionId : string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Authentication;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.DefaultImpl
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation for <see cref="IAzureSubscriptionIdentifier" />
|
||||
/// Contains information about an Azure subscription identifier
|
||||
/// </summary>
|
||||
public class AzureSubscriptionIdentifier : IAzureSubscriptionIdentifier
|
||||
{
|
||||
/// <summary>
|
||||
/// Default constructor to initialize the subscription identifier
|
||||
/// </summary>
|
||||
public AzureSubscriptionIdentifier(IAzureUserAccount userAccount, string subscriptionId, Uri serviceManagementEndpoint)
|
||||
{
|
||||
UserAccount = userAccount;
|
||||
SubscriptionId = subscriptionId;
|
||||
ServiceManagementEndpoint = serviceManagementEndpoint;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if given subscription identifier equals this class
|
||||
/// </summary>
|
||||
public bool Equals(IAzureSubscriptionIdentifier other)
|
||||
{
|
||||
return other != null &&
|
||||
CommonUtil.SameString(SubscriptionId, other.SubscriptionId) &&
|
||||
CommonUtil.SameUri(ServiceManagementEndpoint, other.ServiceManagementEndpoint);
|
||||
}
|
||||
|
||||
public IAzureUserAccount UserAccount
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the endpoint url used by the identifier
|
||||
/// </summary>
|
||||
public Uri ServiceManagementEndpoint
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscription id
|
||||
/// </summary>
|
||||
public string SubscriptionId
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Authentication;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.DefaultImpl
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation for <see cref="IAzureUserAccount" /> using VS services
|
||||
/// Contains information about an Azure account
|
||||
/// </summary>
|
||||
public class AzureUserAccount : IAzureUserAccount
|
||||
{
|
||||
private string uniqueId;
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor to initializes user session
|
||||
/// </summary>
|
||||
public AzureUserAccount()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor to initializes user session
|
||||
/// </summary>
|
||||
public AzureUserAccount(IAzureUserAccount azureUserAccount)
|
||||
{
|
||||
CopyFrom(azureUserAccount);
|
||||
}
|
||||
|
||||
private void CopyFrom(IAzureUserAccount azureUserAccount)
|
||||
{
|
||||
this.DisplayInfo = new AzureUserAccountDisplayInfo(azureUserAccount.DisplayInfo);
|
||||
this.NeedsReauthentication = azureUserAccount.NeedsReauthentication;
|
||||
this.TenantId = azureUserAccount.TenantId;
|
||||
this.UniqueId = azureUserAccount.UniqueId;
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns true if given user account equals this class
|
||||
/// </summary>
|
||||
public bool Equals(IAzureUserAccount other)
|
||||
{
|
||||
return other != null &&
|
||||
CommonUtil.SameString(other.UniqueId, UniqueId) &&
|
||||
CommonUtil.SameString(other.TenantId, TenantId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unique Id
|
||||
/// </summary>
|
||||
public string UniqueId
|
||||
{
|
||||
get
|
||||
{
|
||||
return uniqueId == null ? string.Empty : uniqueId;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.uniqueId = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if user needs reauthentication
|
||||
/// </summary>
|
||||
public bool NeedsReauthentication
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// User display info
|
||||
/// </summary>
|
||||
public IAzureUserAccountDisplayInfo DisplayInfo
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tenant Id
|
||||
/// </summary>
|
||||
public string TenantId
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Authentication;
|
||||
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.DefaultImpl
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation for <see cref="IAzureUserAccountDisplayInfo" /> using VS services
|
||||
/// Contains information about an Azure account display info
|
||||
/// </summary>
|
||||
public class AzureUserAccountDisplayInfo : IAzureUserAccountDisplayInfo
|
||||
{
|
||||
private string userName;
|
||||
private string accountDisplayName;
|
||||
|
||||
/// <summary>
|
||||
/// Creating the instance using <see cref="IAzureUserAccountDisplayInfo" />
|
||||
/// </summary>
|
||||
public AzureUserAccountDisplayInfo(IAzureUserAccountDisplayInfo azureUserAccountDisplayInfo)
|
||||
{
|
||||
CopyFrom(azureUserAccountDisplayInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creating empty instance
|
||||
/// </summary>
|
||||
public AzureUserAccountDisplayInfo()
|
||||
{
|
||||
}
|
||||
|
||||
private void CopyFrom(IAzureUserAccountDisplayInfo azureUserAccountDisplayInfo)
|
||||
{
|
||||
this.AccountDisplayName = azureUserAccountDisplayInfo.AccountDisplayName;
|
||||
this.AccountLogo = azureUserAccountDisplayInfo.AccountLogo;
|
||||
this.ProviderDisplayName = azureUserAccountDisplayInfo.ProviderDisplayName;
|
||||
this.ProviderLogo = azureUserAccountDisplayInfo.ProviderLogo;
|
||||
this.UserName = azureUserAccountDisplayInfo.UserName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if given user account equals this class
|
||||
/// </summary>
|
||||
public bool Equals(IAzureUserAccountDisplayInfo other)
|
||||
{
|
||||
return other != null &&
|
||||
((other.AccountDisplayName == null && AccountDisplayName == null ) || (other.AccountDisplayName != null && other.AccountDisplayName.Equals(AccountDisplayName))) &&
|
||||
((other.UserName == null && UserName == null ) || (other.UserName != null && other.UserName.Equals(UserName)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Account display name
|
||||
/// </summary>
|
||||
public string AccountDisplayName
|
||||
{
|
||||
get
|
||||
{
|
||||
return accountDisplayName != null ? accountDisplayName : string.Empty;
|
||||
}
|
||||
set
|
||||
{
|
||||
accountDisplayName = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Account lego
|
||||
/// </summary>
|
||||
public byte[] AccountLogo
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provider display name
|
||||
/// </summary>
|
||||
public string ProviderDisplayName
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provider lego
|
||||
/// </summary>
|
||||
public byte[] ProviderLogo
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// User name
|
||||
/// </summary>
|
||||
public string UserName
|
||||
{
|
||||
get
|
||||
{
|
||||
return userName != null ? userName : string.Empty;
|
||||
}
|
||||
set
|
||||
{
|
||||
userName = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Rest;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Authentication;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.DefaultImpl
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation for <see cref="IAzureUserAccountSubscriptionContext" /> using built-in services
|
||||
/// Contains information about an Azure account subscription
|
||||
/// </summary>
|
||||
public class AzureUserAccountSubscriptionContext : IAzureUserAccountSubscriptionContext
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor to initializes user account and subscription
|
||||
/// </summary>
|
||||
public AzureUserAccountSubscriptionContext(AzureSubscriptionIdentifier subscription, ServiceClientCredentials credentials)
|
||||
{
|
||||
CommonUtil.CheckForNull(subscription, nameof(subscription));
|
||||
CommonUtil.CheckForNull(credentials, nameof(credentials));
|
||||
Subscription = subscription;
|
||||
Credentials = credentials;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a subscription context for connecting with a known access token. This creates a <see cref="TokenCredentials"/> object for use
|
||||
/// in a request
|
||||
/// </summary>
|
||||
public static AzureUserAccountSubscriptionContext CreateStringTokenContext(AzureSubscriptionIdentifier subscription, string accessToken)
|
||||
{
|
||||
CommonUtil.CheckForNull(subscription, nameof(subscription));
|
||||
CommonUtil.CheckStringForNullOrEmpty(accessToken, nameof(accessToken));
|
||||
TokenCredentials credentials = new TokenCredentials(accessToken);
|
||||
return new AzureUserAccountSubscriptionContext(subscription, credentials);
|
||||
}
|
||||
|
||||
public bool Equals(IAzureSubscriptionContext other)
|
||||
{
|
||||
return other != null && other.Equals(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the wraper for the subscription identifier
|
||||
/// </summary>
|
||||
public IAzureSubscriptionIdentifier Subscription
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscription name
|
||||
/// </summary>
|
||||
public string SubscriptionName
|
||||
{
|
||||
get { return Subscription != null ? Subscription.SubscriptionId : string.Empty; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool Equals(IAzureUserAccountSubscriptionContext other)
|
||||
{
|
||||
return other != null &&
|
||||
CommonUtil.SameSubscriptionIdentifier(Subscription, other.Subscription) &&
|
||||
CommonUtil.SameUserAccount(UserAccount, other.UserAccount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// User Account
|
||||
/// </summary>
|
||||
public IAzureUserAccount UserAccount
|
||||
{
|
||||
get
|
||||
{
|
||||
return Subscription != null ?
|
||||
new AzureUserAccount(Subscription.UserAccount) : null;
|
||||
}
|
||||
}
|
||||
|
||||
public ServiceClientCredentials Credentials
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
// WARNING:
|
||||
// This file was generated by the Microsoft DataWarehouse String Resource Tool 1.37.0.0
|
||||
// from information in sr.strings
|
||||
// DO NOT MODIFY THIS FILE'S CONTENTS, THEY WILL BE OVERWRITTEN
|
||||
//
|
||||
namespace Microsoft.SqlTools.ResourceProvider.DefaultImpl
|
||||
{
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Globalization;
|
||||
|
||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class SR
|
||||
{
|
||||
protected SR()
|
||||
{ }
|
||||
|
||||
public static CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.Culture;
|
||||
}
|
||||
set
|
||||
{
|
||||
Keys.Culture = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static string FailedToGetAzureDatabasesErrorMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.FailedToGetAzureDatabasesErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public static string FailedToGetAzureResourceGroupsErrorMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.FailedToGetAzureResourceGroupsErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public static string FailedToGetAzureSqlServersErrorMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.FailedToGetAzureSqlServersErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public static string FirewallRuleCreationFailed
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.GetString(Keys.FirewallRuleCreationFailed);
|
||||
}
|
||||
}
|
||||
|
||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class Keys
|
||||
{
|
||||
static ResourceManager resourceManager = new ResourceManager("Microsoft.SqlTools.ResourceProvider.DefaultImpl.Localization.SR", typeof(SR).GetTypeInfo().Assembly);
|
||||
|
||||
static CultureInfo _culture = null;
|
||||
|
||||
|
||||
public const string FailedToGetAzureDatabasesErrorMessage = "FailedToGetAzureDatabasesErrorMessage";
|
||||
|
||||
|
||||
public const string FailedToGetAzureResourceGroupsErrorMessage = "FailedToGetAzureResourceGroupsErrorMessage";
|
||||
|
||||
|
||||
public const string FailedToGetAzureSqlServersErrorMessage = "FailedToGetAzureSqlServersErrorMessage";
|
||||
|
||||
|
||||
public const string FirewallRuleCreationFailed = "FirewallRuleCreationFailed";
|
||||
|
||||
|
||||
private Keys()
|
||||
{ }
|
||||
|
||||
public static CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return _culture;
|
||||
}
|
||||
set
|
||||
{
|
||||
_culture = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetString(string key)
|
||||
{
|
||||
return resourceManager.GetString(key, _culture);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype=">text/microsoft-resx</resheader>
|
||||
<resheader name="version=">2.0</resheader>
|
||||
<resheader name="reader=">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer=">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1="><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing=">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64=">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64=">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata=">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true=">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded=">
|
||||
<xsd:element name="metadata=">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly=">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data=">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader=">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="FailedToGetAzureDatabasesErrorMessage" xml:space="preserve">
|
||||
<value>An error occurred while getting Azure databases</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="FailedToGetAzureResourceGroupsErrorMessage" xml:space="preserve">
|
||||
<value>An error occurred while getting Azure resource groups</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="FailedToGetAzureSqlServersErrorMessage" xml:space="preserve">
|
||||
<value>An error occurred while getting Azure Sql Servers</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
<data name="FirewallRuleCreationFailed" xml:space="preserve">
|
||||
<value>An error occurred while creating a new firewall rule.</value>
|
||||
<comment></comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -0,0 +1,28 @@
|
||||
# String resource file
|
||||
#
|
||||
# When processed by the String Resource Tool, this file generates
|
||||
# both a .CS and a .RESX file with the same name as the file.
|
||||
# The .CS file contains a class which can be used to access these
|
||||
# string resources, including the ability to format in
|
||||
# parameters, which are identified with the .NET {x} format
|
||||
# (see String.Format help).
|
||||
#
|
||||
# Comments below assume the file name is SR.strings.
|
||||
#
|
||||
# Lines starting with a semicolon ";" are also treated as comments, but
|
||||
# in a future version they will be extracted and made available in LocStudio
|
||||
# Put your comments to localizers _before_ the string they apply to.
|
||||
#
|
||||
# SMO build specific comment
|
||||
# after generating the .resx file, run srgen on it and get the .resx file
|
||||
# please remember to also check that .resx in, along with the
|
||||
# .strings and .cs files
|
||||
|
||||
[strings]
|
||||
|
||||
############################################################################
|
||||
# Azure Core DLL
|
||||
FailedToGetAzureDatabasesErrorMessage = An error occurred while getting Azure databases
|
||||
FailedToGetAzureResourceGroupsErrorMessage = An error occurred while getting Azure resource groups
|
||||
FailedToGetAzureSqlServersErrorMessage = An error occurred while getting Azure Sql Servers
|
||||
FirewallRuleCreationFailed = An error occurred while creating a new firewall rule.
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
|
||||
<file datatype="xml" original="sr.resx" source-language="en">
|
||||
<body>
|
||||
<trans-unit id="FailedToGetAzureDatabasesErrorMessage">
|
||||
<source>An error occurred while getting Azure databases</source>
|
||||
<target state="new">An error occurred while getting Azure databases</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="FailedToGetAzureResourceGroupsErrorMessage">
|
||||
<source>An error occurred while getting Azure resource groups</source>
|
||||
<target state="new">An error occurred while getting Azure resource groups</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="FailedToGetAzureSqlServersErrorMessage">
|
||||
<source>An error occurred while getting Azure Sql Servers</source>
|
||||
<target state="new">An error occurred while getting Azure Sql Servers</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="FirewallRuleCreationFailed">
|
||||
<source>An error occurred while creating a new firewall rule.</source>
|
||||
<target state="new">An error occurred while creating a new firewall rule.</target>
|
||||
<note></note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
@@ -0,0 +1,31 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<PackageId>Microsoft.SqlTools.ResourceProvider.DefaultImpl</PackageId>
|
||||
<AssemblyName>Microsoft.SqlTools.ResourceProvider.DefaultImpl</AssemblyName>
|
||||
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
|
||||
<ApplicationIcon />
|
||||
<OutputType>Library</OutputType>
|
||||
<StartupObject />
|
||||
<Description>Provides the default for SqlTools applications.</Description>
|
||||
<Copyright><EFBFBD> Microsoft Corporation. All rights reserved.</Copyright>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Azure.Management.ResourceManager" Version="1.6.0-preview" />
|
||||
<PackageReference Include="Microsoft.Rest.ClientRuntime" Version="2.3.10" />
|
||||
<PackageReference Include="Microsoft.Rest.ClientRuntime.Azure" Version="3.3.10" />
|
||||
<PackageReference Include="System.Data.SqlClient" Version="4.4.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="2.0.0" />
|
||||
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
|
||||
<PackageReference Include="System.Composition" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.Azure.Management.Sql" Version="1.6.0-preview" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.SqlTools.ResourceProvider.Core\Microsoft.SqlTools.ResourceProvider.Core.csproj" />
|
||||
<ProjectReference Include="..\Microsoft.SqlTools.Hosting\Microsoft.SqlTools.Hosting.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Localization\sr.resx" />
|
||||
<None Include="Localization\sr.strings" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core;
|
||||
using Models = Microsoft.Azure.Management.Sql.Models;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider.DefaultImpl
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementation for <see cref="IAzureSqlServerResource" /> using VS services
|
||||
/// Provides information about an Azure Sql Server resource
|
||||
/// </summary>
|
||||
public class SqlAzureResource : AzureResourceWrapper, IAzureSqlServerResource
|
||||
{
|
||||
private readonly Models.Server _azureSqlServerResource;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the resource
|
||||
/// </summary>
|
||||
public SqlAzureResource(Models.Server azureResource) : base(azureResource)
|
||||
{
|
||||
CommonUtil.CheckForNull(azureResource, nameof(azureResource));
|
||||
_azureSqlServerResource = azureResource;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fully qualified domain name
|
||||
/// </summary>
|
||||
public string FullyQualifiedDomainName
|
||||
{
|
||||
get
|
||||
{
|
||||
return _azureSqlServerResource != null ? _azureSqlServerResource.FullyQualifiedDomainName : string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Administrator User
|
||||
/// </summary>
|
||||
public string AdministratorLogin
|
||||
{
|
||||
get
|
||||
{
|
||||
return _azureSqlServerResource != null ? _azureSqlServerResource.AdministratorLogin : string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
62
src/Microsoft.SqlTools.ResourceProvider/Localization/sr.cs
Normal file
62
src/Microsoft.SqlTools.ResourceProvider/Localization/sr.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
// WARNING:
|
||||
// This file was generated by the Microsoft DataWarehouse String Resource Tool 1.37.0.0
|
||||
// from information in sr.strings
|
||||
// DO NOT MODIFY THIS FILE'S CONTENTS, THEY WILL BE OVERWRITTEN
|
||||
//
|
||||
namespace Microsoft.SqlTools.ResourceProvider
|
||||
{
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Globalization;
|
||||
|
||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class SR
|
||||
{
|
||||
protected SR()
|
||||
{ }
|
||||
|
||||
public static CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return Keys.Culture;
|
||||
}
|
||||
set
|
||||
{
|
||||
Keys.Culture = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class Keys
|
||||
{
|
||||
static ResourceManager resourceManager = new ResourceManager("Microsoft.SqlTools.ResourceProvider.Localization.SR", typeof(SR).GetTypeInfo().Assembly);
|
||||
|
||||
static CultureInfo _culture = null;
|
||||
|
||||
|
||||
private Keys()
|
||||
{ }
|
||||
|
||||
public static CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return _culture;
|
||||
}
|
||||
set
|
||||
{
|
||||
_culture = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetString(string key)
|
||||
{
|
||||
return resourceManager.GetString(key, _culture);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
120
src/Microsoft.SqlTools.ResourceProvider/Localization/sr.resx
Normal file
120
src/Microsoft.SqlTools.ResourceProvider/Localization/sr.resx
Normal file
@@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype=">text/microsoft-resx</resheader>
|
||||
<resheader name="version=">2.0</resheader>
|
||||
<resheader name="reader=">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer=">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1="><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing=">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64=">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64=">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata=">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true=">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded=">
|
||||
<xsd:element name="metadata=">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly=">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data=">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader=">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@@ -0,0 +1,24 @@
|
||||
# String resource file
|
||||
#
|
||||
# When processed by the String Resource Tool, this file generates
|
||||
# both a .CS and a .RESX file with the same name as the file.
|
||||
# The .CS file contains a class which can be used to access these
|
||||
# string resources, including the ability to format in
|
||||
# parameters, which are identified with the .NET {x} format
|
||||
# (see String.Format help).
|
||||
#
|
||||
# Comments below assume the file name is SR.strings.
|
||||
#
|
||||
# Lines starting with a semicolon ";" are also treated as comments, but
|
||||
# in a future version they will be extracted and made available in LocStudio
|
||||
# Put your comments to localizers _before_ the string they apply to.
|
||||
#
|
||||
# SMO build specific comment
|
||||
# after generating the .resx file, run srgen on it and get the .resx file
|
||||
# please remember to also check that .resx in, along with the
|
||||
# .strings and .cs files
|
||||
|
||||
[strings]
|
||||
|
||||
############################################################################
|
||||
# Azure Service DLL
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
|
||||
<file datatype="xml" original="sr.resx" source-language="en">
|
||||
<body></body>
|
||||
</file>
|
||||
</xliff>
|
||||
@@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<PackageId>SqlToolsResourceProviderService</PackageId>
|
||||
<AssemblyName>SqlToolsResourceProviderService</AssemblyName>
|
||||
<OutputType>Exe</OutputType>
|
||||
<Company>Microsoft</Company>
|
||||
<Product>Sql Tools Service for Resource Provider services</Product>
|
||||
<Description>Provides Resource Provider and control plane support.</Description>
|
||||
<Copyright><EFBFBD> Microsoft Corporation. All rights reserved.</Copyright>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<PreserveCompilationContext>true</PreserveCompilationContext>
|
||||
<DebugType>portable</DebugType>
|
||||
<EnableDefaultEmbeddedResourceItems>false</EnableDefaultEmbeddedResourceItems>
|
||||
<RuntimeIdentifiers>win7-x64;win7-x86;ubuntu.14.04-x64;ubuntu.16.04-x64;centos.7-x64;rhel.7.2-x64;debian.8-x64;fedora.23-x64;opensuse.13.2-x64;osx.10.11-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DefineConstants>TRACE;DEBUG;NETCOREAPP1_0;NETCOREAPP2_0</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.SqlTools.Hosting\Microsoft.SqlTools.Hosting.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Localization\sr.resx" />
|
||||
<None Include="Localization\sr.strings" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
63
src/Microsoft.SqlTools.ResourceProvider/Program.cs
Normal file
63
src/Microsoft.SqlTools.ResourceProvider/Program.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.SqlTools.Hosting.Utility;
|
||||
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Main application class for Credentials Service Host executable
|
||||
/// </summary>
|
||||
internal class Program
|
||||
{
|
||||
private const string ServiceName = "SqlToolsAzure.exe";
|
||||
|
||||
/// <summary>
|
||||
/// Main entry point into the Credentials Service Host
|
||||
/// </summary>
|
||||
internal static void Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
// read command-line arguments
|
||||
CommandOptions commandOptions = new CommandOptions(args, ServiceName);
|
||||
if (commandOptions.ShouldExit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string logFilePath = "sqltoolsazure";
|
||||
if (!string.IsNullOrWhiteSpace(commandOptions.LoggingDirectory))
|
||||
{
|
||||
logFilePath = Path.Combine(commandOptions.LoggingDirectory, logFilePath);
|
||||
}
|
||||
|
||||
// turn on Verbose logging during early development
|
||||
// we need to switch to Normal when preparing for public preview
|
||||
Logger.Initialize(logFilePath: logFilePath, minimumLogLevel: LogLevel.Verbose, isEnabled: commandOptions.EnableLogging);
|
||||
Logger.Write(LogLevel.Normal, "Starting SqlTools Azure Provider");
|
||||
|
||||
// set up the host details and profile paths
|
||||
var hostDetails = new HostDetails(
|
||||
name: "SqlTools Azure Provider",
|
||||
profileId: "Microsoft.SqlTools.ResourceProvider",
|
||||
version: new Version(1, 0));
|
||||
|
||||
SqlToolsContext sqlToolsContext = new SqlToolsContext(hostDetails);
|
||||
UtilityServiceHost serviceHost = ResourceProviderHostLoader.CreateAndStartServiceHost(sqlToolsContext);
|
||||
|
||||
serviceHost.WaitForExit();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Write(LogLevel.Error, string.Format("An unhandled exception occurred: {0}", e));
|
||||
Environment.Exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using Microsoft.SqlTools.Extensibility;
|
||||
using Microsoft.SqlTools.Hosting;
|
||||
using Microsoft.SqlTools.Hosting.Protocol;
|
||||
using Microsoft.SqlTools.ServiceLayer.SqlContext;
|
||||
using Microsoft.SqlTools.Utility;
|
||||
|
||||
namespace Microsoft.SqlTools.ResourceProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides support for starting up a service host. This is a common responsibility
|
||||
/// for both the main service program and test driver that interacts with it
|
||||
/// </summary>
|
||||
public static class ResourceProviderHostLoader
|
||||
{
|
||||
private static object lockObject = new object();
|
||||
private static bool isLoaded;
|
||||
|
||||
internal static UtilityServiceHost CreateAndStartServiceHost(SqlToolsContext sqlToolsContext)
|
||||
{
|
||||
UtilityServiceHost serviceHost = UtilityServiceHost.Instance;
|
||||
lock (lockObject)
|
||||
{
|
||||
if (!isLoaded)
|
||||
{
|
||||
// Grab the instance of the service host
|
||||
serviceHost.Initialize();
|
||||
|
||||
InitializeRequestHandlersAndServices(serviceHost, sqlToolsContext);
|
||||
|
||||
// Start the service only after all request handlers are setup. This is vital
|
||||
// as otherwise the Initialize event can be lost - it's processed and discarded before the handler
|
||||
// is hooked up to receive the message
|
||||
serviceHost.Start().Wait();
|
||||
isLoaded = true;
|
||||
}
|
||||
}
|
||||
return serviceHost;
|
||||
}
|
||||
|
||||
private static void InitializeRequestHandlersAndServices(UtilityServiceHost serviceHost, SqlToolsContext sqlToolsContext)
|
||||
{
|
||||
// Load extension provider, which currently finds all exports in current DLL. Can be changed to find based
|
||||
// on directory or assembly list quite easily in the future
|
||||
ExtensionServiceProvider serviceProvider = ExtensionServiceProvider.CreateDefaultServiceProvider();
|
||||
serviceProvider.RegisterSingleService(sqlToolsContext);
|
||||
serviceProvider.RegisterSingleService(serviceHost);
|
||||
|
||||
// CredentialService.Instance.InitializeService(serviceHost);
|
||||
// serviceProvider.RegisterSingleService(CredentialService.Instance);
|
||||
|
||||
InitializeHostedServices(serviceProvider, serviceHost);
|
||||
|
||||
serviceHost.InitializeRequestHandlers();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal to support testing. Initializes <see cref="IHostedService"/> instances in the service,
|
||||
/// and registers them for their preferred service type
|
||||
/// </summary>
|
||||
internal static void InitializeHostedServices(RegisteredServiceProvider provider, IProtocolEndpoint host)
|
||||
{
|
||||
// Pre-register all services before initializing. This ensures that if one service wishes to reference
|
||||
// another one during initialization, it will be able to safely do so
|
||||
foreach (IHostedService service in provider.GetServices<IHostedService>())
|
||||
{
|
||||
provider.RegisterSingleService(service.ServiceType, service);
|
||||
}
|
||||
|
||||
foreach (IHostedService service in provider.GetServices<IHostedService>())
|
||||
{
|
||||
// Initialize all hosted services, and register them in the service provider for their requested
|
||||
// service type. This ensures that when searching for the ConnectionService you can get it without
|
||||
// searching for an IHostedService of type ConnectionService
|
||||
service.InitializeService(host);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Localization\sr.resx" />
|
||||
<None Include="Localization\sr.strings" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Connection\ReliableConnection\ReliableSqlCommand.cs">
|
||||
|
||||
@@ -12,12 +12,18 @@ REM backup current CSPROJ files
|
||||
COPY /Y %REPOROOT%\src\Microsoft.SqlTools.Credentials\Microsoft.SqlTools.Credentials.csproj %REPOROOT%\src\Microsoft.SqlTools.Credentials\Microsoft.SqlTools.Credentials.csproj.BAK
|
||||
COPY /Y %REPOROOT%\src\Microsoft.SqlTools.Hosting\Microsoft.SqlTools.Hosting.csproj %REPOROOT%\src\Microsoft.SqlTools.Hosting\Microsoft.SqlTools.Hosting.csproj.BAK
|
||||
COPY /Y %REPOROOT%\src\Microsoft.SqlTools.ServiceLayer\Microsoft.SqlTools.ServiceLayer.csproj %REPOROOT%\src\Microsoft.SqlTools.ServiceLayer\Microsoft.SqlTools.ServiceLayer.csproj.BAK
|
||||
COPY /Y %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider\Microsoft.SqlTools.ResourceProvider.csproj %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider\Microsoft.SqlTools.ResourceProvider.csproj.BAK
|
||||
COPY /Y %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider.Core\Microsoft.SqlTools.ResourceProvider.Core.csproj %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider.Core\Microsoft.SqlTools.ResourceProvider.Core.csproj.BAK
|
||||
COPY /Y %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider.DefaultImpl\Microsoft.SqlTools.ResourceProvider.DefaultImpl.csproj %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider.DefaultImpl\Microsoft.SqlTools.ResourceProvider.DefaultImpl.csproj.BAK
|
||||
|
||||
REM switch PDB type to Full since that is required by OpenCover for now
|
||||
REM we should remove this step on OpenCover supports portable PDB
|
||||
cscript /nologo ReplaceText.vbs %REPOROOT%\src\Microsoft.SqlTools.Credentials\Microsoft.SqlTools.Credentials.csproj portable full
|
||||
cscript /nologo ReplaceText.vbs %REPOROOT%\src\Microsoft.SqlTools.Hosting\Microsoft.SqlTools.Hosting.csproj portable full
|
||||
cscript /nologo ReplaceText.vbs %REPOROOT%\src\Microsoft.SqlTools.ServiceLayer\Microsoft.SqlTools.ServiceLayer.csproj portable full
|
||||
cscript /nologo ReplaceText.vbs %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider\Microsoft.SqlTools.ResourceProvider.csproj portable full
|
||||
cscript /nologo ReplaceText.vbs %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider.Core\Microsoft.SqlTools.ResourceProvider.Core.csproj portable full
|
||||
cscript /nologo ReplaceText.vbs %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider.DefaultImpl\Microsoft.SqlTools.ResourceProvider.DefaultImpl.csproj portable full
|
||||
|
||||
REM rebuild the SqlToolsService project
|
||||
dotnet restore %REPOROOT%\src\Microsoft.SqlTools.Credentials\Microsoft.SqlTools.Credentials.csproj
|
||||
@@ -26,6 +32,12 @@ dotnet restore %REPOROOT%\src\Microsoft.SqlTools.Hosting\Microsoft.SqlTools.Host
|
||||
dotnet build %REPOROOT%\src\Microsoft.SqlTools.Hosting\Microsoft.SqlTools.Hosting.csproj %DOTNETCONFIG%
|
||||
dotnet restore %REPOROOT%\src\Microsoft.SqlTools.ServiceLayer\Microsoft.SqlTools.ServiceLayer.csproj
|
||||
dotnet build %REPOROOT%\src\Microsoft.SqlTools.ServiceLayer\Microsoft.SqlTools.ServiceLayer.csproj %DOTNETCONFIG% -r win7-x64
|
||||
dotnet restore %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider\Microsoft.SqlTools.ResourceProvider.csproj
|
||||
dotnet build %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider\Microsoft.SqlTools.ResourceProvider.csproj %DOTNETCONFIG% -r win7-x64
|
||||
dotnet restore %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider.Core\Microsoft.SqlTools.ResourceProvider.Core.csproj
|
||||
dotnet build %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider.Core\Microsoft.SqlTools.ResourceProvider.Core.csproj %DOTNETCONFIG% -r win7-x64
|
||||
dotnet restore %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider.DefaultImpl\Microsoft.SqlTools.ResourceProvider.DefaultImpl.csproj
|
||||
dotnet build %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider.DefaultImpl\Microsoft.SqlTools.ResourceProvider.DefaultImpl.csproj %DOTNETCONFIG% -r win7-x64
|
||||
|
||||
REM run the tests through OpenCover and generate a report
|
||||
dotnet restore %REPOROOT%\test\Microsoft.SqlTools.ServiceLayer.TestDriver\Microsoft.SqlTools.ServiceLayer.TestDriver.csproj
|
||||
@@ -66,5 +78,11 @@ COPY /Y %REPOROOT%\src\Microsoft.SqlTools.Credentials\Microsoft.SqlTools.Credent
|
||||
DEL %REPOROOT%\src\Microsoft.SqlTools.Credentials\Microsoft.SqlTools.Credentials.csproj.BAK
|
||||
COPY /Y %REPOROOT%\src\Microsoft.SqlTools.Hosting\Microsoft.SqlTools.Hosting.csproj.BAK %REPOROOT%\src\Microsoft.SqlTools.Hosting\Microsoft.SqlTools.Hosting.csproj
|
||||
DEL %REPOROOT%\src\Microsoft.SqlTools.Hosting\Microsoft.SqlTools.Hosting.csproj.BAK
|
||||
COPY /Y %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider\Microsoft.SqlTools.ResourceProvider.csproj.BAK %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider\Microsoft.SqlTools.ResourceProvider.csproj
|
||||
DEL %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider\Microsoft.SqlTools.ResourceProvider.csproj.BAK
|
||||
COPY /Y %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider.Core\Microsoft.SqlTools.ResourceProvider.Core.csproj.BAK %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider.Core\Microsoft.SqlTools.ResourceProvider.Core.csproj
|
||||
DEL %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider.Core\Microsoft.SqlTools.ResourceProvider.Core.csproj.BAK
|
||||
COPY /Y %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider.DefaultImpl\Microsoft.SqlTools.ResourceProvider.DefaultImpl.csproj.BAK %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider.DefaultImpl\Microsoft.SqlTools.ResourceProvider.DefaultImpl.csproj
|
||||
DEL %REPOROOT%\src\Microsoft.SqlTools.ResourceProvider.DefaultImpl\Microsoft.SqlTools.ResourceProvider.DefaultImpl.csproj.BAK
|
||||
|
||||
EXIT
|
||||
|
||||
@@ -16,19 +16,21 @@
|
||||
<PackageReference Include="Microsoft.SqlServer.Smo" Version="140.2.6" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>../../bin/ref/Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Moq">
|
||||
<HintPath>../../bin/ref/Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Castle.Core">
|
||||
<HintPath>../../bin/ref/Castle.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<ProjectReference Include="../../src/Microsoft.SqlTools.Hosting/Microsoft.SqlTools.Hosting.csproj" />
|
||||
<ProjectReference Include="../../src/Microsoft.SqlTools.Credentials/Microsoft.SqlTools.Credentials.csproj" />
|
||||
<ProjectReference Include="../../src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj" />
|
||||
<ProjectReference Include="../../test/Microsoft.SqlTools.ServiceLayer.Test.Common/Microsoft.SqlTools.ServiceLayer.Test.Common.csproj" />
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>../../bin/ref/Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Moq">
|
||||
<HintPath>../../bin/ref/Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Castle.Core">
|
||||
<HintPath>../../bin/ref/Castle.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<ProjectReference Include="../../src/Microsoft.SqlTools.Hosting/Microsoft.SqlTools.Hosting.csproj" />
|
||||
<ProjectReference Include="../../src/Microsoft.SqlTools.Credentials/Microsoft.SqlTools.Credentials.csproj" />
|
||||
<ProjectReference Include="../../src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj" />
|
||||
<ProjectReference Include="../../src/Microsoft.SqlTools.ResourceProvider.Core/Microsoft.SqlTools.ResourceProvider.Core.csproj" />
|
||||
<ProjectReference Include="../../src/Microsoft.SqlTools.ResourceProvider.DefaultImpl/Microsoft.SqlTools.ResourceProvider.DefaultImpl.csproj" />
|
||||
<ProjectReference Include="../../test/Microsoft.SqlTools.ServiceLayer.Test.Common/Microsoft.SqlTools.ServiceLayer.Test.Common.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="obj\Debug\netcoreapp2.0\" />
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core;
|
||||
using Microsoft.SqlTools.ServiceLayer.UnitTests.ResourceProvider.Fakes;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ResourceProvider.Azure
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for AzureDatabaseDiscoveryProvider to verify getting azure databases using azure resource manager
|
||||
/// </summary>
|
||||
public class AzureDatabaseDiscoveryProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task GetShouldReturnDatabasesSuccessfully()
|
||||
{
|
||||
string databaseName1 = "server/db1";
|
||||
string databaseName2 = "db2";
|
||||
string databaseName3 = "server/";
|
||||
string databaseName4 = "/db4";
|
||||
List<string> databasesForSubscription = new List<string>()
|
||||
{
|
||||
databaseName1,
|
||||
databaseName2
|
||||
};
|
||||
|
||||
Dictionary<string, List<string>> subscriptionToDatabaseMap = new Dictionary<string, List<string>>();
|
||||
subscriptionToDatabaseMap.Add(Guid.NewGuid().ToString(), databasesForSubscription);
|
||||
subscriptionToDatabaseMap.Add(Guid.NewGuid().ToString(), new List<string>()
|
||||
{
|
||||
databaseName3,
|
||||
databaseName4,
|
||||
});
|
||||
|
||||
AzureDatabaseDiscoveryProvider databaseDiscoveryProvider = FakeDataFactory.CreateAzureDatabaseDiscoveryProvider(subscriptionToDatabaseMap);
|
||||
ServiceResponse<DatabaseInstanceInfo> response = await databaseDiscoveryProvider.GetDatabaseInstancesAsync(serverName: null, cancellationToken: new CancellationToken());
|
||||
List<DatabaseInstanceInfo> list = response.Data.ToList();
|
||||
Assert.NotNull(list);
|
||||
Assert.True(list.Any(x => x.Name == "db1" && x.ServerInstanceInfo.Name == "server"));
|
||||
Assert.False(list.Any(x => x.Name == "db2" && x.ServerInstanceInfo.Name == ""));
|
||||
Assert.True(list.Any(x => x.Name == "" && x.ServerInstanceInfo.Name == "server"));
|
||||
Assert.False(list.Any(x => x.Name == "db4" && x.ServerInstanceInfo.Name == ""));
|
||||
Assert.True(list.Count() == 2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetShouldReturnDatabasesEvenIfFailsForOneServer()
|
||||
{
|
||||
string databaseName1 = "server1/db1";
|
||||
string databaseName2 = "server1/db2";
|
||||
string databaseName3 = "error/db3";
|
||||
string databaseName4 = "server2/db4";
|
||||
List<string> databasesForSubscription = new List<string>()
|
||||
{
|
||||
databaseName1,
|
||||
databaseName2
|
||||
};
|
||||
|
||||
Dictionary<string, List<string>> subscriptionToDatabaseMap = new Dictionary<string, List<string>>();
|
||||
subscriptionToDatabaseMap.Add(Guid.NewGuid().ToString(), databasesForSubscription);
|
||||
subscriptionToDatabaseMap.Add(Guid.NewGuid().ToString(), new List<string>()
|
||||
{
|
||||
databaseName3,
|
||||
databaseName4,
|
||||
});
|
||||
|
||||
AzureDatabaseDiscoveryProvider databaseDiscoveryProvider = FakeDataFactory.CreateAzureDatabaseDiscoveryProvider(subscriptionToDatabaseMap);
|
||||
ServiceResponse<DatabaseInstanceInfo> response = await databaseDiscoveryProvider.GetDatabaseInstancesAsync(serverName: null, cancellationToken: new CancellationToken());
|
||||
List<DatabaseInstanceInfo> list = response.Data.ToList();
|
||||
Assert.NotNull(list);
|
||||
Assert.True(list.Any(x => x.Name == "db1" && x.ServerInstanceInfo.Name == "server1"));
|
||||
Assert.True(list.Any(x => x.Name == "db2" && x.ServerInstanceInfo.Name == "server1"));
|
||||
Assert.True(list.Any(x => x.Name == "db4" && x.ServerInstanceInfo.Name == "server2"));
|
||||
Assert.False(list.Any(x => x.Name == "db3" && x.ServerInstanceInfo.Name == "error"));
|
||||
Assert.True(list.Count() == 3);
|
||||
Assert.NotNull(response.Errors);
|
||||
Assert.True(response.Errors.Count() == 1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetShouldReturnDatabasesFromCacheIfGetCalledTwice()
|
||||
{
|
||||
Dictionary<string, List<string>> subscriptionToDatabaseMap = CreateSubscriptonMap(2);
|
||||
AddDatabases(subscriptionToDatabaseMap, 3);
|
||||
|
||||
AzureDatabaseDiscoveryProvider databaseDiscoveryProvider = FakeDataFactory.CreateAzureDatabaseDiscoveryProvider(subscriptionToDatabaseMap);
|
||||
ServiceResponse<DatabaseInstanceInfo> response = await databaseDiscoveryProvider.GetDatabaseInstancesAsync(serverName: null, cancellationToken: new CancellationToken());
|
||||
List<DatabaseInstanceInfo> list = response.Data.ToList();
|
||||
ValidateResult(subscriptionToDatabaseMap, list);
|
||||
|
||||
Dictionary<string, List<string>> subscriptionToDatabaseMap2 = CopySubscriptonMap(subscriptionToDatabaseMap);
|
||||
AddDatabases(subscriptionToDatabaseMap2, 5);
|
||||
AzureTestContext testContext = new AzureTestContext(subscriptionToDatabaseMap2);
|
||||
databaseDiscoveryProvider.AccountManager = testContext.AzureAccountManager;
|
||||
databaseDiscoveryProvider.AzureResourceManager = testContext.AzureResourceManager;
|
||||
response = await databaseDiscoveryProvider.GetDatabaseInstancesAsync(serverName: null, cancellationToken: new CancellationToken());
|
||||
list = response.Data.ToList();
|
||||
//the provider should get the databases from cache for the list should match the first created data
|
||||
ValidateResult(subscriptionToDatabaseMap, list);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetShouldReturnDatabasesFromServiceIfGetCalledTwiceButRefreshed()
|
||||
{
|
||||
Dictionary<string, List<string>> subscriptionToDatabaseMap = CreateSubscriptonMap(2);
|
||||
AddDatabases(subscriptionToDatabaseMap, 3);
|
||||
|
||||
AzureDatabaseDiscoveryProvider databaseDiscoveryProvider = FakeDataFactory.CreateAzureDatabaseDiscoveryProvider(subscriptionToDatabaseMap);
|
||||
ServiceResponse<DatabaseInstanceInfo> response = await databaseDiscoveryProvider.GetDatabaseInstancesAsync(serverName: null, cancellationToken: new CancellationToken());
|
||||
List<DatabaseInstanceInfo> list = response.Data.ToList();
|
||||
ValidateResult(subscriptionToDatabaseMap, list);
|
||||
|
||||
Dictionary<string, List<string>> subscriptionToDatabaseMap2 = CopySubscriptonMap(subscriptionToDatabaseMap);
|
||||
AddDatabases(subscriptionToDatabaseMap2, 5);
|
||||
AzureTestContext testContext = new AzureTestContext(subscriptionToDatabaseMap2);
|
||||
databaseDiscoveryProvider.AccountManager = testContext.AzureAccountManager;
|
||||
databaseDiscoveryProvider.AzureResourceManager = testContext.AzureResourceManager;
|
||||
await databaseDiscoveryProvider.ClearCacheAsync();
|
||||
response = await databaseDiscoveryProvider.GetDatabaseInstancesAsync(serverName: null, cancellationToken: new CancellationToken());
|
||||
list = response.Data.ToList();
|
||||
//the provider should get the databases from cache for the list should match the first created data
|
||||
ValidateResult(subscriptionToDatabaseMap2, list);
|
||||
}
|
||||
|
||||
private void ValidateResult(Dictionary<string, List<string>> subscriptionToDatabaseMap, List<DatabaseInstanceInfo> result)
|
||||
{
|
||||
Assert.NotNull(result);
|
||||
int numberOfDatabases = 0;
|
||||
foreach (KeyValuePair<string, List<string>> item in subscriptionToDatabaseMap)
|
||||
{
|
||||
numberOfDatabases += item.Value.Count;
|
||||
foreach (string databaseFullName in item.Value)
|
||||
{
|
||||
string serverName = AzureTestContext.GetServerName(databaseFullName);
|
||||
string databaseName = databaseFullName.Replace(serverName + @"/", "");
|
||||
Assert.True(result.Any(x => x.Name == databaseName && x.ServerInstanceInfo.Name == serverName));
|
||||
}
|
||||
}
|
||||
Assert.True(result.Count() == numberOfDatabases);
|
||||
}
|
||||
|
||||
private void AddDatabases(Dictionary<string, List<string>> subscriptionToDatabaseMap, int numberOfDatabases)
|
||||
{
|
||||
foreach (string key in subscriptionToDatabaseMap.Keys.ToList())
|
||||
{
|
||||
List<string> databases = CreateDatabases(numberOfDatabases);
|
||||
subscriptionToDatabaseMap[key] = databases;
|
||||
}
|
||||
}
|
||||
|
||||
private List<string> CreateDatabases(int numberOfDatabases)
|
||||
{
|
||||
List<string> databases = new List<string>();
|
||||
for (int j = 0; j < numberOfDatabases; j++)
|
||||
{
|
||||
databases.Add(string.Format(CultureInfo.InvariantCulture, @"{0}/{1}", Guid.NewGuid().ToString(), Guid.NewGuid().ToString()));
|
||||
}
|
||||
return databases;
|
||||
}
|
||||
|
||||
private Dictionary<string, List<string>> CreateSubscriptonMap(int numberOfSubscriptions)
|
||||
{
|
||||
Dictionary<string, List<string>> subscriptionToDatabaseMap = new Dictionary<string, List<string>>();
|
||||
for (int i = 0; i < numberOfSubscriptions; i++)
|
||||
{
|
||||
string id = Guid.NewGuid().ToString();
|
||||
subscriptionToDatabaseMap.Add(id, null);
|
||||
}
|
||||
return subscriptionToDatabaseMap;
|
||||
}
|
||||
|
||||
private Dictionary<string, List<string>> CopySubscriptonMap(Dictionary<string, List<string>> subscriptionToDatabaseMap)
|
||||
{
|
||||
Dictionary<string, List<string>> subscriptionToDatabaseMapCopy = new Dictionary<string, List<string>>();
|
||||
foreach (KeyValuePair<string, List<string>> pair in subscriptionToDatabaseMap)
|
||||
{
|
||||
subscriptionToDatabaseMapCopy.Add(pair.Key, null);
|
||||
}
|
||||
return subscriptionToDatabaseMapCopy;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetShouldReturnEmptyGivenNotSubscriptionFound()
|
||||
{
|
||||
Dictionary<string, List<string>> subscriptionToDatabaseMap = new Dictionary<string, List<string>>();
|
||||
|
||||
AzureDatabaseDiscoveryProvider databaseDiscoveryProvider =
|
||||
FakeDataFactory.CreateAzureDatabaseDiscoveryProvider(subscriptionToDatabaseMap);
|
||||
ServiceResponse<DatabaseInstanceInfo> response =
|
||||
await databaseDiscoveryProvider.GetDatabaseInstancesAsync(serverName: null, cancellationToken: new CancellationToken());
|
||||
Assert.NotNull(response);
|
||||
Assert.NotNull(response.Data);
|
||||
Assert.False(response.Data.Any());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core;
|
||||
using Microsoft.SqlTools.ServiceLayer.UnitTests.ResourceProvider.Fakes;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ResourceProvider.Azure
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for AzureServerDiscoveryProvider to verify getting azure servers using azure resource manager
|
||||
/// </summary>
|
||||
public class AzureSqlServerDiscoveryProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task GetShouldReturnServersSuccessfully()
|
||||
{
|
||||
string serverName = "server";
|
||||
List<string> serversForSubscription = new List<string>()
|
||||
{
|
||||
Guid.NewGuid().ToString(),
|
||||
serverName
|
||||
};
|
||||
|
||||
Dictionary<string, List<string>> subscriptionToDatabaseMap = new Dictionary<string, List<string>>();
|
||||
subscriptionToDatabaseMap.Add(Guid.NewGuid().ToString(), serversForSubscription);
|
||||
subscriptionToDatabaseMap.Add(Guid.NewGuid().ToString(), new List<string>()
|
||||
{
|
||||
Guid.NewGuid().ToString(),
|
||||
Guid.NewGuid().ToString(),
|
||||
});
|
||||
|
||||
AzureSqlServerDiscoveryProvider discoveryProvider =
|
||||
FakeDataFactory.CreateAzureServerDiscoveryProvider(subscriptionToDatabaseMap);
|
||||
ServiceResponse<ServerInstanceInfo> response = await discoveryProvider.GetServerInstancesAsync();
|
||||
IEnumerable<ServerInstanceInfo> servers = response.Data;
|
||||
Assert.NotNull(servers);
|
||||
Assert.True(servers.Any(x => x.Name == serverName));
|
||||
Assert.True(servers.Count() == 4);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetShouldReturnEmptyGivenNotSubscriptionFound()
|
||||
{
|
||||
Dictionary<string, List<string>> subscriptionToDatabaseMap = new Dictionary<string, List<string>>();
|
||||
|
||||
AzureSqlServerDiscoveryProvider discoveryProvider =
|
||||
FakeDataFactory.CreateAzureServerDiscoveryProvider(subscriptionToDatabaseMap);
|
||||
ServiceResponse<ServerInstanceInfo> response = await discoveryProvider.GetServerInstancesAsync();
|
||||
IEnumerable<ServerInstanceInfo> servers = response.Data;
|
||||
Assert.NotNull(servers);
|
||||
Assert.False(servers.Any());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.SqlTools.ResourceProvider.DefaultImpl;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ResourceProvider.Azure
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for AzureSubscriptionContextWrapper to verify the wrapper on azure subscription class
|
||||
/// </summary>
|
||||
public class AzureSubscriptionContextTest
|
||||
{
|
||||
[Fact]
|
||||
public void SubscriptionNameShouldReturnNullGivenNullSubscription()
|
||||
{
|
||||
AzureSubscriptionContext subscriptionContext = new AzureSubscriptionContext(null);
|
||||
Assert.True(subscriptionContext.SubscriptionName == String.Empty);
|
||||
Assert.True(subscriptionContext.Subscription == null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SubscriptionNameShouldReturnCorrectValueGivenValidSubscription()
|
||||
{
|
||||
string name = Guid.NewGuid().ToString();
|
||||
|
||||
AzureSubscriptionContext subscriptionContext = new AzureSubscriptionContext(new AzureSubscriptionIdentifier(null, name, null));
|
||||
Assert.True(subscriptionContext.SubscriptionName == name);
|
||||
Assert.True(subscriptionContext.Subscription != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Management.Sql.Models;
|
||||
using Microsoft.Rest;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Authentication;
|
||||
using Microsoft.SqlTools.ResourceProvider.DefaultImpl;
|
||||
using Moq;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ResourceProvider.Azure
|
||||
{
|
||||
/// <summary>
|
||||
/// A container to create test data and mock classes to test azure services and providers
|
||||
/// </summary>
|
||||
internal class AzureTestContext
|
||||
{
|
||||
public AzureTestContext(Dictionary<string, List<string>> subscriptionToDatabaseMap)
|
||||
{
|
||||
AzureAccountManagerMock = new Mock<IAzureAuthenticationManager>();
|
||||
List<IAzureUserAccountSubscriptionContext> accountSubscriptions = new List
|
||||
<IAzureUserAccountSubscriptionContext>();
|
||||
AzureResourceManagerMock = new Mock<IAzureResourceManager>();
|
||||
|
||||
foreach (string subscriptionName in subscriptionToDatabaseMap.Keys)
|
||||
{
|
||||
var azureAccount = new AzureUserAccount();
|
||||
AzureSubscriptionIdentifier subId = new AzureSubscriptionIdentifier(azureAccount, subscriptionName, null);
|
||||
var subscription = new AzureUserAccountSubscriptionContext(subId, new TokenCredentials("dummy"));
|
||||
accountSubscriptions.Add(subscription);
|
||||
|
||||
var sessionMock = new Mock<IAzureResourceManagementSession>();
|
||||
IAzureResourceManagementSession session = sessionMock.Object;
|
||||
sessionMock.Setup(x => x.SubscriptionContext).Returns(subscription);
|
||||
AzureResourceManagerMock.Setup(x => x.CreateSessionAsync(subscription)).Returns(Task.FromResult(session));
|
||||
MockServersAndDatabases(subscriptionToDatabaseMap[subscriptionName], session);
|
||||
}
|
||||
AzureAccountManagerMock.Setup(x => x.GetSelectedSubscriptionsAsync()).Returns
|
||||
(Task.FromResult(accountSubscriptions as IEnumerable<IAzureUserAccountSubscriptionContext>));
|
||||
}
|
||||
|
||||
private void MockServersAndDatabases(List<string> resourceNames, IAzureResourceManagementSession session)
|
||||
{
|
||||
IEnumerable<IAzureResource> azureResources = resourceNames.Select(
|
||||
x => new AzureResourceWrapper(new TrackedResource(Guid.NewGuid().ToString(), "id", x, "type")) { ResourceGroupName = Guid.NewGuid().ToString()}
|
||||
).ToList();
|
||||
|
||||
List<IAzureSqlServerResource> servers = new List<IAzureSqlServerResource>();
|
||||
foreach (var azureResourceWrapper in azureResources.ToList())
|
||||
{
|
||||
var serverName = GetServerName(azureResourceWrapper.Name);
|
||||
if (string.IsNullOrEmpty(serverName) || servers.Any(x => x.Name == serverName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var databases = azureResources.Where(x => x.Name.StartsWith(serverName + "/"));
|
||||
if (serverName.Equals("error", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
AzureResourceManagerMock.Setup(x => x.GetAzureDatabasesAsync(session, azureResourceWrapper.ResourceGroupName, serverName))
|
||||
.Throws(new ApplicationException(serverName));
|
||||
}
|
||||
else
|
||||
{
|
||||
AzureResourceManagerMock.Setup(x => x.GetAzureDatabasesAsync(session, azureResourceWrapper.ResourceGroupName, serverName))
|
||||
.Returns(Task.FromResult(databases));
|
||||
}
|
||||
|
||||
Server azureSqlServer = new Server(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), serverName, null, null, null, null, null, null, null, null, fullyQualifiedDomainName: serverName + ".database.windows.net");
|
||||
servers.Add(new SqlAzureResource(azureSqlServer)
|
||||
{
|
||||
ResourceGroupName = azureResourceWrapper.ResourceGroupName
|
||||
});
|
||||
}
|
||||
AzureResourceManagerMock.Setup(x => x.GetSqlServerAzureResourcesAsync(session))
|
||||
.Returns(Task.FromResult(servers as IEnumerable<IAzureSqlServerResource>));
|
||||
}
|
||||
|
||||
internal static string GetServerName(string name)
|
||||
{
|
||||
string azureResourceName = name;
|
||||
int separatorIndex = azureResourceName.IndexOf("/", StringComparison.OrdinalIgnoreCase);
|
||||
if (separatorIndex >= 0)
|
||||
{
|
||||
return azureResourceName.Substring(0, separatorIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
return azureResourceName;
|
||||
}
|
||||
}
|
||||
|
||||
public Mock<IAzureAuthenticationManager> AzureAccountManagerMock
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public IAzureAuthenticationManager AzureAccountManager
|
||||
{
|
||||
get { return AzureAccountManagerMock.Object; }
|
||||
}
|
||||
|
||||
public IAzureResourceManager AzureResourceManager
|
||||
{
|
||||
get { return AzureResourceManagerMock.Object; }
|
||||
}
|
||||
|
||||
public Mock<IAzureResourceManager> AzureResourceManagerMock
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Authentication;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Extensibility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ResourceProvider.Azure
|
||||
{
|
||||
/// <summary>
|
||||
/// A fake server discovery class
|
||||
/// </summary>
|
||||
[Exportable(ServerTypes.SqlServer, Categories.Azure
|
||||
, typeof(IServerDiscoveryProvider),
|
||||
"Microsoft.SqlTools.ServiceLayer.UnitTests.ResourceProvider.Azure.FakeAzureServerDiscoveryProvider", 1)]
|
||||
public class FakeAzureServerDiscoveryProvider : ExportableBase, IServerDiscoveryProvider, ISecureService
|
||||
{
|
||||
public FakeAzureServerDiscoveryProvider()
|
||||
{
|
||||
Metadata = new ExportableMetadata(ServerTypes.SqlServer, Categories.Azure,
|
||||
"Microsoft.SqlTools.ServiceLayer.UnitTests.ResourceProvider.Azure.FakeAzureServerDiscoveryProvider", 1);
|
||||
}
|
||||
public Task<ServiceResponse<ServerInstanceInfo>> GetServerInstancesAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IAccountManager AccountManager
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This should always return null otherwise there's going to be a infinite loop
|
||||
/// </summary>
|
||||
public IServerDiscoveryProvider ServerDiscoveryProvider
|
||||
{
|
||||
get
|
||||
{
|
||||
return GetService<IServerDiscoveryProvider>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
// TODO Ideally would reenable these but using ExtensionServiceProvider
|
||||
|
||||
//using System;
|
||||
//using System.Collections.Generic;
|
||||
//using System.Linq;
|
||||
//using System.Threading.Tasks;
|
||||
//using Microsoft.SqlTools.ResourceProvider.Core;
|
||||
//using Microsoft.SqlTools.ResourceProvider.Core.Extensibility;
|
||||
//using Microsoft.SqlTools.ServiceLayer.UnitTests.ResourceProvider.Fakes;
|
||||
//using Moq;
|
||||
//using Xunit;
|
||||
|
||||
//namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ResourceProvider
|
||||
//{
|
||||
// /// <summary>
|
||||
// /// Tests for DependencyManager to verify the services and providers can be created given different types of catalogs
|
||||
// /// </summary>
|
||||
// public class DependencyManagerTest
|
||||
// {
|
||||
// private ExtensionProperties _serviceProperties;
|
||||
// private DependencyManager _dependencyManager;
|
||||
// private IList<Lazy<IServerDiscoveryProvider, IExportableMetadata>> _providers;
|
||||
|
||||
// private readonly List<ServerInstanceInfo> _localSqlServers = new List<ServerInstanceInfo>()
|
||||
// {
|
||||
// new ServerInstanceInfo(),
|
||||
// new ServerInstanceInfo(),
|
||||
// };
|
||||
|
||||
// public DependencyManagerTest()
|
||||
// {
|
||||
// var provider1 = new Mock<IServerDiscoveryProvider>();
|
||||
// var provider2 = new Mock<IServerDiscoveryProvider>();
|
||||
// provider1.Setup(x => x.GetServerInstancesAsync()).Returns(Task.FromResult(new ServiceResponse<ServerInstanceInfo>(_localSqlServers.AsEnumerable())));
|
||||
// _providers = new List<Lazy<IServerDiscoveryProvider, IExportableMetadata>>()
|
||||
// {
|
||||
// new Lazy<IServerDiscoveryProvider, IExportableMetadata>(() => provider1.Object,
|
||||
// new ExportableAttribute("SqlServer", "Local", typeof(IServerDiscoveryProvider), Guid.NewGuid().ToString())),
|
||||
// new Lazy<IServerDiscoveryProvider, IExportableMetadata>(() => provider2.Object,
|
||||
// new ExportableAttribute("SqlServer", "Network", typeof(IServerDiscoveryProvider), Guid.NewGuid().ToString())),
|
||||
// };
|
||||
|
||||
// _serviceProperties = FakeDataFactory.CreateServiceProperties(_providers);
|
||||
// _dependencyManager = new DependencyManager(_serviceProperties);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void GetShouldReturnProvidersFromTheCatalog()
|
||||
// {
|
||||
// IEnumerable<ExportableDescriptor<IServerDiscoveryProvider>> providers =
|
||||
// _dependencyManager.GetServiceDescriptors<IServerDiscoveryProvider>();
|
||||
// Assert.NotNull(providers);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void GetShouldReturnEmptyListGivenInvalidCategory()
|
||||
// {
|
||||
// Assert.False(_dependencyManager.GetServiceDescriptors<IServerDiscoveryProvider>(new ServerDefinition(null, "invalid category")).Any());
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void GetShouldReturnEmptyListGivenInvalidServerType()
|
||||
// {
|
||||
// Assert.False(_dependencyManager.GetServiceDescriptors<IServerDiscoveryProvider>(new ServerDefinition("invalid server type", null)).Any());
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void GetShouldReturnAllProvidersGivenNoParameter()
|
||||
// {
|
||||
// IEnumerable<ExportableDescriptor<IServerDiscoveryProvider>> providers =
|
||||
// _dependencyManager.GetServiceDescriptors<IServerDiscoveryProvider>();
|
||||
// Assert.NotNull(providers);
|
||||
// Assert.True(providers.Count() == _providers.Count());
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void GetShouldReturnProvidersGivenServerType()
|
||||
// {
|
||||
// var serverType = "sqlServer";
|
||||
// IEnumerable<ExportableDescriptor<IServerDiscoveryProvider>> providers =
|
||||
// _dependencyManager.GetServiceDescriptors<IServerDiscoveryProvider>(new ServerDefinition(serverType, null));
|
||||
// Assert.NotNull(providers);
|
||||
// Assert.True(providers.Any());
|
||||
// Assert.True(providers.Count() == _providers.Count(x => x.Metadata.ServerType.Equals(serverType, StringComparison.OrdinalIgnoreCase)));
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void GetShouldReturnProvidersGivenCategory()
|
||||
// {
|
||||
// IEnumerable<ExportableDescriptor<IServerDiscoveryProvider>> providers =
|
||||
// _dependencyManager.GetServiceDescriptors<IServerDiscoveryProvider>(new ServerDefinition(null, "local"));
|
||||
// Assert.NotNull(providers);
|
||||
// Assert.True(providers.Count() == 1);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void GetShouldReturnProviderForEmptyCategoryGivenEmptyCategory()
|
||||
// {
|
||||
// // Given choice of 2 providers, one with empty category and other with specified one
|
||||
|
||||
// IServerDiscoveryProvider provider1 = new Mock<IServerDiscoveryProvider>();
|
||||
// IServerDiscoveryProvider provider2 = new Mock<IServerDiscoveryProvider>();
|
||||
// provider1.Setup(x => x.GetServerInstancesAsync()).Returns(Task.FromResult(new ServiceResponse<ServerInstanceInfo>(_localSqlServers.AsEnumerable())));
|
||||
// var providers = new List<Lazy<IServerDiscoveryProvider, IExportableMetadata>>()
|
||||
// {
|
||||
// new Lazy<IServerDiscoveryProvider, IExportableMetadata>(() => provider1,
|
||||
// new ExportableAttribute("SqlServer", "Azure", typeof(IServerDiscoveryProvider), Guid.NewGuid().ToString())),
|
||||
// new Lazy<IServerDiscoveryProvider, IExportableMetadata>(() => provider2,
|
||||
// new ExportableAttribute("SqlServer", "", typeof(IServerDiscoveryProvider), Guid.NewGuid().ToString())),
|
||||
// };
|
||||
|
||||
// var serviceProperties = FakeDataFactory.CreateServiceProperties(providers);
|
||||
// var dependencyManager = new DependencyManager(serviceProperties);
|
||||
|
||||
// // When getting the correct descriptor
|
||||
|
||||
// IEnumerable<ExportableDescriptor<IServerDiscoveryProvider>> foundProviders =
|
||||
// dependencyManager.GetServiceDescriptors<IServerDiscoveryProvider>(new ServerDefinition("SqlServer", ""));
|
||||
|
||||
// // Then expect only the provider with the empty categorty to be returned
|
||||
// Assert.NotNull(foundProviders);
|
||||
// Assert.True(foundProviders.Count() == 1);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void GetShouldReturnProviderGivenServerTypeAndLocationWithValidProvider()
|
||||
// {
|
||||
// IEnumerable<ExportableDescriptor<IServerDiscoveryProvider>> providers =
|
||||
// _dependencyManager.GetServiceDescriptors<IServerDiscoveryProvider>(new ServerDefinition("SqlServer", "local"));
|
||||
// Assert.NotNull(providers);
|
||||
// Assert.True(providers.Count() == 1);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
|
||||
// public void GetShouldReturnTheServiceWithTheHighestPriorityIdMultipleFound()
|
||||
// {
|
||||
// IServerDiscoveryProvider expectedProvider = new Mock<IServerDiscoveryProvider>();
|
||||
// List<Lazy<IServerDiscoveryProvider, IExportableMetadata>> providers = new List<Lazy<IServerDiscoveryProvider, IExportableMetadata>>()
|
||||
// {
|
||||
// new Lazy<IServerDiscoveryProvider, IExportableMetadata>(() => new Mock<IServerDiscoveryProvider>(),
|
||||
// new ExportableAttribute("SqlServer", "Local", typeof(IServerDiscoveryProvider), Guid.NewGuid().ToString())),
|
||||
// new Lazy<IServerDiscoveryProvider, IExportableMetadata>(() => new Mock<IServerDiscoveryProvider>(),
|
||||
// new ExportableAttribute("SqlServer", "Network", typeof(IServerDiscoveryProvider), Guid.NewGuid().ToString(), 1)),
|
||||
// new Lazy<IServerDiscoveryProvider, IExportableMetadata>(() => expectedProvider,
|
||||
// new ExportableAttribute("SqlServer", "Network", typeof(IServerDiscoveryProvider), Guid.NewGuid().ToString(), 2))
|
||||
// };
|
||||
|
||||
// ExtensionProperties serviceProperties = FakeDataFactory.CreateServiceProperties(providers);
|
||||
// DependencyManager dependencyManager = new DependencyManager(serviceProperties);
|
||||
|
||||
// IEnumerable<ExportableDescriptor<IServerDiscoveryProvider>> descriptors =
|
||||
// dependencyManager.GetServiceDescriptors<IServerDiscoveryProvider>();
|
||||
// Assert.NotNull(descriptors);
|
||||
|
||||
// ExportableDescriptor<IServerDiscoveryProvider> descriptor = descriptors.FindMatchedDescriptor(new ServerDefinition("SqlServer", "network"));
|
||||
// Assert.NotNull(descriptor);
|
||||
// Assert.True(descriptor.Exportable == expectedProvider);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void GetShouldReturnTheServiceEvenIfTheServerTypeNotSet()
|
||||
// {
|
||||
// IServerDiscoveryProvider expectedProvider = new Mock<IServerDiscoveryProvider>();
|
||||
// List<Lazy<IServerDiscoveryProvider, IExportableMetadata>> providers = new List<Lazy<IServerDiscoveryProvider, IExportableMetadata>>()
|
||||
// {
|
||||
// new Lazy<IServerDiscoveryProvider, IExportableMetadata>(() => new Mock<IServerDiscoveryProvider>(),
|
||||
// new ExportableAttribute("SqlServer", "", typeof(IServerDiscoveryProvider), Guid.NewGuid().ToString())),
|
||||
// new Lazy<IServerDiscoveryProvider, IExportableMetadata>(() => new Mock<IServerDiscoveryProvider>(),
|
||||
// new ExportableAttribute("SqlServer", "Network", typeof(IServerDiscoveryProvider), Guid.NewGuid().ToString(), 1)),
|
||||
// new Lazy<IServerDiscoveryProvider, IExportableMetadata>(() => expectedProvider,
|
||||
// new ExportableAttribute("", "Network", typeof(IServerDiscoveryProvider), Guid.NewGuid().ToString(), 2))
|
||||
// };
|
||||
|
||||
// ExtensionProperties serviceProperties = FakeDataFactory.CreateServiceProperties(providers);
|
||||
// DependencyManager dependencyManager = new DependencyManager(serviceProperties);
|
||||
|
||||
// IEnumerable<ExportableDescriptor<IServerDiscoveryProvider>> descriptors =
|
||||
// dependencyManager.GetServiceDescriptors<IServerDiscoveryProvider>();
|
||||
// Assert.NotNull(descriptors);
|
||||
|
||||
// ExportableDescriptor<IServerDiscoveryProvider> descriptor = descriptors.FindMatchedDescriptor(new ServerDefinition("", "network"));
|
||||
// Assert.NotNull(descriptor);
|
||||
// Assert.True(descriptor.Exportable == expectedProvider);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void GetShouldReturnTheServiceThatMatchedExactlyIfServerTypeSpecified()
|
||||
// {
|
||||
// IServerDiscoveryProvider expectedProvider = new Mock<IServerDiscoveryProvider>();
|
||||
// List<Lazy<IServerDiscoveryProvider, IExportableMetadata>> providers = new List<Lazy<IServerDiscoveryProvider, IExportableMetadata>>()
|
||||
// {
|
||||
// new Lazy<IServerDiscoveryProvider, IExportableMetadata>(() => new Mock<IServerDiscoveryProvider>(),
|
||||
// new ExportableAttribute("SqlServer", "", typeof(IServerDiscoveryProvider), Guid.NewGuid().ToString())),
|
||||
// new Lazy<IServerDiscoveryProvider, IExportableMetadata>(() => expectedProvider,
|
||||
// new ExportableAttribute("SqlServer", "Network", typeof(IServerDiscoveryProvider), Guid.NewGuid().ToString(), 1)),
|
||||
// new Lazy<IServerDiscoveryProvider, IExportableMetadata>(() => new Mock<IServerDiscoveryProvider>(),
|
||||
// new ExportableAttribute("", "Network", typeof(IServerDiscoveryProvider), Guid.NewGuid().ToString(), 2))
|
||||
// };
|
||||
|
||||
// ExtensionProperties serviceProperties = FakeDataFactory.CreateServiceProperties(providers);
|
||||
// DependencyManager dependencyManager = new DependencyManager(serviceProperties);
|
||||
|
||||
// IEnumerable<ExportableDescriptor<IServerDiscoveryProvider>> descriptors =
|
||||
// dependencyManager.GetServiceDescriptors<IServerDiscoveryProvider>();
|
||||
// Assert.NotNull(descriptors);
|
||||
|
||||
// ExportableDescriptor<IServerDiscoveryProvider> descriptor = descriptors.FindMatchedDescriptor(new ServerDefinition("SqlServer", "network"));
|
||||
// Assert.NotNull(descriptor);
|
||||
// Assert.True(descriptor.Exportable == expectedProvider);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void GetShouldReturnTheServiceThatMatchedExactlyIfCategorySpecified()
|
||||
// {
|
||||
// IServerDiscoveryProvider expectedProvider = new Mock<IServerDiscoveryProvider>();
|
||||
// List<Lazy<IServerDiscoveryProvider, IExportableMetadata>> providers = new List<Lazy<IServerDiscoveryProvider, IExportableMetadata>>()
|
||||
// {
|
||||
// new Lazy<IServerDiscoveryProvider, IExportableMetadata>(() => new Mock<IServerDiscoveryProvider>(),
|
||||
// new ExportableAttribute("SqlServer", "", typeof(IServerDiscoveryProvider), Guid.NewGuid().ToString())),
|
||||
// new Lazy<IServerDiscoveryProvider, IExportableMetadata>(() => expectedProvider,
|
||||
// new ExportableAttribute("SqlServer", "Network", typeof(IServerDiscoveryProvider), Guid.NewGuid().ToString(), 1)),
|
||||
// new Lazy<IServerDiscoveryProvider, IExportableMetadata>(() => new Mock<IServerDiscoveryProvider>(),
|
||||
// new ExportableAttribute("", "Network", typeof(IServerDiscoveryProvider), Guid.NewGuid().ToString(), 2))
|
||||
// };
|
||||
|
||||
// ExtensionProperties serviceProperties = FakeDataFactory.CreateServiceProperties(providers);
|
||||
// DependencyManager dependencyManager = new DependencyManager(serviceProperties);
|
||||
|
||||
// IEnumerable<ExportableDescriptor<IServerDiscoveryProvider>> descriptors =
|
||||
// dependencyManager.GetServiceDescriptors<IServerDiscoveryProvider>();
|
||||
// Assert.NotNull(descriptors);
|
||||
|
||||
// ExportableDescriptor<IServerDiscoveryProvider> descriptor = descriptors.FindMatchedDescriptor(new ServerDefinition("SqlServer", "network"));
|
||||
// Assert.NotNull(descriptor);
|
||||
// Assert.True(descriptor.Exportable == expectedProvider);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
|
||||
// public void GetShouldReturnTheServiceEvenIfTheCategoryNotSet()
|
||||
// {
|
||||
// IServerDiscoveryProvider expectedProvider = new Mock<IServerDiscoveryProvider>();
|
||||
// List<Lazy<IServerDiscoveryProvider, IExportableMetadata>> providers = new List<Lazy<IServerDiscoveryProvider, IExportableMetadata>>()
|
||||
// {
|
||||
// new Lazy<IServerDiscoveryProvider, IExportableMetadata>(() => new Mock<IServerDiscoveryProvider>(),
|
||||
// new ExportableAttribute("SqlServer", "Local", typeof(IServerDiscoveryProvider), Guid.NewGuid().ToString())),
|
||||
// new Lazy<IServerDiscoveryProvider, IExportableMetadata>(() => new Mock<IServerDiscoveryProvider>(),
|
||||
// new ExportableAttribute("SqlServer", "Network", typeof(IServerDiscoveryProvider), Guid.NewGuid().ToString(), 1)),
|
||||
// new Lazy<IServerDiscoveryProvider, IExportableMetadata>(() => expectedProvider,
|
||||
// new ExportableAttribute("SqlServer", "", typeof(IServerDiscoveryProvider), Guid.NewGuid().ToString(), 2))
|
||||
// };
|
||||
|
||||
// ExtensionProperties serviceProperties = FakeDataFactory.CreateServiceProperties(providers);
|
||||
// DependencyManager dependencyManager = new DependencyManager(serviceProperties);
|
||||
|
||||
// IEnumerable<ExportableDescriptor<IServerDiscoveryProvider>> descriptors =
|
||||
// dependencyManager.GetServiceDescriptors<IServerDiscoveryProvider>();
|
||||
// Assert.NotNull(descriptors);
|
||||
|
||||
// ExportableDescriptor<IServerDiscoveryProvider> descriptor = descriptors.FindMatchedDescriptor(new ServerDefinition("SqlServer", ""));
|
||||
// Assert.NotNull(descriptor);
|
||||
// Assert.True(descriptor.Exportable == expectedProvider);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public void GetShouldReturnProvidersGivenServerTypeAndMoreThanOneLocation()
|
||||
// {
|
||||
// var serverType = "sqlServer";
|
||||
// IEnumerable<ExportableDescriptor<IServerDiscoveryProvider>> providers =
|
||||
// _dependencyManager.GetServiceDescriptors<IServerDiscoveryProvider>(new ServerDefinition(serverType, null));
|
||||
// Assert.NotNull(providers);
|
||||
// Assert.True(providers.Count() == _providers.Count(x => x.Metadata.ServerType.Equals(serverType, StringComparison.OrdinalIgnoreCase)));
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async Task ProviderCreatedByFactoryShouldReturnServersSuccessfully()
|
||||
// {
|
||||
// List<ServerInstanceInfo> expectedServers = _localSqlServers;
|
||||
// IEnumerable<ExportableDescriptor<IServerDiscoveryProvider>> providers =
|
||||
// _dependencyManager.GetServiceDescriptors<IServerDiscoveryProvider>(new ServerDefinition("SqlServer",
|
||||
// "local"));
|
||||
// ExportableDescriptor<IServerDiscoveryProvider> provider = providers.First();
|
||||
// Assert.NotNull(provider);
|
||||
// ServiceResponse<ServerInstanceInfo> result = await provider.Exportable.GetServerInstancesAsync();
|
||||
// var servers = result.Data;
|
||||
// Assert.NotNull(servers);
|
||||
// Assert.Equal(expectedServers, servers);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,72 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Data.Common;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ResourceProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for ExceptionUtil to verify the helper and extension methods
|
||||
/// </summary>
|
||||
public class ExceptionUtilTest
|
||||
{
|
||||
[Fact]
|
||||
public void IsSqlExceptionShouldReturnFalseGivenNullException()
|
||||
{
|
||||
Exception exception = null;
|
||||
bool expected = false;
|
||||
bool actual = exception.IsDbException();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsSqlExceptionShouldReturnFalseGivenNonSqlException()
|
||||
{
|
||||
Exception exception = new ApplicationException();
|
||||
bool expected = false;
|
||||
bool actual = exception.IsDbException();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsSqlExceptionShouldReturnFalseGivenNonSqlExceptionWithInternalException()
|
||||
{
|
||||
Exception exception = new ApplicationException("Exception message", new ServiceFailedException());
|
||||
bool expected = false;
|
||||
bool actual = exception.IsDbException();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsSqlExceptionShouldReturnTrueGivenSqlException()
|
||||
{
|
||||
Exception exception = CreateDbException();
|
||||
Assert.NotNull(exception);
|
||||
|
||||
bool expected = true;
|
||||
bool actual = exception.IsDbException();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsSqlExceptionShouldReturnTrueGivenExceptionWithInnerSqlException()
|
||||
{
|
||||
Exception exception = new ApplicationException("", CreateDbException());
|
||||
Assert.NotNull(exception);
|
||||
|
||||
bool expected = true;
|
||||
bool actual = exception.IsDbException();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
private Exception CreateDbException()
|
||||
{
|
||||
return new Mock<DbException>().Object;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.Extensibility;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Authentication;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Extensibility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ResourceProvider.Fakes
|
||||
{
|
||||
[Exportable(ServerTypes.SqlServer, Categories.Azure,
|
||||
typeof(IAccountManager),
|
||||
"Microsoft.SqlTools.ServiceLayer.UnitTests.ResourceProvider.Fakes.FakeAccountManager", 1)]
|
||||
public class FakeAccountManager : IAccountManager
|
||||
{
|
||||
public FakeAccountManager(IExportableMetadata metadata)
|
||||
{
|
||||
Metadata = metadata;
|
||||
}
|
||||
|
||||
public ITrace Trace { get; set; }
|
||||
public Task<bool> GetUserNeedsReauthenticationAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IUserAccount> AuthenticateAsync()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsCachExpired { get; private set; }
|
||||
public bool SessionIsCached { get; private set; }
|
||||
public void ResetSession()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IUserAccount> AddUserAccountAsync()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IUserAccount> SetCurrentAccountAsync(object account)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IUserAccount> SetCurrentAccountFromLoginDialogAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IUserAccount> GetCurrentAccountAsync()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public bool HasLoginDialog { get; }
|
||||
|
||||
public event EventHandler CurrentAccountChanged;
|
||||
|
||||
public IUserAccount SetCurrentAccount(object account)
|
||||
{
|
||||
if (CurrentAccountChanged != null)
|
||||
{
|
||||
CurrentAccountChanged(this, null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SetServiceProvider(IMultiServiceProvider provider)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IExportableMetadata Metadata { get; set; }
|
||||
public ExportableStatus Status { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.Extensibility;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Authentication;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Extensibility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ResourceProvider.Fakes
|
||||
{
|
||||
[Exportable("SqlServer", "Network",
|
||||
typeof(IAccountManager), "Microsoft.SqlTools.ServiceLayer.UnitTests.ResourceProvider.Fakes.FakeAccountManager2", 2)]
|
||||
public class FakeAccountManager2 : IAccountManager
|
||||
{
|
||||
public FakeAccountManager2(IExportableMetadata metadata)
|
||||
{
|
||||
Metadata = metadata;
|
||||
}
|
||||
|
||||
public ITrace Trace { get; set; }
|
||||
public Task<bool> GetUserNeedsReauthenticationAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IUserAccount> AuthenticateAsync()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsCachExpired { get; private set; }
|
||||
public bool SessionIsCached { get; private set; }
|
||||
public void ResetSession()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IUserAccount> AddUserAccountAsync()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IUserAccount> SetCurrentAccountAsync(object account)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IUserAccount> SetCurrentAccountFromLoginDialogAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IUserAccount> GetCurrentAccountAsync()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public bool HasLoginDialog { get; }
|
||||
|
||||
public event EventHandler CurrentAccountChanged;
|
||||
|
||||
public IUserAccount SetCurrentAccount(object account)
|
||||
{
|
||||
if (CurrentAccountChanged != null)
|
||||
{
|
||||
CurrentAccountChanged(this, null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SetServiceProvider(IMultiServiceProvider provider)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IExportableMetadata Metadata { get; set; }
|
||||
public ExportableStatus Status { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.SqlClient;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Extensibility;
|
||||
using Microsoft.SqlTools.ServiceLayer.UnitTests.ResourceProvider.Azure;
|
||||
using Moq;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ResourceProvider.Fakes
|
||||
{
|
||||
internal static class FakeDataFactory
|
||||
{
|
||||
//public static ExtensionProperties CreateServiceProperties(IList<Lazy<IServerDiscoveryProvider, IExportableMetadata>> exports)
|
||||
//{
|
||||
// FakeExportProvider fakeProvider = new FakeExportProvider(f => ((FakeInstanceExportDefinition)f).Instance);
|
||||
// foreach (var export in exports)
|
||||
// {
|
||||
// var metadata = new Dictionary<string, object>()
|
||||
// {
|
||||
// {"ServerType", export.Metadata.ServerType},
|
||||
// {"Category", export.Metadata.Category},
|
||||
// {"Id", export.Metadata.Id },
|
||||
// {"Priority", export.Metadata.Priority}
|
||||
// };
|
||||
|
||||
// var definition = new FakeInstanceExportDefinition(typeof(IServerDiscoveryProvider), export.Value, metadata);
|
||||
// fakeProvider.AddExportDefinitions(definition);
|
||||
// }
|
||||
// var trace = new Mock<ITrace>();
|
||||
// trace.Setup(x => x.TraceEvent(It.IsAny<TraceEventType>(), It.IsAny<int>(), It.IsAny<string>(), It.IsAny<object[]>()))
|
||||
// .Returns(true);
|
||||
|
||||
// var metadata2 = new Dictionary<string, object>()
|
||||
// {
|
||||
// {"Id", Guid.NewGuid().ToString()},
|
||||
// {"Priority", 0}
|
||||
// };
|
||||
// var traceDefinition = new FakeInstanceExportDefinition(typeof(ITrace), trace, metadata2);
|
||||
// fakeProvider.AddExportDefinitions(traceDefinition);
|
||||
|
||||
// ExtensionProperties serviceProperties = new ExtensionProperties(false);
|
||||
// serviceProperties.Providers = new ExportProvider[] { fakeProvider };
|
||||
// TypeCatalog typeCatalog = new TypeCatalog(typeof(FakeTrace));
|
||||
// serviceProperties.AddCatalog(typeCatalog);
|
||||
// return serviceProperties;
|
||||
//}
|
||||
|
||||
internal static AzureDatabaseDiscoveryProvider CreateAzureDatabaseDiscoveryProvider(
|
||||
Dictionary<string, List<string>> subscriptionToDatabaseMap)
|
||||
{
|
||||
AzureTestContext testContext = new AzureTestContext(subscriptionToDatabaseMap);
|
||||
|
||||
AzureDatabaseDiscoveryProvider databaseDiscoveryProvider = new AzureDatabaseDiscoveryProvider();
|
||||
databaseDiscoveryProvider.AccountManager = testContext.AzureAccountManager;
|
||||
databaseDiscoveryProvider.AzureResourceManager = testContext.AzureResourceManager;
|
||||
|
||||
return databaseDiscoveryProvider;
|
||||
}
|
||||
|
||||
internal static AzureSqlServerDiscoveryProvider CreateAzureServerDiscoveryProvider(Dictionary<string, List<string>> subscriptionToDatabaseMap)
|
||||
{
|
||||
AzureTestContext testContext = new AzureTestContext(subscriptionToDatabaseMap);
|
||||
|
||||
AzureSqlServerDiscoveryProvider serverDiscoveryProvider = new AzureSqlServerDiscoveryProvider();
|
||||
serverDiscoveryProvider.AccountManager = testContext.AzureAccountManager;
|
||||
serverDiscoveryProvider.AzureResourceManager = testContext.AzureResourceManager;
|
||||
|
||||
return serverDiscoveryProvider;
|
||||
}
|
||||
|
||||
//internal static IDependencyManager AddDependencyProvider<T>(T provider,
|
||||
// ServerDefinition serverDefinition, IDependencyManager existingDependencyManager = null)
|
||||
// where T : IExportable
|
||||
//{
|
||||
// return AddDependencyProviders(new Dictionary<T,ServerDefinition>() {{ provider, serverDefinition}}, existingDependencyManager);
|
||||
//}
|
||||
|
||||
//internal static IDependencyManager AddDependencyProviders<T>(Dictionary<T, ServerDefinition> providers, IDependencyManager existingDependencyManager = null)
|
||||
// where T : IExportable
|
||||
//{
|
||||
// IDependencyManager dependencyManager = existingDependencyManager ?? new Mock<IDependencyManager>();
|
||||
|
||||
// IEnumerable<ExportableDescriptor<T>> exportableDescriptors =
|
||||
// providers.Select(x => new ExportableDescriptorImpl<T>(
|
||||
// new ExtensionDescriptor<T, IExportableMetadata>(
|
||||
// new Lazy<T, IExportableMetadata>(
|
||||
// () => x.Key,
|
||||
// new ExportableAttribute(x.Value.ServerType, x.Value.Category,
|
||||
// typeof (T), Guid.NewGuid().ToString())))));
|
||||
|
||||
// dependencyManager.Setup(x => x.GetServiceDescriptors<T>()).Returns(exportableDescriptors);
|
||||
|
||||
// return dependencyManager;
|
||||
//}
|
||||
|
||||
internal static ServiceResponse<ServerInstanceInfo> CreateServerInstanceResponse(int numberOfServers, ServerDefinition serverDefinition, Exception exception = null)
|
||||
{
|
||||
List<ServerInstanceInfo> servers = new List<ServerInstanceInfo>();
|
||||
for (int i = 0; i < numberOfServers; i++)
|
||||
{
|
||||
servers.Add(new ServerInstanceInfo(serverDefinition)
|
||||
{
|
||||
Name = Guid.NewGuid().ToString(),
|
||||
FullyQualifiedDomainName = Guid.NewGuid().ToString()
|
||||
});
|
||||
}
|
||||
ServiceResponse<ServerInstanceInfo> response;
|
||||
if (exception != null)
|
||||
{
|
||||
response = new ServiceResponse<ServerInstanceInfo>(servers, new List<Exception> { exception });
|
||||
}
|
||||
else
|
||||
{
|
||||
response = new ServiceResponse<ServerInstanceInfo>(servers);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
internal static ServiceResponse<DatabaseInstanceInfo> CreateDatabaseInstanceResponse(int numberOfServers, ServerDefinition serverDefinition = null,
|
||||
string serverName = "", Exception exception = null)
|
||||
{
|
||||
serverDefinition = serverDefinition ?? ServerDefinition.Default;
|
||||
List<DatabaseInstanceInfo> databases = new List<DatabaseInstanceInfo>();
|
||||
for (int i = 0; i < numberOfServers; i++)
|
||||
{
|
||||
databases.Add(new DatabaseInstanceInfo(serverDefinition, serverName, Guid.NewGuid().ToString()));
|
||||
}
|
||||
ServiceResponse<DatabaseInstanceInfo> response;
|
||||
if (exception != null)
|
||||
{
|
||||
response = new ServiceResponse<DatabaseInstanceInfo>(databases, new List<Exception> { exception });
|
||||
}
|
||||
else
|
||||
{
|
||||
response = new ServiceResponse<DatabaseInstanceInfo>(databases);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
//internal static UIConnectionInfo CreateUiConnectionInfo(string baseDbName)
|
||||
//{
|
||||
// SqlConnectionStringBuilder connectionStringBuilder = CreateConnectionStringBuilder(baseDbName);
|
||||
// return CreateUiConnectionInfo(connectionStringBuilder);
|
||||
//}
|
||||
//internal static UIConnectionInfo CreateUiConnectionInfo(SqlConnectionStringBuilder connectionStringBuilder)
|
||||
//{
|
||||
// UIConnectionInfo ci = UIConnectionInfoUtil.GetUIConnectionInfoFromConnectionString(connectionStringBuilder.ConnectionString, (new SqlServerType()));
|
||||
// ci.PersistPassword = connectionStringBuilder.PersistSecurityInfo;
|
||||
// return ci;
|
||||
//}
|
||||
|
||||
|
||||
//internal static SqlConnectionStringBuilder CreateConnectionStringBuilder(string baseDbName)
|
||||
//{
|
||||
// return CreateConnectionStringBuilder(baseDbName, InstanceManager.DefaultSql2011);
|
||||
//}
|
||||
|
||||
|
||||
//internal static SqlConnectionStringBuilder CreateConnectionStringBuilder(string baseDbName, InstanceInfo dbInstance)
|
||||
//{
|
||||
// string dbName = ConnectionDialogHelper.CreateTestDatabase(baseDbName, dbInstance);
|
||||
// string dbConnectionString = dbInstance.BuildConnectionString(dbName);
|
||||
// SqlConnectionStringBuilder connectionStringBuilder = new SqlConnectionStringBuilder(dbConnectionString);
|
||||
// connectionStringBuilder.ApplicationName = Guid.NewGuid().ToString();
|
||||
// connectionStringBuilder.ConnectTimeout = 123;
|
||||
// connectionStringBuilder.Encrypt = true;
|
||||
// connectionStringBuilder.ApplicationIntent = ApplicationIntent.ReadWrite;
|
||||
// connectionStringBuilder.AsynchronousProcessing = true;
|
||||
// connectionStringBuilder.MaxPoolSize = 45;
|
||||
// connectionStringBuilder.MinPoolSize = 3;
|
||||
// connectionStringBuilder.PacketSize = 600;
|
||||
// connectionStringBuilder.Pooling = true;
|
||||
// connectionStringBuilder.TrustServerCertificate = false;
|
||||
// return connectionStringBuilder;
|
||||
//}
|
||||
|
||||
//internal static ConnectionInfo CreateConnectionInfo(string baseDbName, IEventsChannel eventsChannel = null)
|
||||
//{
|
||||
// ConnectionInfo connectionInfo = new ConnectionInfo(eventsChannel);
|
||||
// UIConnectionInfo uiConnectionInfo = CreateUiConnectionInfo(baseDbName);
|
||||
// connectionInfo.UpdateConnectionInfo(uiConnectionInfo);
|
||||
// return connectionInfo;
|
||||
//}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SqlTools.Extensibility;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core;
|
||||
using Microsoft.SqlTools.ResourceProvider.Core.Extensibility;
|
||||
|
||||
namespace Microsoft.SqlTools.ServiceLayer.UnitTests.ResourceProvider.Fakes
|
||||
{
|
||||
/// <summary>
|
||||
/// A fake database discovery class which generates db names for 5 seconds or until it gets canceled
|
||||
/// </summary>
|
||||
public class FakeDatabaseDiscoveryProvider : IDatabaseDiscoveryProvider
|
||||
{
|
||||
private TimeSpan _timeout = TimeSpan.FromSeconds(5);
|
||||
|
||||
public IExportableMetadata Metadata { get; set; }
|
||||
public ExportableStatus Status { get; }
|
||||
IExportableMetadata IExportable.Metadata { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
|
||||
ExportableStatus IExportable.Status => throw new NotImplementedException();
|
||||
|
||||
public Task<ServiceResponse<DatabaseInstanceInfo>> GetDatabaseInstancesAsync(string serverName, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
//public Task<ServiceResponse<DatabaseInstanceInfo>> GetDatabaseInstancesAsync(UIConnectionInfo uiConnectionInfo, CancellationToken cancellationToken)
|
||||
//{
|
||||
// return Task.Factory.StartNew(() => GetDatabaseInstances(uiConnectionInfo, cancellationToken), cancellationToken);
|
||||
//}
|
||||
|
||||
//private ServiceResponse<DatabaseInstanceInfo> GetDatabaseInstances(UIConnectionInfo uiConnectionInfo, CancellationToken cancellationToken)
|
||||
//{
|
||||
// List<DatabaseInstanceInfo> databases = new List<DatabaseInstanceInfo>();
|
||||
// DateTime startTime = DateTime.UtcNow;
|
||||
// while (!cancellationToken.IsCancellationRequested)
|
||||
// {
|
||||
// DateTime now = DateTime.UtcNow;
|
||||
// if (now.Subtract(startTime).TotalMilliseconds >= _timeout.TotalMilliseconds)
|
||||
// {
|
||||
// break;
|
||||
// }
|
||||
// databases.Add(new DatabaseInstanceInfo(ServerDefinition.Default, uiConnectionInfo.ServerName, uiConnectionInfo.ServerName + "" + Guid.NewGuid().ToString()));
|
||||
// }
|
||||
|
||||
// return new ServiceResponse<DatabaseInstanceInfo>(databases);
|
||||
//}
|
||||
|
||||
private static void TimerCallback(object state)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void OnDatabaseFound(DatabaseInstanceInfo databaseInfo)
|
||||
{
|
||||
if (DatabaseFound != null)
|
||||
{
|
||||
DatabaseFound(this, new DatabaseInfoEventArgs() { Database = databaseInfo });
|
||||
}
|
||||
}
|
||||
|
||||
public void SetServiceProvider(IMultiServiceProvider provider)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public event EventHandler<DatabaseInfoEventArgs> DatabaseFound;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user