diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/SaveResultsRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/SaveResultsRequest.cs
index 78c2c72f..721d13c9 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/SaveResultsRequest.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/SaveResultsRequest.cs
@@ -18,16 +18,6 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts
///
public string FilePath { get; set; }
- ///
- /// The encoding of the file to save results in
- ///
- public string FileEncoding { get; set; }
-
- ///
- /// Include headers of columns in CSV
- ///
- public bool IncludeHeaders { get; set; }
-
///
/// Index of the batch to get the results from
///
@@ -38,15 +28,38 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts
///
public int ResultSetIndex { get; set; }
+ ///
+ /// URI for the editor that called save results
+ ///
+ public string OwnerUri { get; set; }
+ }
+
+ ///
+ /// Parameters to save results as CSV
+ ///
+ public class SaveResultsAsCsvRequestParams: SaveResultsRequestParams{
+
///
/// CSV - Write values in quotes
///
public Boolean ValueInQuotes { get; set; }
///
- /// URI for the editor that called save results
+ /// The encoding of the file to save results in
///
- public string OwnerUri { get; set; }
+ public string FileEncoding { get; set; }
+
+ ///
+ /// Include headers of columns in CSV
+ ///
+ public bool IncludeHeaders { get; set; }
+ }
+
+ ///
+ /// Parameters to save results as JSON
+ ///
+ public class SaveResultsAsJsonRequestParams: SaveResultsRequestParams{
+ //TODO: define config for save as JSON
}
///
@@ -60,11 +73,24 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts
public string Messages { get; set; }
}
+ ///
+ /// Request type to save results as CSV
+ ///
public class SaveResultsAsCsvRequest
{
public static readonly
- RequestType Type =
- RequestType.Create("query/save");
+ RequestType Type =
+ RequestType.Create("query/saveCsv");
+ }
+
+ ///
+ /// Request type to save results as JSON
+ ///
+ public class SaveResultsAsJsonRequest
+ {
+ public static readonly
+ RequestType Type =
+ RequestType.Create("query/saveJson");
}
}
\ No newline at end of file
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/QueryExecutionService.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/QueryExecutionService.cs
index 882cf11b..24d4de09 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/QueryExecutionService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/QueryExecutionService.cs
@@ -14,6 +14,7 @@ using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
using Microsoft.SqlTools.ServiceLayer.Workspace;
+using Newtonsoft.Json;
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
{
@@ -100,6 +101,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
serviceHost.SetRequestHandler(QueryDisposeRequest.Type, HandleDisposeRequest);
serviceHost.SetRequestHandler(QueryCancelRequest.Type, HandleCancelRequest);
serviceHost.SetRequestHandler(SaveResultsAsCsvRequest.Type, HandleSaveResultsAsCsvRequest);
+ serviceHost.SetRequestHandler(SaveResultsAsJsonRequest.Type, HandleSaveResultsAsJsonRequest);
// Register handler for shutdown event
serviceHost.RegisterShutdownTask((shutdownParams, requestContext) =>
@@ -261,7 +263,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
///
/// Process request to save a resultSet to a file in CSV format
///
- public async Task HandleSaveResultsAsCsvRequest( SaveResultsRequestParams saveParams,
+ public async Task HandleSaveResultsAsCsvRequest( SaveResultsAsCsvRequestParams saveParams,
RequestContext requestContext)
{
// retrieve query for OwnerUri
@@ -276,28 +278,94 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution
}
try
{
- using (StreamWriter csvFile = new StreamWriter(File.OpenWrite(saveParams.FilePath)))
+ using (StreamWriter csvFile = new StreamWriter(File.Open(saveParams.FilePath, FileMode.Create)))
{
// get the requested resultSet from query
Batch selectedBatch = result.Batches[saveParams.BatchIndex];
ResultSet selectedResultSet = (selectedBatch.ResultSets.ToList())[saveParams.ResultSetIndex];
- if ( saveParams.IncludeHeaders)
+ if (saveParams.IncludeHeaders)
{
// write column names to csv
await csvFile.WriteLineAsync( string.Join( ",", selectedResultSet.Columns.Select( column => SaveResults.EncodeCsvField(column.ColumnName) ?? string.Empty)));
}
// write rows to csv
- foreach( var row in selectedResultSet.Rows)
+ foreach (var row in selectedResultSet.Rows)
{
- await csvFile.WriteLineAsync(string.Join( ",", row.Select( field => SaveResults.EncodeCsvField( (field != null) ? field.ToString(): string.Empty))));
+ await csvFile.WriteLineAsync( string.Join( ",", row.Select( field => SaveResults.EncodeCsvField((field != null) ? field.ToString(): string.Empty))));
}
}
}
catch(Exception ex)
{
// Delete file when exception occurs
- if(File.Exists(saveParams.FilePath))
+ if (File.Exists(saveParams.FilePath))
+ {
+ File.Delete(saveParams.FilePath);
+ }
+ await requestContext.SendError(ex.Message);
+ return;
+ }
+ await requestContext.SendResult(new SaveResultRequestResult
+ {
+ Messages = "Success"
+ });
+ return;
+ }
+
+ ///
+ /// Process request to save a resultSet to a file in JSON format
+ ///
+ public async Task HandleSaveResultsAsJsonRequest( SaveResultsAsJsonRequestParams saveParams,
+ RequestContext requestContext)
+ {
+ // retrieve query for OwnerUri
+ Query result;
+ if (!ActiveQueries.TryGetValue(saveParams.OwnerUri, out result))
+ {
+ await requestContext.SendResult(new SaveResultRequestResult
+ {
+ Messages = "Failed to save results, ID not found."
+ });
+ return;
+ }
+ try
+ {
+ using (StreamWriter jsonFile = new StreamWriter(File.Open(saveParams.FilePath, FileMode.Create)))
+ using (JsonWriter jsonWriter = new JsonTextWriter(jsonFile) )
+ {
+ jsonWriter.Formatting = Formatting.Indented;
+ jsonWriter.WriteStartArray();
+
+ // get the requested resultSet from query
+ Batch selectedBatch = result.Batches[saveParams.BatchIndex];
+ ResultSet selectedResultSet = (selectedBatch.ResultSets.ToList())[saveParams.ResultSetIndex];
+
+ // write each row to JSON
+ foreach (var row in selectedResultSet.Rows)
+ {
+ jsonWriter.WriteStartObject();
+ foreach (var field in row.Select((value,i) => new {value, i}))
+ {
+ jsonWriter.WritePropertyName(selectedResultSet.Columns[field.i].ColumnName);
+ if (field.value != null)
+ {
+ jsonWriter.WriteValue(field.value);
+ }
+ else
+ {
+ jsonWriter.WriteNull();
+ }
+ }
+ jsonWriter.WriteEndObject();
+ }
+ jsonWriter.WriteEndArray();
+ }
+ }
+ catch(Exception ex)
+ {
+ // Delete file when exception occurs
+ if (File.Exists(saveParams.FilePath))
{
File.Delete(saveParams.FilePath);
}
diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/SaveResultsTests.cs b/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/SaveResultsTests.cs
index 61cf5fea..b359705a 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/SaveResultsTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.Test/QueryExecution/SaveResultsTests.cs
@@ -32,7 +32,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
queryService.HandleExecuteRequest(executeParams, executeRequest.Object).Wait();
// Request to save the results as csv with correct parameters
- var saveParams = new SaveResultsRequestParams { OwnerUri = Common.OwnerUri, ResultSetIndex = 0, BatchIndex = 0 };
+ var saveParams = new SaveResultsAsCsvRequestParams { OwnerUri = Common.OwnerUri, ResultSetIndex = 0, BatchIndex = 0 };
saveParams.FilePath = "testwrite.csv";
saveParams.IncludeHeaders = true;
SaveResultRequestResult result = null;
@@ -46,14 +46,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
VerifySaveResultsCallCount(saveRequest, Times.Once(), Times.Never());
// Delete temp file after test
- if(File.Exists(saveParams.FilePath))
+ if (File.Exists(saveParams.FilePath))
{
File.Delete(saveParams.FilePath);
}
}
///
- /// Test handling exception in saving results to file
+ /// Test handling exception in saving results to CSV file
///
[Fact]
public void SaveResultsAsCsvExceptionTest()
@@ -65,8 +65,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
queryService.HandleExecuteRequest(executeParams, executeRequest.Object).Wait();
// Request to save the results as csv with incorrect filepath
- var saveParams = new SaveResultsRequestParams { OwnerUri = Common.OwnerUri, ResultSetIndex = 0, BatchIndex = 0 };
- if ( RuntimeInformation.IsOSPlatform( OSPlatform.Windows))
+ var saveParams = new SaveResultsAsCsvRequestParams { OwnerUri = Common.OwnerUri, ResultSetIndex = 0, BatchIndex = 0 };
+ if (RuntimeInformation.IsOSPlatform( OSPlatform.Windows))
{
saveParams.FilePath = "G:\\test.csv";
}
@@ -75,8 +75,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
saveParams.FilePath = "/test.csv";
}
// SaveResultRequestResult result = null;
- String errMessage = null;
- var saveRequest = GetSaveResultsContextMock( null, err => errMessage = (String) err);
+ string errMessage = null;
+ var saveRequest = GetSaveResultsContextMock( null, err => errMessage = (string) err);
queryService.ActiveQueries[Common.OwnerUri].Batches[0] = Common.GetBasicExecutedBatch();
queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object).Wait();
@@ -87,7 +87,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
}
///
- /// Test saving results to file when the requested result set is no longer active
+ /// Test saving results to CSV file when the requested result set is no longer active
///
[Fact]
public void SaveResultsAsCsvQueryNotFoundTest()
@@ -99,11 +99,10 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
queryService.HandleExecuteRequest(executeParams, executeRequest.Object).Wait();
// Request to save the results as csv with query that is no longer active
- var saveParams = new SaveResultsRequestParams { OwnerUri = "falseuri", ResultSetIndex = 0, BatchIndex = 0 };
+ var saveParams = new SaveResultsAsCsvRequestParams { OwnerUri = "falseuri", ResultSetIndex = 0, BatchIndex = 0 };
saveParams.FilePath = "testwrite.csv";
SaveResultRequestResult result = null;
var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
- // queryService.ActiveQueries[Common.OwnerUri].Batches[0] = Common.GetBasicExecutedBatch();
queryService.HandleSaveResultsAsCsvRequest(saveParams, saveRequest.Object).Wait();
// Expect message that save failed
@@ -112,6 +111,97 @@ namespace Microsoft.SqlTools.ServiceLayer.Test.QueryExecution
VerifySaveResultsCallCount(saveRequest, Times.Once(), Times.Never());
}
+ ///
+ /// Test save results to a file as JSON with correct parameters
+ ///
+ [Fact]
+ public void SaveResultsAsJsonSuccessTest()
+ {
+ // Execute a query
+ var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true);
+ var executeParams = new QueryExecuteParams { QueryText = Common.StandardQuery, OwnerUri = Common.OwnerUri };
+ var executeRequest = GetQueryExecuteResultContextMock(null, null, null);
+ queryService.HandleExecuteRequest(executeParams, executeRequest.Object).Wait();
+
+ // Request to save the results as json with correct parameters
+ var saveParams = new SaveResultsAsJsonRequestParams { OwnerUri = Common.OwnerUri, ResultSetIndex = 0, BatchIndex = 0 };
+ saveParams.FilePath = "testwrite.json";
+ SaveResultRequestResult result = null;
+ var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
+ queryService.ActiveQueries[Common.OwnerUri].Batches[0] = Common.GetBasicExecutedBatch();
+ queryService.HandleSaveResultsAsJsonRequest(saveParams, saveRequest.Object).Wait();
+
+ // Expect to see a file successfully created in filepath and a success message
+ Assert.Equal("Success", result.Messages);
+ Assert.True(File.Exists(saveParams.FilePath));
+ VerifySaveResultsCallCount(saveRequest, Times.Once(), Times.Never());
+
+ // Delete temp file after test
+ if (File.Exists(saveParams.FilePath))
+ {
+ File.Delete(saveParams.FilePath);
+ }
+ }
+
+ ///
+ /// Test handling exception in saving results to JSON file
+ ///
+ [Fact]
+ public void SaveResultsAsJsonExceptionTest()
+ {
+ // Execute a query
+ var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true);
+ var executeParams = new QueryExecuteParams { QueryText = Common.StandardQuery, OwnerUri = Common.OwnerUri };
+ var executeRequest = GetQueryExecuteResultContextMock(null, null, null);
+ queryService.HandleExecuteRequest(executeParams, executeRequest.Object).Wait();
+
+ // Request to save the results as json with incorrect filepath
+ var saveParams = new SaveResultsAsJsonRequestParams { OwnerUri = Common.OwnerUri, ResultSetIndex = 0, BatchIndex = 0 };
+ if (RuntimeInformation.IsOSPlatform( OSPlatform.Windows))
+ {
+ saveParams.FilePath = "G:\\test.json";
+ }
+ else
+ {
+ saveParams.FilePath = "/test.json";
+ }
+ // SaveResultRequestResult result = null;
+ string errMessage = null;
+ var saveRequest = GetSaveResultsContextMock( null, err => errMessage = (string) err);
+ queryService.ActiveQueries[Common.OwnerUri].Batches[0] = Common.GetBasicExecutedBatch();
+ queryService.HandleSaveResultsAsJsonRequest(saveParams, saveRequest.Object).Wait();
+
+ // Expect to see error message
+ Assert.NotNull(errMessage);
+ VerifySaveResultsCallCount(saveRequest, Times.Never(), Times.Once());
+ Assert.False(File.Exists(saveParams.FilePath));
+ }
+
+ ///
+ /// Test saving results to JSON file when the requested result set is no longer active
+ ///
+ [Fact]
+ public void SaveResultsAsJsonQueryNotFoundTest()
+ {
+ // Execute a query
+ var queryService = Common.GetPrimedExecutionService(Common.CreateMockFactory(null, false), true);
+ var executeParams = new QueryExecuteParams { QueryText = Common.StandardQuery, OwnerUri = Common.OwnerUri };
+ var executeRequest = GetQueryExecuteResultContextMock(null, null, null);
+ queryService.HandleExecuteRequest(executeParams, executeRequest.Object).Wait();
+
+ // Request to save the results as json with query that is no longer active
+ var saveParams = new SaveResultsAsJsonRequestParams { OwnerUri = "falseuri", ResultSetIndex = 0, BatchIndex = 0 };
+ saveParams.FilePath = "testwrite.json";
+ SaveResultRequestResult result = null;
+ var saveRequest = GetSaveResultsContextMock(qcr => result = qcr, null);
+ queryService.HandleSaveResultsAsJsonRequest(saveParams, saveRequest.Object).Wait();
+
+ // Expect message that save failed
+ Assert.Equal("Failed to save results, ID not found.", result.Messages);
+ Assert.False(File.Exists(saveParams.FilePath));
+ VerifySaveResultsCallCount(saveRequest, Times.Once(), Times.Never());
+ }
+
#region Mocking
///