mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-31 17:24:37 -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:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user