Skip to content

Commit d76dc50

Browse files
committed
Validation cleanup
1 parent abf1522 commit d76dc50

File tree

3 files changed

+112
-26
lines changed

3 files changed

+112
-26
lines changed

Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension.UnitTests/CosmosSinkSettingsTests.cs

+67-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ namespace Cosmos.DataTransfer.CosmosExtension.UnitTests;
55
[TestClass]
66
public class CosmosSinkSettingsTests
77
{
8+
private static void LogErrors(IEnumerable<string?> errors)
9+
{
10+
foreach (var error in errors) Console.WriteLine($"Validation Error: {error}");
11+
}
12+
813
[TestMethod]
914
public void GetValidationErrors_WithNoConnection_ReturnsError()
1015
{
@@ -15,6 +20,7 @@ public void GetValidationErrors_WithNoConnection_ReturnsError()
1520
};
1621

1722
var validationErrors = settings.GetValidationErrors();
23+
LogErrors(validationErrors);
1824

1925
Assert.AreEqual(1, validationErrors.Count(v => v.Contains(nameof(CosmosSinkSettings.ConnectionString))));
2026
}
@@ -30,6 +36,7 @@ public void GetValidationErrors_WithNoRbacConnection_ReturnsError()
3036
};
3137

3238
var validationErrors = settings.GetValidationErrors();
39+
LogErrors(validationErrors);
3340

3441
Assert.AreEqual(1, validationErrors.Count(v => v.Contains(nameof(CosmosSinkSettings.AccountEndpoint))));
3542
}
@@ -55,7 +62,7 @@ public void Validate_WithAccountEndpoint_Succeeds()
5562
UseRbacAuth = true,
5663
AccountEndpoint = "https://localhost:8081/",
5764
Database = "db",
58-
Container = "container",
65+
Container = "container"
5966
};
6067

6168
settings.Validate();
@@ -70,11 +77,31 @@ public void GetValidationErrors_WhenRecreateContainerTrue_RequiresPartitionKeyPa
7077
Database = "db",
7178
Container = "container",
7279
RecreateContainer = true,
80+
WriteMode = DataWriteMode.Insert,
7381
};
7482

7583
var validationErrors = settings.GetValidationErrors();
84+
LogErrors(validationErrors);
7685

77-
Assert.AreEqual(1, validationErrors.Count(v => v.Contains(nameof(CosmosSinkSettings.PartitionKeyPath))));
86+
Assert.AreEqual(1, validationErrors.Count(v => v.Contains(nameof(CosmosSinkSettings.PartitionKeyPath)) && v.Contains(nameof(CosmosSinkSettings.RecreateContainer))));
87+
}
88+
89+
[TestMethod]
90+
public void GetValidationErrors_WhenWriteModeIsStream_RequiresPartitionKeyPath()
91+
{
92+
var settings = new CosmosSinkSettings
93+
{
94+
ConnectionString = "AccountEndpoint=https://localhost:8081/;AccountKey=",
95+
Database = "db",
96+
Container = "container",
97+
RecreateContainer = false,
98+
WriteMode = DataWriteMode.InsertStream,
99+
};
100+
101+
var validationErrors = settings.GetValidationErrors();
102+
LogErrors(validationErrors);
103+
104+
Assert.AreEqual(1, validationErrors.Count(v => v.Contains(nameof(CosmosSinkSettings.PartitionKeyPath)) && v.Contains(nameof(CosmosSinkSettings.WriteMode))));
78105
}
79106

80107
[TestMethod]
@@ -86,8 +113,46 @@ public void GetValidationErrors_WhenDbContainerMissing_ReturnsErrors()
86113
};
87114

88115
var validationErrors = settings.GetValidationErrors();
116+
LogErrors(validationErrors);
89117

90118
Assert.AreEqual(1, validationErrors.Count(v => v.Contains(nameof(CosmosSinkSettings.Database))));
91119
Assert.AreEqual(1, validationErrors.Count(v => v.Contains(nameof(CosmosSinkSettings.Container))));
92120
}
121+
122+
[TestMethod]
123+
public void GetValidationErrors_WhenRecreateContainerTrueAndWriteModeStreamWithPartitionKeys_Succeeds()
124+
{
125+
var settings = new CosmosSinkSettings
126+
{
127+
ConnectionString = "AccountEndpoint=https://localhost:8081/;AccountKey=",
128+
Database = "db",
129+
Container = "container",
130+
RecreateContainer = true,
131+
WriteMode = DataWriteMode.InsertStream,
132+
PartitionKeyPaths = new List<string> { "/a", "/b" },
133+
};
134+
135+
var validationErrors = settings.GetValidationErrors();
136+
LogErrors(validationErrors);
137+
138+
Assert.AreEqual(0, validationErrors.Count());
139+
}
140+
141+
[TestMethod]
142+
public void GetValidationErrors_WhenPartitionKeysInvalid_ReturnsErrors()
143+
{
144+
var settings = new CosmosSinkSettings
145+
{
146+
ConnectionString = "AccountEndpoint=https://localhost:8081/;AccountKey=",
147+
Database = "db",
148+
Container = "container",
149+
RecreateContainer = true,
150+
PartitionKeyPaths = new List<string> { "a", "b" },
151+
};
152+
153+
var validationErrors = settings.GetValidationErrors();
154+
LogErrors(validationErrors);
155+
156+
Assert.AreEqual(1, validationErrors.Count(v => v.Contains(nameof(CosmosSinkSettings.PartitionKeyPaths))));
157+
}
93158
}

Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosDataSinkExtension.cs

