Adding support for database literal references for Dacpac and SqlProj references (#1858)

* fixing up some nullable spots

* Adding database literal support

* Adding tests and support for same database refs

* Fixing test

* merge laggard

* Split database reference tests up

* PR feedback

* Normalizing strings for cross-plat test passing

* Updating Projects nuget package

* Fixing up test
This commit is contained in:
Benjin Dubishar
2023-02-10 11:36:31 -08:00
committed by GitHub
parent 15250a9178
commit d0d67373d1
21 changed files with 293 additions and 99 deletions

View File

@@ -13,18 +13,12 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts
/// <summary>
/// Parameters for adding a Dacpac reference to a SQL project
/// </summary>
public class AddDacpacReferenceParams : AddDatabaseReferenceParams
public class AddDacpacReferenceParams : AddUserDatabaseReferenceParams
{
/// <summary>
/// Path to the .dacpac file
/// </summary>
public string DacpacPath { get; set; }
/// <summary>
/// SQLCMD variable name for specifying the other server this reference is to, if different from that of the current project.
/// If this is set, DatabaseVariable must also be set.
/// </summary>
public string? ServerVariable { get; set; }
}
public class AddDacpacReferenceRequest

View File

@@ -3,8 +3,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
namespace Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts
{
/// <summary>
@@ -18,8 +16,8 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts
public bool SuppressMissingDependencies { get; set; }
/// <summary>
/// SQLCMD variable name for specifying the other database this reference is to, if different from that of the current project
/// Literal name used to reference another database in the same server, if not using SQLCMD variables
/// </summary>
public string? DatabaseVariable { get; set; }
public string? DatabaseLiteral { get; set; }
}
}

View File

@@ -13,7 +13,7 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts
/// <summary>
/// Parameters for adding a reference to another SQL project
/// </summary>
public class AddSqlProjectReferenceParams : AddDatabaseReferenceParams
public class AddSqlProjectReferenceParams : AddUserDatabaseReferenceParams
{
/// <summary>
/// Path to the referenced .sqlproj file
@@ -23,13 +23,7 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts
/// <summary>
/// GUID for the referenced SQL project
/// </summary>
public string? ProjectGuid { get; set; }
/// <summary>
/// SQLCMD variable name for specifying the other server this reference is to, if different from that of the current project.
/// If this is set, DatabaseVariable must also be set.
/// </summary>
public string? ServerVariable { get; set; }
public string ProjectGuid { get; set; }
}

View File

@@ -3,8 +3,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using Microsoft.SqlServer.Dac.Projects;
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.ServiceLayer.Utility;

View File

@@ -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 System;
namespace Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts
{
public abstract class AddUserDatabaseReferenceParams : AddDatabaseReferenceParams
{
/// <summary>
/// SQLCMD variable name for specifying the other database this reference is to, if different from that of the current project
/// </summary>
public string? DatabaseVariable { get; set; }
/// <summary>
/// SQLCMD variable name for specifying the other server this reference is to, if different from that of the current project.
/// If this is set, DatabaseVariable must also be set.
/// </summary>
public string? ServerVariable { get; set; }
/// <summary>
/// Throws if either both DatabaseVariable and DatabaseLiteral are set. This only validates
/// what is necessary for Tools Service. The DacFx Projects library does comprehensive validation.
/// </summary>
/// <exception cref="ArgumentException"></exception>
internal void Validate()
{
if (DatabaseVariable != null && DatabaseLiteral != null)
{
throw new ArgumentException($"Both {nameof(DatabaseVariable)} and {nameof(DatabaseLiteral)} cannot be set.");
}
}
}
}

View File

@@ -3,6 +3,8 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.ServiceLayer.Utility;

View File

@@ -3,8 +3,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.ServiceLayer.Utility;

View File

@@ -3,8 +3,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.ServiceLayer.Utility;

View File

@@ -3,8 +3,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using Microsoft.SqlServer.Dac.Projects;
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.ServiceLayer.Utility;

View File

@@ -3,8 +3,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.ServiceLayer.Utility;

View File

@@ -3,8 +3,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.ServiceLayer.Utility;
@@ -18,7 +16,7 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects.Contracts
/// <summary>
/// Name of the SQLCMD variable to be deleted
/// </summary>
public string Name { get; set; }
public string? Name { get; set; }
}
public class DeleteSqlCmdVariableRequest

