Skip to content

Commit 8d0d62f

Browse files
committed
feat: SqlSeverSource can point to an SQL file
Adds `FilePath` argument to point to a text file with SQL statement, instead of including the entire SQL statement in the migrationsettings-file. add: Coverage to SqlServer extension
1 parent cfc7654 commit 8d0d62f

File tree

8 files changed

+116
-33
lines changed

8 files changed

+116
-33
lines changed

Extensions/SqlServer/Cosmos.DataTransfer.SqlServerExtension.UnitTests/Cosmos.DataTransfer.SqlServerExtension.UnitTests.csproj

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
<Nullable>enable</Nullable>
77

88
<IsPackable>false</IsPackable>
9+
<CollectCoverage>true</CollectCoverage>
10+
<CoverletOutputFormat>cobertura</CoverletOutputFormat>
11+
<CoverletOutput>../../../Extensions/SqlServer/Cosmos.DataTransfer.SqlServerExtension/coverage.cobertura.xml</CoverletOutput>
912
</PropertyGroup>
1013

1114
<ItemGroup>
@@ -15,14 +18,20 @@
1518
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
1619
<PackageReference Include="coverlet.collector" Version="3.1.2" />
1720
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
21+
<PackageReference Include="coverlet.msbuild" Version="2.8.0">
22+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
23+
<PrivateAssets>all</PrivateAssets>
24+
</PackageReference>
1825
</ItemGroup>
1926

2027
<ItemGroup>
28+
<ProjectReference Include="..\..\..\Interfaces\Cosmos.DataTransfer.Common.UnitTests\Cosmos.DataTransfer.Common.UnitTests.csproj" />
2129
<ProjectReference Include="..\Cosmos.DataTransfer.SqlServerExtension\Cosmos.DataTransfer.SqlServerExtension.csproj" />
2230
</ItemGroup>
2331

32+
2433
<ItemGroup>
25-
<None Update="Data\FlatFile.json">
34+
<None Update="Data\SqlStatement.sql">
2635
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
2736
</None>
2837
</ItemGroup>

Extensions/SqlServer/Cosmos.DataTransfer.SqlServerExtension.UnitTests/Data/FlatFile.json

Lines changed: 0 additions & 18 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SELECT * FROM foobar;

Extensions/SqlServer/Cosmos.DataTransfer.SqlServerExtension.UnitTests/SqlServerExtensionTests.cs

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using Cosmos.DataTransfer.Interfaces;
2+
using System.ComponentModel.DataAnnotations;
3+
4+
namespace Cosmos.DataTransfer.SqlServerExtension.UnitTests;
5+
6+
[TestClass]
7+
public class SqlServerSourceSettingsTests
8+
{
9+
10+
[TestMethod]
11+
public void TestSourceSettings_ValidationFails1()
12+
{
13+
var settings = new SqlServerSourceSettings {
14+
};
15+
16+
var validationResults = settings.Validate(new ValidationContext(settings)).ToList();
17+
var expected = new List<ValidationResult>() {
18+
new ValidationResult("The `ConnectionString` field is required.",
19+
new string[] { "ConnectionString" }),
20+
new ValidationResult("Either `QueryText` or `FilePath` are required!",
21+
new string[] { "QueryText", "FilePath"})
22+
};
23+
CollectionAssert.AreEquivalent(expected.Select(x => x.ErrorMessage).ToList(),
24+
validationResults.Select(x => x.ErrorMessage).ToList());
25+
CollectionAssert.AreEquivalent(expected.SelectMany(x => x.MemberNames).ToList(),
26+
validationResults.SelectMany(x => x.MemberNames).ToList());
27+
28+
Assert.ThrowsException<AggregateException>(() => settings.Validate());
29+
}
30+
31+
[TestMethod]
32+
public void TestSourceSettings_ValidationFails2()
33+
{
34+
var settings = new SqlServerSourceSettings {
35+
QueryText = "SELECT 1;",
36+
FilePath = "dmt-query.sql"
37+
};
38+
39+
var validationResults = settings.Validate(new ValidationContext(settings)).ToList();
40+
var expected = new List<ValidationResult>() {
41+
new ValidationResult("The `ConnectionString` field is required.",
42+
new string[] { "ConnectionString" }),
43+
new ValidationResult("Both `QueryText` and `FilePath` are not allowed.",
44+
new string[] { "QueryText", "FilePath"})
45+
};
46+
CollectionAssert.AreEquivalent(expected.Select(x => x.ErrorMessage).ToList(),
47+
validationResults.Select(x => x.ErrorMessage).ToList());
48+
CollectionAssert.AreEquivalent(expected.SelectMany(x => x.MemberNames).ToList(),
49+
validationResults.SelectMany(x => x.MemberNames).ToList());
50+
51+
Assert.ThrowsException<AggregateException>(() => settings.Validate());
52+
}
53+
54+
[TestMethod]
55+
[DataRow("SELECT 1", null)]
56+
[DataRow("SELECT 1", " ")]
57+
[DataRow(null, "filename")]
58+
[DataRow(" ", "filename")]
59+
public void TestSourceSettings_ValidationSuccess(string queryText, string filePath) {
60+
var settings = new SqlServerSourceSettings {
61+
ConnectionString = "Server",
62+
QueryText = queryText,
63+
FilePath = filePath
64+
};
65+
66+
var validationResults = settings.Validate(new ValidationContext(settings));
67+
Assert.AreEqual(0, validationResults.Count());
68+
69+
settings.Validate();
70+
}
71+
}