+15-13
Original file line numberDiff line numberDiff line change
@@ -46,19 +46,21 @@ public async Task WriteAsync(IAsyncEnumerable<IDataItem> dataItems, IConfigurati
4646
catch { }
4747
}
4848

49-
var containerProperties = new ContainerProperties
50-
{
51-
Id = settings.Container,
52-
PartitionKeyDefinitionVersion = PartitionKeyDefinitionVersion.V2,
53-
};
54-
if (settings.PartitionKeyPaths != null)
55-
{
56-
containerProperties.PartitionKeyPaths = settings.PartitionKeyPaths;
57-
}
58-
else
59-
{
60-
containerProperties.PartitionKeyPath = settings.PartitionKeyPath;
61-
}
49+
var containerProperties = new ContainerProperties
50+
{
51+
Id = settings.Container,
52+
PartitionKeyDefinitionVersion = PartitionKeyDefinitionVersion.V2,
53+
};
54+
55+
if (settings.PartitionKeyPaths != null)
56+
{
57+
logger.LogInformation("Using partition key paths: {PartitionKeyPaths}", string.Join(", ", settings.PartitionKeyPaths));
58+
containerProperties.PartitionKeyPaths = settings.PartitionKeyPaths;
59+
}
60+
else
61+
{
62+
containerProperties.PartitionKeyPath = settings.PartitionKeyPath;
63+
}
6264

6365
ThroughputProperties? throughputProperties = settings.IsServerlessAccount
6466
? null

Extensions/Cosmos/Cosmos.DataTransfer.CosmosExtension/CosmosSinkSettings.cs

+30-11
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
using System.Collections.Generic;
2-
using System.ComponentModel.DataAnnotations;
1+
using System.ComponentModel.DataAnnotations;
32
using Cosmos.DataTransfer.Interfaces;
4-
using Cosmos.DataTransfer.Interfaces.Manifest;
5-
using Microsoft.Azure.Cosmos;
63

74
namespace Cosmos.DataTransfer.CosmosExtension
85
{
@@ -16,7 +13,7 @@ public class CosmosSinkSettings : CosmosSettingsBase, IDataExtensionSettings
1613
public int? CreatedContainerMaxThroughput { get; set; }
1714
public bool UseAutoscaleForCreatedContainer { get; set; } = true;
1815
public bool IsServerlessAccount { get; set; } = false;
19-
public DataWriteMode WriteMode { get; set; } = DataWriteMode.InsertStream;
16+
public DataWriteMode WriteMode { get; set; } = DataWriteMode.Insert;
2017
public List<string>? PartitionKeyPaths { get; set; }
2118

2219
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
@@ -33,20 +30,42 @@ public override IEnumerable<ValidationResult> Validate(ValidationContext validat
3330
yield return new ValidationResult("RBAC auth does not support Container creation", new[] { nameof(UseRbacAuth) });
3431
}
3532

36-
if (string.IsNullOrWhiteSpace(PartitionKeyPath))
33+
if (MissingPartitionKeys())
3734
{
38-
yield return new ValidationResult("PartitionKeyPath must be specified when RecreateContainer is true", new[] { nameof(PartitionKeyPath) });
35+
yield return new ValidationResult("PartitionKeyPath must be specified when RecreateContainer is true", new[] { nameof(PartitionKeyPath), nameof(PartitionKeyPaths) });
3936
}
4037
}
41-
if (!string.IsNullOrWhiteSpace(PartitionKeyPath) && !PartitionKeyPath.StartsWith("/"))
38+
39+
if (PartitionKeyPaths?.Any(p => !string.IsNullOrEmpty(p)) == true)
40+
{
41+
if (PartitionKeyPaths.Any(p => !p.StartsWith("/")))
42+
{
43+
yield return new ValidationResult("PartitionKeyPaths values must start with /", new[] { nameof(PartitionKeyPaths) });
44+
}
45+
}
46+
else if (!string.IsNullOrWhiteSpace(PartitionKeyPath))
4247
{
43-
yield return new ValidationResult("PartitionKeyPath must start with /", new[] { nameof(PartitionKeyPath) });
48+
if (!PartitionKeyPath.StartsWith("/"))
49+
{
50+
yield return new ValidationResult("PartitionKeyPath must start with /", new[] { nameof(PartitionKeyPath) });
51+
}
4452
}
4553

46-
if (string.IsNullOrWhiteSpace(PartitionKeyPath) && WriteMode is DataWriteMode.InsertStream or DataWriteMode.UpsertStream)
54+
if (MissingPartitionKeys() && WriteMode is DataWriteMode.InsertStream or DataWriteMode.UpsertStream)
4755
{
48-
yield return new ValidationResult("PartitionKeyPath must be specified when WriteMode is set to InsertStream or UpsertStream", new[] { nameof(PartitionKeyPath), nameof(WriteMode) });
56+
yield return new ValidationResult("PartitionKeyPath must be specified when WriteMode is set to InsertStream or UpsertStream", new[] { nameof(PartitionKeyPath), nameof(PartitionKeyPaths), nameof(WriteMode) });
4957
}
5058
}
59+
60+
private bool MissingPartitionKeys()
61+
{
62+
if (!string.IsNullOrWhiteSpace(PartitionKeyPath))
63+
return false;
64+
65+
if (PartitionKeyPaths?.Any(p => !string.IsNullOrEmpty(p)) == true)
66+
return false;
67+
68+
return true;
69+
}
5170
}
5271
}

0 commit comments

Comments
 (0)