Skip to content

Commit a9e902c

Browse files
committed
feat: Get/Create/Update a user
1 parent 7ff129a commit a9e902c

31 files changed

+1443
-222
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,4 +442,5 @@ $RECYCLE.BIN/
442442
!.vscode/extensions.json
443443

444444
## Ingenium Defaults
445-
artefacts/*
445+
artefacts/*
446+
**/appsettings.env.json

SailthruSDK.sln

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{89340FB5
5555
EndProject
5656
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SailthruSDK.Tests", "tests\SailthruSDK.Tests\SailthruSDK.Tests.csproj", "{2E0DE426-D135-4710-80B5-173338D364C6}"
5757
EndProject
58-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SailthruSDK.Extensions.DependencyInjection", "libs\SailthruSDK.Extensions.DependencyInjection\SailthruSDK.Extensions.DependencyInjection.csproj", "{806DCE4B-D0BD-4AA5-B8BC-502B69ABFAF3}"
58+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SailthruSDK.Extensions.DependencyInjection", "libs\SailthruSDK.Extensions.DependencyInjection\SailthruSDK.Extensions.DependencyInjection.csproj", "{806DCE4B-D0BD-4AA5-B8BC-502B69ABFAF3}"
59+
EndProject
60+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{0876C34F-5AAD-405F-9303-90145D7744CF}"
61+
EndProject
62+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SailthruSDK.Samples.Console", "samples\SailthruSDK.Samples.Console\SailthruSDK.Samples.Console.csproj", "{5CEF974B-EAA0-4DB2-9812-C8EFB74ACC96}"
5963
EndProject
6064
Global
6165
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -83,6 +87,10 @@ Global
8387
{806DCE4B-D0BD-4AA5-B8BC-502B69ABFAF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
8488
{806DCE4B-D0BD-4AA5-B8BC-502B69ABFAF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
8589
{806DCE4B-D0BD-4AA5-B8BC-502B69ABFAF3}.Release|Any CPU.Build.0 = Release|Any CPU
90+
{5CEF974B-EAA0-4DB2-9812-C8EFB74ACC96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
91+
{5CEF974B-EAA0-4DB2-9812-C8EFB74ACC96}.Debug|Any CPU.Build.0 = Debug|Any CPU
92+
{5CEF974B-EAA0-4DB2-9812-C8EFB74ACC96}.Release|Any CPU.ActiveCfg = Release|Any CPU
93+
{5CEF974B-EAA0-4DB2-9812-C8EFB74ACC96}.Release|Any CPU.Build.0 = Release|Any CPU
8694
EndGlobalSection
8795
GlobalSection(SolutionProperties) = preSolution
8896
HideSolutionNode = FALSE
@@ -95,6 +103,7 @@ Global
95103
{562C63D3-F1F8-4929-B856-1463283DFC93} = {3E233261-9EBB-4F79-B840-BD73052C8428}
96104
{2E0DE426-D135-4710-80B5-173338D364C6} = {89340FB5-E457-4182-A510-7F1D8DD97371}
97105
{806DCE4B-D0BD-4AA5-B8BC-502B69ABFAF3} = {3E233261-9EBB-4F79-B840-BD73052C8428}
106+
{5CEF974B-EAA0-4DB2-9812-C8EFB74ACC96} = {0876C34F-5AAD-405F-9303-90145D7744CF}
98107
EndGlobalSection
99108
GlobalSection(ExtensibilityGlobals) = postSolution
100109
SolutionGuid = {B7D62062-4558-4545-9EE0-3A9B24CFD70D}

libs/SailthruSDK.Extensions.DependencyInjection/SailthruSDK.Extensions.DependencyInjection.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
<PropertyGroup>
44
<TargetFramework>netstandard2.0</TargetFramework>
5+
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
6+
<Version>0.1.0</Version>
57
</PropertyGroup>
68

79
<ItemGroup>

libs/SailthruSDK.Extensions.DependencyInjection/ServiceCollectionExtensions.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public static class ServiceCollectionExtensions
2323
/// <param name="configure">The configure delegate.</param>
2424
/// <returns>The services collection.</returns>
2525
public static IServiceCollection AddSailthru(
26-
IServiceCollection services,
26+
this IServiceCollection services,
2727
Action<SailthruSettings> configure)
2828
{
2929
Ensure.IsNotNull(services, nameof(services));
@@ -43,13 +43,13 @@ public static IServiceCollection AddSailthru(
4343
/// <param name="settings">The Sailthru settings.</param>
4444
/// <returns>The services collection.</returns>
4545
public static IServiceCollection AddSailthru(
46-
IServiceCollection services,
46+
this IServiceCollection services,
4747
SailthruSettings settings)
4848
{
4949
Ensure.IsNotNull(services, nameof(services));
5050
Ensure.IsNotNull(settings, nameof(settings));
5151

52-
services.AddSingleton(Options.Create(settings));
52+
services.AddSingleton(settings.AsOptions());
5353

5454
AddCoreServices(services);
5555

@@ -60,16 +60,16 @@ public static IServiceCollection AddSailthru(
6060
/// Adds Sailthru services to the given services collection.
6161
/// </summary>
6262
/// <param name="services">The services collection.</param>
63-
/// <param name="configurationSection">The configuration section.</param>
63+
/// <param name="configuration">The configuration.</param>
6464
/// <returns>The services collection.</returns>
6565
public static IServiceCollection AddSailthru(
66-
IServiceCollection services,
67-
IConfigurationSection configurationSection)
66+
this IServiceCollection services,
67+
IConfiguration configuration)
6868
{
6969
Ensure.IsNotNull(services, nameof(services));
70-
Ensure.IsNotNull(configurationSection, nameof(configurationSection));
70+
Ensure.IsNotNull(configuration, nameof(configuration));
7171

72-
services.Configure<SailthruSettings>(configurationSection);
72+
services.Configure<SailthruSettings>(configuration.GetSection(SailthruSettings.ConfigurationSection));
7373

7474
AddCoreServices(services);
7575

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace SailthruSDK.Converters
2+
{
3+
using System.Text.Json;
4+
using System.Text.Json.Serialization;
5+
6+
/// <summary>
7+
/// Provides a base implementation fo a converter.
8+
/// </summary>
9+
/// <typeparam name="T">The model type.</typeparam>
10+
internal abstract class ConverterBase<T> : JsonConverter<T>
11+
{
12+
protected string GetName(string name, JsonSerializerOptions options)
13+
=> options.PropertyNamingPolicy!.ConvertName(name);
14+
}
15+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
namespace SailthruSDK.Converters
2+
{
3+
using System;
4+
using System.Globalization;
5+
using System.Text.Json;
6+
7+
/// <summary>
8+
/// Provides conversion of <see cref="DateTimeOffset"/> values using the format used by Sailthru.
9+
/// </summary>
10+
internal class NullableDateTimeOffsetConverter : ConverterBase<DateTimeOffset?>
11+
{
12+
const string DateTimeFormat = "ddd, dd MMM yyyy HH:mm:ss";
13+
const string TimeZoneFormat = "hhmm";
14+
15+
/// <inheritdoc />
16+
public override DateTimeOffset? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
17+
{
18+
string? value = reader.GetString();
19+
if (value is { Length: > 0 })
20+
{
21+
string suffix = value.Substring(value.Length - 5, 5).Trim();
22+
value = value.Substring(0, value.Length - 5).Trim();
23+
24+
bool isNegative = suffix.StartsWith("-");
25+
suffix = suffix.TrimStart('-', '+');
26+
27+
if (TimeSpan.TryParseExact(suffix, TimeZoneFormat, CultureInfo.InvariantCulture, out var timeZone))
28+
{
29+
if (isNegative)
30+
{
31+
timeZone = -timeZone;
32+
}
33+
}
34+
else
35+
{
36+
return default;
37+
}
38+
39+
if (DateTime.TryParseExact(value, DateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var dateTime))
40+
{
41+
return new DateTimeOffset(dateTime, timeZone);
42+
}
43+
44+
return default;
45+
}
46+
47+
return default;
48+
}
49+
50+
/// <inheritdoc />
51+
public override void Write(Utf8JsonWriter writer, DateTimeOffset? value, JsonSerializerOptions options) { }
52+
}
53+
54+
/// <summary>
55+
/// Provides conversion of <see cref="DateTimeOffset"/> values using the format used by Sailthru.
56+
/// </summary>
57+
internal class DateTimeOffsetConverter : ConverterBase<DateTimeOffset>
58+
{
59+
const string DateTimeFormat = "ddd, dd MMM yyyy HH:mm:ss";
60+
const string TimeZoneFormat = "hhmm";
61+
62+
/// <inheritdoc />
63+
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
64+
{
65+
string? value = reader.GetString();
66+
if (value is { Length: > 0 })
67+
{
68+
string suffix = value.Substring(value.Length - 5, 5).Trim();
69+
value = value.Substring(0, value.Length - 5).Trim();
70+
71+
bool isNegative = suffix.StartsWith("-");
72+
suffix = suffix.TrimStart('-', '+');
73+
74+
if (TimeSpan.TryParseExact(suffix, TimeZoneFormat, CultureInfo.InvariantCulture, out var timeZone))
75+
{
76+
if (isNegative)
77+
{
78+
timeZone = -timeZone;
79+
}
80+
}
81+
else
82+
{
83+
return default;
84+
}
85+
86+
if (DateTime.TryParseExact(value, DateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out var dateTime))
87+
{
88+
return new DateTimeOffset(dateTime, timeZone);
89+
}
90+
91+
return default;
92+
}
93+
94+
return default;
95+
}
96+
97+
/// <inheritdoc />
98+
public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options) { }
99+
}
100+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
namespace SailthruSDK.Converters
2+
{
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Text.Json;
7+
using System.Text.Json.Serialization;
8+
9+
/// <summary>
10+
/// Deserializes a <see cref="Map{TValue}"/> type.
11+
/// </summary>
12+
/// <typeparam name="T">The element type.</typeparam>
13+
internal class MapConverter<T> : ConverterBase<Map<T>>
14+
{
15+
public override Map<T>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
16+
{
17+
var map = new Map<T>();
18+
var source = JsonSerializer.Deserialize<Dictionary<string, T>>(ref reader, options);
19+
if (source is { Count: >0 })
20+
{
21+
foreach (var pair in source)
22+
{
23+
map.Add(pair.Key, pair.Value);
24+
}
25+
}
26+
27+
return map;
28+
}
29+
30+
public override void Write(Utf8JsonWriter writer, Map<T> value, JsonSerializerOptions options)
31+
{
32+
if (value is null)
33+
{
34+
writer.WriteNullValue();
35+
}
36+
else
37+
{
38+
writer.WriteStartObject();
39+
var converter = options.GetConverter(typeof(T)) as JsonConverter<T>;
40+
41+
if (value.Count > 0)
42+
{
43+
foreach (var pair in value.OrderBy(p => p.Key, StringComparer.Ordinal))
44+
{
45+
writer.WritePropertyName(GetName(pair.Key, options));
46+
47+
if (converter is null)
48+
{
49+
JsonSerializer.Serialize(writer, pair.Value, options);
50+
}
51+
else
52+
{
53+
converter.Write(writer, pair.Value, options);
54+
}
55+
}
56+
}
57+
58+
writer.WriteEndObject();
59+
}
60+
}
61+
}
62+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
namespace SailthruSDK.Converters
2+
{
3+
using System;
4+
using System.Text.Json;
5+
6+
/// <summary>
7+
/// Represents a converter for handling nullable enum values.
8+
/// </summary>
9+
/// <typeparam name="TEnum">The enum type.</typeparam>
10+
internal class NullableEnumConverter<TEnum> : ConverterBase<TEnum?>
11+
where TEnum : struct
12+
{
13+
/// <inheritdoc />
14+
public override TEnum? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
15+
{
16+
string? value = reader.GetString();
17+
if (value is { Length: > 0 })
18+
{
19+
if (Enum.TryParse<TEnum>(value, true, out var enumValue))
20+
{
21+
return enumValue;
22+
}
23+
24+
return default;
25+
}
26+
else
27+
{
28+
return default;
29+
}
30+
}
31+
32+
/// <inheritdoc />
33+
public override void Write(Utf8JsonWriter writer, TEnum? value, JsonSerializerOptions options)
34+
{
35+
if (value.HasValue)
36+
{
37+
writer.WriteStringValue(value.Value.ToString());
38+
}
39+
else
40+
{
41+
writer.WriteNullValue();
42+
}
43+
}
44+
}
45+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
namespace SailthruSDK.Converters
2+
{
3+
using System;
4+
using System.Text.Json;
5+
using System.Text.Json.Serialization;
6+
7+
/// <summary>
8+
/// Serializes a <see cref="OneOf{TFirst, TSecond}"/> type.
9+
/// </summary>
10+
/// <typeparam name="TFirst">The first type.</typeparam>
11+
/// <typeparam name="TSecond">The second type.</typeparam>
12+
internal class OneOfConverter<TFirst, TSecond> : ConverterBase<OneOf<TFirst, TSecond>>
13+
{
14+
public override OneOf<TFirst, TSecond> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
15+
=> default;
16+
17+
public override void Write(Utf8JsonWriter writer, OneOf<TFirst, TSecond> value, JsonSerializerOptions options)
18+
{
19+
if (value.HasValue)
20+
{
21+
if (value.IsFirst)
22+
{
23+
Write(writer, value.First, options);
24+
}
25+
else
26+
{
27+
Write(writer, value.Second, options);
28+
}
29+
}
30+
else
31+
{
32+
writer.WriteNullValue();
33+
}
34+
}
35+
36+
static void Write<TValue>(Utf8JsonWriter writer, TValue value, JsonSerializerOptions options)
37+
{
38+
var converter = options.GetConverter(typeof(TValue)) as JsonConverter<TValue>;
39+
if (converter is not null)
40+
{
41+
converter.Write(writer, value, options);
42+
}
43+
else
44+
{
45+
JsonSerializer.Serialize(writer, value, options);
46+
}
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)