Extensions/SqlServer/Cosmos.DataTransfer.SqlServerExtension/SqlServerDataSourceExtension.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.ComponentModel.Composition;
2+
using System.IO;
23
using System.Runtime.CompilerServices;
34
using Cosmos.DataTransfer.Interfaces;
45
using Microsoft.Data.SqlClient;
@@ -17,9 +18,14 @@ public async IAsyncEnumerable<IDataItem> ReadAsync(IConfiguration config, ILogge
1718
var settings = config.Get<SqlServerSourceSettings>();
1819
settings.Validate();
1920

21+
string queryText = settings.QueryText!;
22+
if (settings.FilePath != null) {
23+
queryText = File.ReadAllText(queryText);
24+
}
25+
2026
await using var connection = new SqlConnection(settings.ConnectionString);
2127
await connection.OpenAsync(cancellationToken);
22-
await using SqlCommand command = new SqlCommand(settings.QueryText, connection);
28+
await using SqlCommand command = new SqlCommand(queryText, connection);
2329
await using SqlDataReader reader = await command.ExecuteReaderAsync(cancellationToken);
2430
while (await reader.ReadAsync(cancellationToken))
2531
{

Extensions/SqlServer/Cosmos.DataTransfer.SqlServerExtension/SqlServerSourceSettings.cs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,32 @@
44

55
namespace Cosmos.DataTransfer.SqlServerExtension
66
{
7-
public class SqlServerSourceSettings : IDataExtensionSettings
7+
public class SqlServerSourceSettings : IDataExtensionSettings, IValidatableObject
88
{
9-
[Required]
109
[SensitiveValue]
1110
public string? ConnectionString { get; set; }
1211

13-
[Required]
1412
public string? QueryText { get; set; }
1513

14+
public string? FilePath { get; set; }
15+
16+
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
17+
{
18+
if (String.IsNullOrWhiteSpace(this.ConnectionString)) {
19+
yield return new ValidationResult("The `ConnectionString` field is required.",
20+
new string[] { "ConnectionString" });
21+
}
22+
if (String.IsNullOrWhiteSpace(this.QueryText) &&
23+
String.IsNullOrWhiteSpace(this.FilePath)) {
24+
yield return new ValidationResult(
25+
"Either `QueryText` or `FilePath` are required!",
26+
new string[] { "QueryText", "FilePath"});
27+
} else if (String.IsNullOrWhiteSpace(this.QueryText) == false &&
28+
String.IsNullOrWhiteSpace(this.FilePath) == false) {
29+
yield return new ValidationResult(
30+
"Both `QueryText` and `FilePath` are not allowed.",
31+
new string[] { "QueryText", "FilePath"});
32+
}
33+
}
1634
}
1735
}

Extensions/SqlServer/README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,18 @@ The SQL Server data transfer extension provides source and sink capabilities for
88

99
Source and sink settings both require a `ConnectionString` parameter.
1010

11-
Source settings also require a `QueryText` parameter to define the data to select from SQL. This can combine data from multiple tables, views, etc. but should produce a single result set.
11+
Source settings also require either a `QueryText` or `FilePath` parameter with the SQL statement that defines the data to select.
12+
`QueryText` can be the SQL statement, while `FilePath` can point to a file with the SQL statement.
13+
In both cases, the SQL statement can combine data from multiple tables, views, etc.,
14+
but should produce a single result set.
1215

1316
### Source
1417

1518
```json
1619
{
1720
"ConnectionString": "",
18-
"QueryText": ""
21+
"QueryText": "", // required if FilePath not set.
22+
"FilePath": "" // required if QueryText not set.
1923
}
2024
```
2125

0 commit comments

Comments
 (0)