View File

@@ -3,8 +3,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.ServiceLayer.Utility;

View File

@@ -3,8 +3,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.ServiceLayer.Utility;

View File

@@ -3,8 +3,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.ServiceLayer.Utility;

View File

@@ -3,8 +3,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
#nullable disable
using Microsoft.SqlTools.Hosting.Protocol.Contracts;
using Microsoft.SqlTools.ServiceLayer.Utility;

View File

@@ -81,20 +81,20 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects
internal async Task HandleOpenSqlProjectRequest(SqlProjectParams requestParams, RequestContext<ResultStatus> requestContext)
{
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri), requestContext);
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri!), requestContext);
}
internal async Task HandleCloseSqlProjectRequest(SqlProjectParams requestParams, RequestContext<ResultStatus> requestContext)
{
await RunWithErrorHandling(() => Projects.TryRemove(requestParams.ProjectUri, out _), requestContext);
await RunWithErrorHandling(() => Projects.TryRemove(requestParams.ProjectUri!, out _), requestContext);
}
internal async Task HandleNewSqlProjectRequest(NewSqlProjectParams requestParams, RequestContext<ResultStatus> requestContext)
{
await RunWithErrorHandling(async () =>
{
await SqlProject.CreateProjectAsync(requestParams.ProjectUri, new CreateSqlProjectParams() { ProjectType = requestParams.SqlProjectType, DspVersion = requestParams.DatabaseSchemaProvider });
GetProject(requestParams.ProjectUri); // load into the cache
await SqlProject.CreateProjectAsync(requestParams.ProjectUri!, new CreateSqlProjectParams() { ProjectType = requestParams.SqlProjectType, DspVersion = requestParams.DatabaseSchemaProvider });
GetProject(requestParams.ProjectUri!); // load into the cache
}, requestContext);
}
@@ -106,14 +106,14 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects
{
Success = true,
ErrorMessage = null,
IsCrossPlatformCompatible = GetProject(requestParams.ProjectUri).CrossPlatformCompatible
IsCrossPlatformCompatible = GetProject(requestParams.ProjectUri!).CrossPlatformCompatible
};
}, requestContext);
}
internal async Task HandleUpdateProjectForCrossPlatformRequest(SqlProjectParams requestParams, RequestContext<ResultStatus> requestContext)
{
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).UpdateForCrossPlatform(), requestContext);
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri!).UpdateForCrossPlatform(), requestContext);
}
#endregion
@@ -124,17 +124,17 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects
internal async Task HandleAddSqlObjectScriptRequest(SqlProjectScriptParams requestParams, RequestContext<ResultStatus> requestContext)
{
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).SqlObjectScripts.Add(new SqlObjectScript(requestParams.Path)), requestContext);
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri!).SqlObjectScripts.Add(new SqlObjectScript(requestParams.Path!)), requestContext);
}
internal async Task HandleDeleteSqlObjectScriptRequest(SqlProjectScriptParams requestParams, RequestContext<ResultStatus> requestContext)
{
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).SqlObjectScripts.Delete(requestParams.Path), requestContext);
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri!).SqlObjectScripts.Delete(requestParams.Path!), requestContext);
}
internal async Task HandleExcludeSqlObjectScriptRequest(SqlProjectScriptParams requestParams, RequestContext<ResultStatus> requestContext)
{
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).SqlObjectScripts.Exclude(requestParams.Path), requestContext);
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri!).SqlObjectScripts.Exclude(requestParams.Path!), requestContext);
}
#endregion
@@ -193,11 +193,11 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects
internal async Task HandleAddSystemDatabaseReferenceRequest(AddSystemDatabaseReferenceParams requestParams, RequestContext<ResultStatus> requestContext)
{
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).DatabaseReferences.Add(
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri!).DatabaseReferences.Add(
new SystemDatabaseReference(
requestParams.SystemDatabase,
requestParams.SuppressMissingDependencies,
requestParams.DatabaseVariable)),
requestParams.DatabaseLiteral)),
requestContext);
}
@@ -205,14 +205,32 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects
{
await RunWithErrorHandling(() =>
{
SqlProject project = GetProject(requestParams.ProjectUri);
requestParams.Validate();
project.DatabaseReferences.Add(
new DacpacReference(
SqlProject project = GetProject(requestParams.ProjectUri!);
DacpacReference reference;
if (requestParams.DatabaseLiteral != null) // same server, different database via database name literal
{
reference = new DacpacReference(
requestParams.DacpacPath,
requestParams.SuppressMissingDependencies,
project.SqlCmdVariables.Get(requestParams.DatabaseVariable),
project.SqlCmdVariables.Get(requestParams.ServerVariable)));
requestParams.DatabaseLiteral);
}
else if (requestParams.DatabaseVariable != null) // different database, possibly different server via sqlcmdvar
{
reference = new DacpacReference(
requestParams.DacpacPath,
requestParams.SuppressMissingDependencies,
project.SqlCmdVariables.Get(requestParams.DatabaseVariable!),
requestParams.ServerVariable != null ? project.SqlCmdVariables.Get(requestParams.ServerVariable) : null);
}
else // same database
{
reference = new DacpacReference(requestParams.DacpacPath, requestParams.SuppressMissingDependencies);
}
project.DatabaseReferences.Add(reference);
}, requestContext);
}
@@ -220,42 +238,64 @@ namespace Microsoft.SqlTools.ServiceLayer.SqlProjects
{
await RunWithErrorHandling(() =>
{
SqlProject project = GetProject(requestParams.ProjectUri);
requestParams.Validate();
project.DatabaseReferences.Add(
new SqlProjectReference(
SqlProject project = GetProject(requestParams.ProjectUri!);
SqlProjectReference reference;
if (requestParams.DatabaseLiteral != null) // same server, different database via database name literal
{
reference = new SqlProjectReference(
requestParams.ProjectPath,
requestParams.ProjectGuid,
requestParams.SuppressMissingDependencies,
project.SqlCmdVariables.Get(requestParams.DatabaseVariable),
project.SqlCmdVariables.Get(requestParams.ServerVariable)));
requestParams.DatabaseLiteral);
}
else if (requestParams.DatabaseVariable != null) // different database, possibly different server via sqlcmdvar
{
reference = new SqlProjectReference(
requestParams.ProjectPath,
requestParams.ProjectGuid, requestParams.SuppressMissingDependencies,
project.SqlCmdVariables.Get(requestParams.DatabaseVariable!),
requestParams.ServerVariable != null ? project.SqlCmdVariables.Get(requestParams.ServerVariable) : null);
}
else // same database
{
reference = new SqlProjectReference(
requestParams.ProjectPath,
requestParams.ProjectGuid,
requestParams.SuppressMissingDependencies);
}
project.DatabaseReferences.Add(reference);
}, requestContext);
}
internal async Task HandleDeleteDatabaseReferenceRequest(DeleteDatabaseReferenceParams requestParams, RequestContext<ResultStatus> requestContext)
{
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).DatabaseReferences.Delete(requestParams.Name), requestContext);
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri!).DatabaseReferences.Delete(requestParams.Name!), requestContext);
}
#endregion
#region SQLCMD variable functions
internal async Task HandleAddSqlCmdVariableRequest(AddSqlCmdVariableParams requestParams, RequestContext<ResultStatus> requestContext)
{
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).SqlCmdVariables.Add(new SqlCmdVariable(requestParams.Name, requestParams.DefaultValue, requestParams.Value)), requestContext);
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri!).SqlCmdVariables.Add(new SqlCmdVariable(requestParams.Name, requestParams.DefaultValue, requestParams.Value)), requestContext);
}
internal async Task HandleDeleteSqlCmdVariableRequest(DeleteSqlCmdVariableParams requestParams, RequestContext<ResultStatus> requestContext)
{
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri).SqlCmdVariables.Delete(requestParams.Name), requestContext);
await RunWithErrorHandling(() => GetProject(requestParams.ProjectUri!).SqlCmdVariables.Delete(requestParams.Name), requestContext);
}
internal async Task HandleUpdateSqlCmdVariableRequest(AddSqlCmdVariableParams requestParams, RequestContext<ResultStatus> requestContext)
{
await RunWithErrorHandling(() =>
{
SqlProject project = GetProject(requestParams.ProjectUri);
SqlProject project = GetProject(requestParams.ProjectUri!);
project.SqlCmdVariables.Delete(requestParams.Name); // idempotent (won't throw if doesn't exist)
project.SqlCmdVariables.Add(new SqlCmdVariable(requestParams.Name, requestParams.DefaultValue, requestParams.Value));
}, requestContext);