Skip to content

Commit

Permalink
fix: Changes requested
Browse files Browse the repository at this point in the history
- Added `IValidatableObject` to `CsvWriterSettings`.
- Updated Culture to accept both `Invariant`/`Current` and `InvariantCulture`/`CurrentCulture`.
- Specified only the latter in README.
- Added unit testing for `CsvWriterSettings`.
  • Loading branch information
stefanedwards committed Jan 24, 2025
1 parent a9aa572 commit 7374087
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using System.Globalization;
using Cosmos.DataTransfer.JsonExtension.UnitTests;
using Microsoft.Extensions.Logging.Abstractions;
using System.ComponentModel.DataAnnotations;
using Cosmos.DataTransfer.CsvExtension.Settings;
using Cosmos.DataTransfer.Interfaces;
using Cosmos.DataTransfer.JsonExtension;

namespace Cosmos.DataTransfer.CsvExtension.UnitTests;

[TestClass]
public class CsvWriterSettingsTests
{


[TestMethod]
public void TestDefault(string culture) {
var settings = new CsvWriterSettings() { };

Assert.AreEqual(settings.GetCultureInfo(), CultureInfo.InvariantCulture);
Assert.AreEqual(settings.Validate(new ValidationContext(this)).Count(), 0);
}

[TestMethod]
[DataRow("invariant")]
[DataRow("Invariant")]
[DataRow("invariantCulture")]
[DataRow("invariantculture")]
public void TestInvariantCulture(string culture) {
var settings = new CsvWriterSettings() {
Culture = culture
};
Assert.AreEqual(settings.GetCultureInfo(), CultureInfo.InvariantCulture);
Assert.AreEqual(settings.Validate(new ValidationContext(this)).Count(), 0);
}

[TestMethod]
[DataRow("current")]
[DataRow("Current")]
[DataRow("currentCultuRE")]
[DataRow("currentCulture")]
public void TestCurrentCulture(string culture) {
var settings = new CsvWriterSettings() {
Culture = culture
};
Assert.AreEqual(settings.GetCultureInfo(), CultureInfo.CurrentCulture);
Assert.AreEqual(settings.Validate(new ValidationContext(this)).Count(), 0);
}

[TestMethod]
public void TestCurrentCultureByName() {
var settings = new CsvWriterSettings() {
Culture = CultureInfo.CurrentCulture.Name
};
Assert.AreEqual(settings.GetCultureInfo(), CultureInfo.CurrentCulture);
Assert.AreEqual(settings.Validate(new ValidationContext(this)).Count(), 0);
}

[TestMethod]
public void TestCultureFails() {
var settings = new CsvWriterSettings() {
Culture = "not a culture"
};
var results = settings.Validate(new ValidationContext(this));
Assert.AreEqual(results.Count(), 1);
Assert.AreEqual(results.First().ErrorMessage, "Could not find CultureInfo `not a culture` on this system.");
}

[TestMethod]
[DataRow(null)]
[DataRow("")]
public void TestCultureMissing(string culture) {
var settings = new CsvWriterSettings() {
Culture = culture
};
var results = settings.Validate(new ValidationContext(this));
Assert.AreEqual(results.Count(), 1);
Assert.AreEqual(results.First().ErrorMessage, "Culture missing.");
}

[TestMethod]
public async Task TestDanishCulture() {
var outputFile = Path.GetTempFileName();
var config = TestHelpers.CreateConfig(new Dictionary<string, string>
{
{ "FilePath", outputFile },
{ "IncludeHeader", "false" },
{ "Culture", "da-DK" },
{ "Delimiter", ";" }
});

var data = new List<DictionaryDataItem>
{
new(new Dictionary<string, object?>
{
{ "Value", 1.2 }
})
};

var sink = new CsvFileSink();

await sink.WriteAsync(data.ToAsyncEnumerable(), config, new JsonFileSource(), NullLogger.Instance);
var result = await File.ReadAllTextAsync(outputFile);
Assert.AreEqual(result, "1,2");
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,47 @@
using System.Globalization;
using Cosmos.DataTransfer.Interfaces;
using System.ComponentModel.DataAnnotations;

namespace Cosmos.DataTransfer.CsvExtension.Settings;

public class CsvWriterSettings : IDataExtensionSettings
public class CsvWriterSettings : IDataExtensionSettings, IValidatableObject
{
public bool IncludeHeader { get; set; } = true;
public string Delimiter { get; set; } = ",";
public string Culture { get; set; } = "InvariantCulture";
public CultureInfo GetCultureInfo() {
switch (this.Culture.ToLower())
switch (this.Culture?.ToLower())
{
case "invariantculture": return CultureInfo.InvariantCulture;
case "current": return CultureInfo.CurrentCulture;
default: return CultureInfo.GetCultureInfo(this.Culture);
case "invariant":
case "invariantculture":
return CultureInfo.InvariantCulture;
case "current":
case "currentculture":
return CultureInfo.CurrentCulture;
default: return CultureInfo.GetCultureInfo(this.Culture!);
}
}

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
ValidationResult? result = null;
try {
_ = this.GetCultureInfo();
} catch (CultureNotFoundException) {
result = new ValidationResult(
$"Could not find CultureInfo `{this.Culture}` on this system.",
new string[] { "Culture" }
);
} catch (ArgumentNullException) {
result = new ValidationResult(
$"Culture missing.",
new string[] { "Culture" }
);
}


if (result != null) {
yield return result;
}
}
}
6 changes: 4 additions & 2 deletions Extensions/Csv/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@ Source supports an optional `Delimiter` parameter (`,` by default) and an option

Sink supports an optional `Delimiter` parameter (`,` by default) and an optional `IncludeHeader` parameter (`true` by default) to add a leading row of column names.

Formatting options, or locale, can be set with an optional `Culture` setting (`"Invariant"` by default).
Formatting options, or locale, can be set with an optional `Culture` setting (`"InvariantCulture"` by default).
This specifies how e.g., numbers and dates are formatted according to a specific culture.
Set to `"Current"` to use the system's or process' current locale setting
Set to `"InvariantCulture"` to use the system's or process' current locale setting
(see [CultureInfo.CurrentCulture](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo.currentculture)),
or e.g., `"en"`, `"en-GB"`, or `"en-US"` for English standards (period, `.`, as decimal separator and other regional standards),
"da-DK" for Danish (comma, `,`, as decimal separator), etc.
Note, if using a culture with comma as decimal separator, specify a different delimiter (e.g., semi-colon, `;`), else all numbers
will be written enclosed with quotes.

```json
{
Expand Down

0 comments on commit 7374087

Please sign in to comment.