From 1917100bfb56f649da43a5953c0bd1ef40e40289 Mon Sep 17 00:00:00 2001
From: kisantia <31145923+kisantia@users.noreply.github.com>
Date: Fri, 25 Jan 2019 11:34:28 -0800
Subject: [PATCH] Add DacFx Generate Deploy Script operation (#759)
* Adding DacFx Generate Deploy Script operation
---
.../Contracts/GenerateDeployScriptRequest.cs | 40 ++++++++++
.../DacFx/DacFxOperation.cs | 11 +--
.../DacFx/DacFxService.cs | 51 ++++++++++--
.../DacFx/DeployOperation.cs | 3 +-
.../DacFx/ExportOperation.cs | 3 +-
.../DacFx/ExtractOperation.cs | 3 +-
.../DacFx/GenerateDeployScriptOperation.cs | 48 +++++++++++
.../DacFx/ImportOperation.cs | 3 +-
.../DacFx/DacFxserviceTests.cs | 79 ++++++++++++++++---
9 files changed, 211 insertions(+), 30 deletions(-)
create mode 100644 src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/GenerateDeployScriptRequest.cs
create mode 100644 src/Microsoft.SqlTools.ServiceLayer/DacFx/GenerateDeployScriptOperation.cs
diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/GenerateDeployScriptRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/GenerateDeployScriptRequest.cs
new file mode 100644
index 00000000..d05ef8df
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/GenerateDeployScriptRequest.cs
@@ -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 Microsoft.SqlTools.Hosting.Protocol.Contracts;
+using Microsoft.SqlTools.ServiceLayer.TaskServices;
+using Microsoft.SqlTools.ServiceLayer.Utility;
+
+namespace Microsoft.SqlTools.ServiceLayer.DacFx.Contracts
+{
+ ///
+ /// Parameters for a DacFx generate deploy script request.
+ ///
+ public class GenerateDeployScriptParams : DacFxParams
+ {
+ ///
+ /// Gets or sets the filepath where to save the generated script
+ ///
+ public string ScriptFilePath { get; set; }
+
+ ///
+ /// Gets or sets whether a Deployment Report should be generated during deploy.
+ ///
+ public bool GenerateDeploymentReport { get; set; }
+
+ ///
+ /// Gets or sets the filepath where to save the deployment report
+ ///
+ public string DeploymentReportFilePath { get; set; }
+ }
+
+ ///
+ /// Defines the DacFx generate deploy script request type
+ ///
+ class GenerateDeployScriptRequest
+ {
+ public static readonly RequestType Type =
+ RequestType.Create("dacfx/generateDeploymentScript");
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxOperation.cs
index 151760f1..e8037aff 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxOperation.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxOperation.cs
@@ -3,6 +3,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using Microsoft.SqlServer.Dac;
+using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.TaskServices;
using Microsoft.SqlTools.Utility;
using System;
@@ -27,14 +28,14 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
public SqlTask SqlTask { get; set; }
- protected SqlConnection SqlConnection { get; private set; }
+ protected string ConnectionString { get; private set; }
protected DacServices DacServices { get; private set; }
- protected DacFxOperation(SqlConnection sqlConnection)
+ protected DacFxOperation(ConnectionInfo connInfo)
{
- Validate.IsNotNull("sqlConnection", sqlConnection);
- this.SqlConnection = sqlConnection;
+ Validate.IsNotNull("connectionInfo", connInfo);
+ this.ConnectionString = ConnectionService.BuildConnectionString(connInfo.ConnectionDetails);
this.OperationId = Guid.NewGuid().ToString();
}
@@ -78,7 +79,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
try
{
- this.DacServices = new DacServices(this.SqlConnection.ConnectionString);
+ this.DacServices = new DacServices(this.ConnectionString);
Execute();
}
catch (Exception e)
diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxService.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxService.cs
index 5e7609b8..7ae9b2d4 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxService.cs
@@ -43,6 +43,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
serviceHost.SetRequestHandler(ImportRequest.Type, this.HandleImportRequest);
serviceHost.SetRequestHandler(ExtractRequest.Type, this.HandleExtractRequest);
serviceHost.SetRequestHandler(DeployRequest.Type, this.HandleDeployRequest);
+ serviceHost.SetRequestHandler(GenerateDeployScriptRequest.Type, this.HandleGenerateDeployScriptRequest);
}
///
@@ -64,8 +65,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
out connInfo);
if (connInfo != null)
{
- SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo, "Export");
- ExportOperation operation = new ExportOperation(parameters, sqlConn);
+ ExportOperation operation = new ExportOperation(parameters, connInfo);
await ExecuteOperation(operation, parameters, "Export bacpac", requestContext);
}
}
@@ -89,8 +89,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
out connInfo);
if (connInfo != null)
{
- SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo, "Import");
- ImportOperation operation = new ImportOperation(parameters, sqlConn);
+ ImportOperation operation = new ImportOperation(parameters, connInfo);
await ExecuteOperation(operation, parameters, "Import bacpac", requestContext);
}
}
@@ -114,8 +113,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
out connInfo);
if (connInfo != null)
{
- SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo, "Extract");
- ExtractOperation operation = new ExtractOperation(parameters, sqlConn);
+ ExtractOperation operation = new ExtractOperation(parameters, connInfo);
await ExecuteOperation(operation, parameters, "Extract dacpac", requestContext);
}
}
@@ -139,8 +137,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
out connInfo);
if (connInfo != null)
{
- SqlConnection sqlConn = ConnectionService.OpenSqlConnection(connInfo, "Deploy");
- DeployOperation operation = new DeployOperation(parameters, sqlConn);
+ DeployOperation operation = new DeployOperation(parameters, connInfo);
await ExecuteOperation(operation, parameters, "Deploy dacpac", requestContext);
}
}
@@ -150,6 +147,44 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
}
}
+ ///
+ /// Handles request to generate deploy script
+ ///
+ ///
+ public async Task HandleGenerateDeployScriptRequest(GenerateDeployScriptParams parameters, RequestContext requestContext)
+ {
+ try
+ {
+ ConnectionInfo connInfo;
+ ConnectionServiceInstance.TryFindConnection(
+ parameters.OwnerUri,
+ out connInfo);
+ if (connInfo != null)
+ {
+ GenerateDeployScriptOperation operation = new GenerateDeployScriptOperation(parameters, connInfo);
+ SqlTask sqlTask = null;
+ TaskMetadata metadata = TaskMetadata.Create(parameters, "Generate script", operation, ConnectionServiceInstance);
+
+ // want to show filepath in task history instead of server and database
+ metadata.ServerName = parameters.ScriptFilePath;
+ metadata.DatabaseName = "";
+
+ sqlTask = SqlTaskManagerInstance.CreateAndRun(metadata);
+
+ await requestContext.SendResult(new DacFxResult()
+ {
+ OperationId = operation.OperationId,
+ Success = true,
+ ErrorMessage = ""
+ });
+ }
+ }
+ catch (Exception e)
+ {
+ await requestContext.SendError(e);
+ }
+ }
+
private async Task ExecuteOperation(DacFxOperation operation, DacFxParams parameters, string taskName, RequestContext requestContext)
{
SqlTask sqlTask = null;
diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/DeployOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/DeployOperation.cs
index b636adcc..f6d07bba 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/DacFx/DeployOperation.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/DeployOperation.cs
@@ -3,6 +3,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using Microsoft.SqlServer.Dac;
+using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts;
using Microsoft.SqlTools.ServiceLayer.TaskServices;
using Microsoft.SqlTools.Utility;
@@ -19,7 +20,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
{
public DeployParams Parameters { get; }
- public DeployOperation(DeployParams parameters, SqlConnection sqlConnection) : base(sqlConnection)
+ public DeployOperation(DeployParams parameters, ConnectionInfo connInfo) : base(connInfo)
{
Validate.IsNotNull("parameters", parameters);
this.Parameters = parameters;
diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/ExportOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/ExportOperation.cs
index 6cda9068..15cb1bf1 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/DacFx/ExportOperation.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/ExportOperation.cs
@@ -3,6 +3,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using Microsoft.SqlServer.Dac;
+using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts;
using Microsoft.SqlTools.ServiceLayer.TaskServices;
using Microsoft.SqlTools.Utility;
@@ -19,7 +20,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
{
public ExportParams Parameters { get; }
- public ExportOperation(ExportParams parameters, SqlConnection sqlConnection): base(sqlConnection)
+ public ExportOperation(ExportParams parameters, ConnectionInfo connInfo) : base(connInfo)
{
Validate.IsNotNull("parameters", parameters);
this.Parameters = parameters;
diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/ExtractOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/ExtractOperation.cs
index 09b6b130..4e4b6180 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/DacFx/ExtractOperation.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/ExtractOperation.cs
@@ -3,6 +3,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using Microsoft.SqlServer.Dac;
+using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts;
using Microsoft.SqlTools.ServiceLayer.TaskServices;
using Microsoft.SqlTools.Utility;
@@ -19,7 +20,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
{
public ExtractParams Parameters { get; }
- public ExtractOperation(ExtractParams parameters, SqlConnection sqlConnection): base(sqlConnection)
+ public ExtractOperation(ExtractParams parameters, ConnectionInfo connInfo) : base(connInfo)
{
Validate.IsNotNull("parameters", parameters);
this.Parameters = parameters;
diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/GenerateDeployScriptOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/GenerateDeployScriptOperation.cs
new file mode 100644
index 00000000..0ef7bee1
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/GenerateDeployScriptOperation.cs
@@ -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 Microsoft.SqlServer.Dac;
+using Microsoft.SqlTools.ServiceLayer.Connection;
+using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts;
+using Microsoft.SqlTools.ServiceLayer.TaskServices;
+using Microsoft.SqlTools.Utility;
+using System;
+using System.Data.SqlClient;
+using System.Diagnostics;
+using System.IO;
+
+namespace Microsoft.SqlTools.ServiceLayer.DacFx
+{
+ ///
+ /// Class to represent an in-progress generate deploy script operation
+ ///
+ class GenerateDeployScriptOperation : DacFxOperation
+ {
+ public GenerateDeployScriptParams Parameters { get; }
+
+ public GenerateDeployScriptOperation(GenerateDeployScriptParams parameters, ConnectionInfo connInfo) : base(connInfo)
+ {
+ Validate.IsNotNull("parameters", parameters);
+ this.Parameters = parameters;
+ }
+
+ public override void Execute()
+ {
+ DacPackage dacpac = DacPackage.Load(this.Parameters.PackageFilePath);
+ PublishOptions publishOptions = new PublishOptions();
+ publishOptions.GenerateDeploymentReport = this.Parameters.GenerateDeploymentReport;
+ publishOptions.CancelToken = this.CancellationToken;
+ publishOptions.DatabaseScriptPath = this.Parameters.ScriptFilePath;
+ // master script is only used if the target is Azure SQL db and the script contains all operations that must be done against the master database
+ publishOptions.MasterDbScriptPath = Path.Combine(Path.GetDirectoryName(this.Parameters.ScriptFilePath), string.Concat("master_", Path.GetFileName(this.Parameters.ScriptFilePath)));
+
+ PublishResult result = this.DacServices.Script(dacpac, this.Parameters.DatabaseName, publishOptions);
+
+ if(this.Parameters.GenerateDeploymentReport && !string.IsNullOrEmpty(this.Parameters.DeploymentReportFilePath))
+ {
+ File.WriteAllText(this.Parameters.DeploymentReportFilePath, result.DeploymentReport);
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/ImportOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/ImportOperation.cs
index fbc51347..df277f36 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/DacFx/ImportOperation.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/ImportOperation.cs
@@ -3,6 +3,7 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using Microsoft.SqlServer.Dac;
+using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts;
using Microsoft.SqlTools.ServiceLayer.TaskServices;
using Microsoft.SqlTools.Utility;
@@ -19,7 +20,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
{
public ImportParams Parameters { get; }
- public ImportOperation(ImportParams parameters, SqlConnection sqlConnection): base(sqlConnection)
+ public ImportOperation(ImportParams parameters, ConnectionInfo connInfo) : base(connInfo)
{
Validate.IsNotNull("parameters", parameters);
this.Parameters = parameters;
diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxserviceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxserviceTests.cs
index 612bda07..d97ce325 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxserviceTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxserviceTests.cs
@@ -42,9 +42,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
PackageFilePath = Path.Combine(folderPath, string.Format("{0}.bacpac", testdb.DatabaseName))
};
- SqlConnection sqlConn = ConnectionService.OpenSqlConnection(result.ConnectionInfo, "Export");
DacFxService service = new DacFxService();
- ExportOperation operation = new ExportOperation(exportParams, sqlConn);
+ ExportOperation operation = new ExportOperation(exportParams, result.ConnectionInfo);
service.PerformOperation(operation);
// cleanup
@@ -71,9 +70,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
PackageFilePath = Path.Combine(folderPath, string.Format("{0}.bacpac", sourceDb.DatabaseName))
};
- SqlConnection sqlConn = ConnectionService.OpenSqlConnection(result.ConnectionInfo, "Import");
DacFxService service = new DacFxService();
- ExportOperation exportOperation = new ExportOperation(exportParams, sqlConn);
+ ExportOperation exportOperation = new ExportOperation(exportParams, result.ConnectionInfo);
service.PerformOperation(exportOperation);
// import the created bacpac
@@ -86,7 +84,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
DatabaseName = string.Concat(sourceDb.DatabaseName, "-imported")
};
- ImportOperation importOperation = new ImportOperation(importParams, sqlConn);
+ ImportOperation importOperation = new ImportOperation(importParams, result.ConnectionInfo);
service.PerformOperation(importOperation);
SqlTestDb targetDb = SqlTestDb.CreateFromExisting(importParams.DatabaseName);
@@ -116,9 +114,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
ApplicationVersion = new Version(1, 0)
};
- SqlConnection sqlConn = ConnectionService.OpenSqlConnection(result.ConnectionInfo, "Extract");
DacFxService service = new DacFxService();
- ExtractOperation operation = new ExtractOperation(extractParams, sqlConn);
+ ExtractOperation operation = new ExtractOperation(extractParams, result.ConnectionInfo);
service.PerformOperation(operation);
// cleanup
@@ -147,9 +144,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
ApplicationVersion = new Version(1, 0)
};
- SqlConnection sqlConn = ConnectionService.OpenSqlConnection(result.ConnectionInfo, "Deploy");
DacFxService service = new DacFxService();
- ExtractOperation extractOperation = new ExtractOperation(extractParams, sqlConn);
+ ExtractOperation extractOperation = new ExtractOperation(extractParams, result.ConnectionInfo);
service.PerformOperation(extractOperation);
// deploy the created dacpac
@@ -164,8 +160,9 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
UpgradeExisting = false
};
- DeployOperation deployOperation = new DeployOperation(deployParams, sqlConn);
- service.PerformOperation(deployOperation); SqlTestDb targetDb = SqlTestDb.CreateFromExisting(deployParams.DatabaseName);
+ DeployOperation deployOperation = new DeployOperation(deployParams, result.ConnectionInfo);
+ service.PerformOperation(deployOperation);
+ SqlTestDb targetDb = SqlTestDb.CreateFromExisting(deployParams.DatabaseName);
// cleanup
VerifyAndCleanup(extractParams.PackageFilePath);
@@ -191,9 +188,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
PackageFilePath = Path.Combine(folderPath, string.Format("{0}.bacpac", testdb.DatabaseName))
};
- SqlConnection sqlConn = ConnectionService.OpenSqlConnection(result.ConnectionInfo, "Export");
DacFxService service = new DacFxService();
- ExportOperation operation = new ExportOperation(exportParams, sqlConn);
+ ExportOperation operation = new ExportOperation(exportParams, result.ConnectionInfo);
// set cancellation token to cancel
operation.Cancel();
@@ -216,6 +212,54 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
return requestContext;
}
+ private async Task>> SendAndValidateGenerateDeployScriptRequest()
+ {
+ // first extract a dacpac
+ var result = GetLiveAutoCompleteTestObjects();
+ var extractRequestContext = new Mock>();
+ extractRequestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object()));
+
+ SqlTestDb sourceDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, null, "DacFxGenerateScriptTest");
+ string folderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DacFxTest");
+ Directory.CreateDirectory(folderPath);
+
+ var extractParams = new ExtractParams
+ {
+ DatabaseName = sourceDb.DatabaseName,
+ PackageFilePath = Path.Combine(folderPath, string.Format("{0}.dacpac", sourceDb.DatabaseName)),
+ ApplicationName = "test",
+ ApplicationVersion = new Version(1, 0)
+ };
+
+ DacFxService service = new DacFxService();
+ ExtractOperation extractOperation = new ExtractOperation(extractParams, result.ConnectionInfo);
+ service.PerformOperation(extractOperation);
+
+ // generate script
+ var generateScriptRequestContext = new Mock>();
+ generateScriptRequestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object()));
+
+
+ var generateScriptParams = new GenerateDeployScriptParams
+ {
+ PackageFilePath = extractParams.PackageFilePath,
+ DatabaseName = string.Concat(sourceDb.DatabaseName, "-deployed"),
+ ScriptFilePath = Path.Combine(folderPath, string.Concat(sourceDb.DatabaseName, "_", "UpgradeDACScript.sql"))
+ };
+
+ GenerateDeployScriptOperation generateScriptOperation = new GenerateDeployScriptOperation(generateScriptParams, result.ConnectionInfo);
+ service.PerformOperation(generateScriptOperation);
+ SqlTestDb targetDb = SqlTestDb.CreateFromExisting(generateScriptParams.DatabaseName);
+
+ // cleanup
+ VerifyAndCleanup(generateScriptParams.ScriptFilePath);
+ VerifyAndCleanup(extractParams.PackageFilePath);
+ sourceDb.Cleanup();
+ targetDb.Cleanup();
+
+ return generateScriptRequestContext;
+ }
+
///
/// Verify the export bacpac request
///
@@ -261,6 +305,15 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
Assert.NotNull(await SendAndValidateDeployRequest());
}
+ ///
+ /// Verify the gnerate deploy script request
+ ///
+ [Fact]
+ public async void GenerateDeployScript()
+ {
+ Assert.NotNull(await SendAndValidateGenerateDeployScriptRequest());
+ }
+
private void VerifyAndCleanup(string filePath)
{
// Verify it was created