Skip to content

Commit 124b15e

Browse files
committed
feat: Create/Update a purchase
1 parent a9e902c commit 124b15e

File tree

10 files changed

+357
-48
lines changed

10 files changed

+357
-48
lines changed

libs/SailthruSDK/JsonUtility.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Text.Json.Serialization;
66

77
using SailthruSDK.Converters;
8+
using SailthruSDK.Purchase;
89
using SailthruSDK.User;
910

1011
static class JsonUtility
@@ -20,6 +21,7 @@ public static JsonSerializerOptions GetSerializerOptions()
2021
/** MODELS **/
2122
options.Converters.Add(new GetUserRequest.Converter());
2223
options.Converters.Add(new UpsertUserRequest.Converter());
24+
options.Converters.Add(new UpsertPurchaseRequest.Convereter());
2325
options.Converters.Add(new DateTimeOffsetConverter());
2426
options.Converters.Add(new NullableDateTimeOffsetConverter());
2527
options.Converters.Add(new OneOfConverter<bool, int>());

libs/SailthruSDK/JsonWriterExtensions.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,36 @@ public static void WriteStringProperty(
5757
}
5858
}
5959

60+
public static void WriteNumberProperty(
61+
this Utf8JsonWriter writer,
62+
string key,
63+
int value,
64+
JsonSerializerOptions options)
65+
{
66+
if (writer is null)
67+
{
68+
throw new ArgumentNullException(nameof(writer));
69+
}
70+
71+
writer.WritePropertyName(GetName(key, options));
72+
writer.WriteNumberValue(value);
73+
}
74+
75+
public static void WriteBooleanProperty(
76+
this Utf8JsonWriter writer,
77+
string key,
78+
bool value,
79+
JsonSerializerOptions options)
80+
{
81+
if (writer is null)
82+
{
83+
throw new ArgumentNullException(nameof(writer));
84+
}
85+
86+
writer.WritePropertyName(GetName(key, options));
87+
writer.WriteBooleanValue(value);
88+
}
89+
6090
public static void WriteMapProperty<TValue>(
6191
this Utf8JsonWriter writer,
6292
string key,
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
namespace SailthruSDK
2+
{
3+
using System.Net.Http;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
7+
using SailthruSDK.Purchase;
8+
9+
public static class SailthruPurchaseExtensions
10+
{
11+
public static async Task<SailthruResponse> UpsertPurchaseAsync(
12+
this SailthruClient client,
13+
string email,
14+
PurchaseItem[] items,
15+
bool incomplete = false,
16+
CancellationToken cancellationToken = default)
17+
{
18+
Ensure.IsNotNull(client, nameof(client));
19+
20+
var model = new UpsertPurchaseRequest(email, items, incomplete);
21+
var request = new SailthruRequest<UpsertPurchaseRequest>(
22+
HttpMethod.Post,
23+
SailthruEndpoints.Purchase,
24+
model);
25+
26+
var response = await client.SendAsync(request, cancellationToken)
27+
.ConfigureAwait(false);
28+
29+
return response;
30+
}
31+
}
32+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
using System;
2+
using System.Text.Json;
3+
4+
using SailthruSDK.Converters;
5+
6+
namespace SailthruSDK.Purchase
7+
{
8+
/// <summary>
9+
/// Represents a request to create or update a purchase
10+
/// </summary>
11+
public class UpsertPurchaseRequest
12+
{
13+
/// <summary>
14+
/// Initialises a new instance of <see cref="UpsertPurchaseRequest"/>
15+
/// </summary>
16+
/// <param name="email">The user email address</param>
17+
/// <param name="items">The set of items.</param>
18+
/// <param name="incomplete">Specifies whether the purchase is incomplete (e.g. an active cart, not an order)</param>
19+
public UpsertPurchaseRequest(
20+
string email,
21+
PurchaseItem[] items,
22+
bool incomplete = false)
23+
{
24+
Email = Ensure.IsNotNullOrEmpty(email, nameof(email));
25+
Items = Ensure.IsNotNull(items, nameof(items));
26+
Incomplete = incomplete;
27+
}
28+
29+
/// <summary>
30+
/// Gets the email address.
31+
/// </summary>
32+
public string Email { get; }
33+
34+
/// <summary>
35+
/// Gets whether the purchase is incomplete.
36+
/// </summary>
37+
public bool Incomplete { get; }
38+
39+
/// <summary>
40+
/// Gets the set of purchase items.
41+
/// </summary>
42+
public PurchaseItem[] Items { get; }
43+
44+
internal class Convereter : ConverterBase<UpsertPurchaseRequest>
45+
{
46+
/// <inhertdoc />
47+
public override UpsertPurchaseRequest? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
48+
{
49+
throw new NotImplementedException();
50+
}
51+
52+
/// <inhertdoc />
53+
public override void Write(Utf8JsonWriter writer, UpsertPurchaseRequest value, JsonSerializerOptions options)
54+
{
55+
if (value is null)
56+
{
57+
writer.WriteNullValue();
58+
}
59+
else
60+
{
61+
writer.WriteStartObject();
62+
63+
writer.WriteStringProperty("email", value.Email, options);
64+
writer.WriteBooleanProperty("incomplete", value.Incomplete, options);
65+
66+
writer.WritePropertyName("items");
67+
writer.WriteStartArray();
68+
69+
foreach (var item in value.Items)
70+
{
71+
writer.WriteStartObject();
72+
73+
writer.WriteStringProperty("id", item.Id, options);
74+
writer.WriteStringProperty("title", item.Title, options);
75+
writer.WriteNumberProperty("price", item.Price, options);
76+
writer.WriteNumberProperty("qty", item.Quantity, options);
77+
writer.WriteStringProperty("url", item.Url, options);
78+
79+
writer.WriteEndObject();
80+
}
81+
82+
writer.WriteEndArray();
83+
writer.WriteEndObject();
84+
}
85+
}
86+
}
87+
}
88+
89+
/// <summary>
90+
/// Represents a purchase item.
91+
/// </summary>
92+
public class PurchaseItem
93+
{
94+
public PurchaseItem(
95+
string id,
96+
string title,
97+
int price,
98+
int quantity,
99+
string url)
100+
{
101+
Id = Ensure.IsNotNullOrEmpty(id, nameof(id));
102+
Title = Ensure.IsNotNullOrEmpty(title, nameof(title));
103+
Price = price;
104+
Quantity = quantity;
105+
Url = Ensure.IsNotNullOrEmpty(url, nameof(url));
106+
}
107+
108+
/// <summary>
109+
/// Gets the ID.
110+
/// </summary>
111+
public string Id { get; }
112+
113+
/// <summary>
114+
/// Gets the item title.
115+
/// </summary>
116+
public string Title { get; }
117+
118+
/// <summary>
119+
/// Gets the price.
120+
/// </summary>
121+
public int Price { get; }
122+
123+
/// <summary>
124+
/// Gets the quantity.
125+
/// </summary>
126+
public int Quantity { get; }
127+
128+
/// <summary>
129+
/// Gets the URL.
130+
/// </summary>
131+
public string Url { get; }
132+
}
133+
}

libs/SailthruSDK/SailthruClient.cs

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Collections.Generic;
55
using System.Net.Http;
66
using System.Text.Json;
7+
using System.Text.Json.Serialization;
78
using System.Threading;
89
using System.Threading.Tasks;
910

@@ -33,6 +34,58 @@ internal async Task<SailthruResponse<TResponse>> SendAsync<TRequest, TResponse>(
3334
SailthruRequest<TRequest> request,
3435
CancellationToken cancellationToken = default)
3536
where TRequest : notnull
37+
{
38+
var httpResponse = await GetHttpResponseAsync(request, cancellationToken);
39+
40+
if (httpResponse.IsSuccessStatusCode)
41+
{
42+
string content = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
43+
44+
return SailthruResponse<TResponse>.Success(
45+
await JsonSerializer.DeserializeAsync<TResponse>(
46+
await httpResponse.Content.ReadAsStreamAsync(),
47+
_jsonOptions,
48+
cancellationToken: cancellationToken
49+
)
50+
);
51+
}
52+
53+
var error = await JsonSerializer.DeserializeAsync<Error>(
54+
await httpResponse.Content.ReadAsStreamAsync(),
55+
_jsonOptions,
56+
cancellationToken: cancellationToken)
57+
?? new Error { Code = -1, Message = "Unknown error response." };
58+
59+
return SailthruResponse<TResponse>.Failure(new SailthruError(error.Code, error.Message));
60+
}
61+
62+
internal async Task<SailthruResponse> SendAsync<TRequest>(
63+
SailthruRequest<TRequest> request,
64+
CancellationToken cancellationToken = default)
65+
where TRequest : notnull
66+
{
67+
var httpResponse = await GetHttpResponseAsync(request, cancellationToken);
68+
69+
if (httpResponse.IsSuccessStatusCode)
70+
{
71+
string content = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
72+
73+
return SailthruResponse.Success();
74+
}
75+
76+
var error = await JsonSerializer.DeserializeAsync<Error>(
77+
await httpResponse.Content.ReadAsStreamAsync(),
78+
_jsonOptions,
79+
cancellationToken: cancellationToken)
80+
?? new Error { Code = -1, Message = "Unknown error response." };
81+
82+
return SailthruResponse.Failure(new SailthruError(error.Code, error.Message));
83+
}
84+
85+
async Task<HttpResponseMessage> GetHttpResponseAsync<TRequest>(
86+
SailthruRequest<TRequest> request,
87+
CancellationToken cancellationToken)
88+
where TRequest : notnull
3689
{
3790
Ensure.IsNotNull(request, nameof(request));
3891

@@ -62,22 +115,15 @@ internal async Task<SailthruResponse<TResponse>> SendAsync<TRequest, TResponse>(
62115
var httpResponse = await _http.SendAsync(httpRequest, cancellationToken)
63116
.ConfigureAwait(false);
64117

65-
if (httpResponse.IsSuccessStatusCode)
66-
{
67-
string content = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
68-
69-
return SailthruResponse<TResponse>.Success(
70-
await JsonSerializer.DeserializeAsync<TResponse>(
71-
await httpResponse.Content.ReadAsStreamAsync(),
72-
_jsonOptions,
73-
cancellationToken: cancellationToken
74-
)
75-
);
76-
}
77-
78-
string error = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
118+
return httpResponse;
119+
}
79120

80-
return SailthruResponse<TResponse>.Error();
121+
public class Error
122+
{
123+
[JsonPropertyName("error")]
124+
public int Code { get; set; }
125+
[JsonPropertyName("errormsg")]
126+
public string Message { get; set; } = default!;
81127
}
82128
}
83129
}

libs/SailthruSDK/SailthruEndpoints.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
/// </summary>
66
public static class SailthruEndpoints
77
{
8+
public const string Purchase = "purchase";
89
public const string User = "user";
910
}
1011
}

0 commit comments

Comments
 (0)