Skip to content

Commit 9cfe4fe

Browse files
committed
fix(client): normalize date filter query timestamps
addresses #3
1 parent 9049a29 commit 9cfe4fe

11 files changed

Lines changed: 226 additions & 27 deletions

File tree

openapi/goaffpro-canonical.yaml

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ info:
1414
## Date Format
1515
All timestamp parameters (`start_time`, `end_time`, `created_at_min`,
1616
`created_at_max`) accept ISO-8601 strings with UTC offset, e.g.
17-
`2026-02-24T11:57:26.000Z`. The `created_at` fields in responses also follow
18-
this format.
17+
`2026-02-24T11:57:26.000Z`. Preferred wire form for requests is
18+
`yyyy-MM-ddTHH:mm:ss.000Z`, though equivalent RFC 3339 / OpenAPI `date-time`
19+
values may also be accepted by the upstream API. The `created_at` fields in
20+
responses also follow this format.
1921
2022
## Monetary strings
2123
Many monetary values are returned as decimal strings (e.g. `"1000.23"`) rather
@@ -213,7 +215,8 @@ paths:
213215
an empty result set (confirmed empirically on 2026-02).
214216
215217
Date range filtering uses `created_at_min` / `created_at_max`, both accepting
216-
ISO-8601 UTC timestamps (e.g. `2026-02-24T00:00:00.000Z`).
218+
ISO-8601 UTC timestamps (e.g. `2026-02-24T00:00:00.000Z`). Preferred wire
219+
form is `yyyy-MM-ddTHH:mm:ss.000Z`.
217220
218221
The `fields` parameter is required.
219222
@@ -585,8 +588,9 @@ components:
585588
name: start_time
586589
description: >
587590
Start of the time window for filtering. ISO-8601 UTC timestamp,
588-
e.g. `2026-02-01T00:00:00.000Z`. Exact filtering behaviour has not been
589-
fully verified against the API.
591+
e.g. `2026-02-01T00:00:00.000Z`. Preferred wire form is
592+
`yyyy-MM-ddTHH:mm:ss.000Z`. Exact filtering behaviour has not been fully
593+
verified against the API.
590594
schema:
591595
type: string
592596
format: date-time
@@ -595,8 +599,9 @@ components:
595599
name: end_time
596600
description: >
597601
End of the time window for filtering. ISO-8601 UTC timestamp,
598-
e.g. `2026-02-28T23:59:59.000Z`. Exact filtering behaviour has not been
599-
fully verified against the API.
602+
e.g. `2026-02-28T23:59:59.000Z`. Preferred wire form is
603+
`yyyy-MM-ddTHH:mm:ss.000Z`. Exact filtering behaviour has not been fully
604+
verified against the API.
600605
schema:
601606
type: string
602607
format: date-time
@@ -623,7 +628,8 @@ components:
623628
name: created_at_max
624629
description: >
625630
Upper bound for `created_at` filtering. ISO-8601 UTC timestamp,
626-
e.g. `2026-02-28T23:59:59.000Z`.
631+
e.g. `2026-02-28T23:59:59.000Z`. Preferred wire form is
632+
`yyyy-MM-ddTHH:mm:ss.000Z`.
627633
schema:
628634
type: string
629635
format: date-time
@@ -632,7 +638,8 @@ components:
632638
name: created_at_min
633639
description: >
634640
Lower bound for `created_at` filtering. ISO-8601 UTC timestamp,
635-
e.g. `2026-02-01T00:00:00.000Z`.
641+
e.g. `2026-02-01T00:00:00.000Z`. Preferred wire form is
642+
`yyyy-MM-ddTHH:mm:ss.000Z`.
636643
schema:
637644
type: string
638645
format: date-time

src/GoAffPro.Client.Generated/Generated/User/Feed/Orders/OrdersRequestBuilder.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public OrdersRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : bas
3434
{
3535
}
3636
/// <summary>
37-
/// Returns orders attributed to the authenticated affiliate across enrolled stores.**IMPORTANT:** `limit` and `offset` must be provided or the endpoint returns an empty result set (confirmed empirically on 2026-02).Date range filtering uses `created_at_min` / `created_at_max`, both accepting ISO-8601 UTC timestamps (e.g. `2026-02-24T00:00:00.000Z`).The `fields` parameter is required.**Note:** `website` and `store_name` appear in order objects regardless of the `fields` selection; they may always be returned.
37+
/// Returns orders attributed to the authenticated affiliate across enrolled stores.**IMPORTANT:** `limit` and `offset` must be provided or the endpoint returns an empty result set (confirmed empirically on 2026-02).Date range filtering uses `created_at_min` / `created_at_max`, both accepting ISO-8601 UTC timestamps (e.g. `2026-02-24T00:00:00.000Z`). Preferred wire form is `yyyy-MM-ddTHH:mm:ss.000Z`.The `fields` parameter is required.**Note:** `website` and `store_name` appear in order objects regardless of the `fields` selection; they may always be returned.
3838
/// </summary>
3939
/// <returns>A <see cref="global::GoAffPro.Client.Generated.Models.UserOrderFeedResponse"/></returns>
4040
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
@@ -61,7 +61,7 @@ public OrdersRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : bas
6161
return await RequestAdapter.SendAsync<global::GoAffPro.Client.Generated.Models.UserOrderFeedResponse>(requestInfo, global::GoAffPro.Client.Generated.Models.UserOrderFeedResponse.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
6262
}
6363
/// <summary>
64-
/// Returns orders attributed to the authenticated affiliate across enrolled stores.**IMPORTANT:** `limit` and `offset` must be provided or the endpoint returns an empty result set (confirmed empirically on 2026-02).Date range filtering uses `created_at_min` / `created_at_max`, both accepting ISO-8601 UTC timestamps (e.g. `2026-02-24T00:00:00.000Z`).The `fields` parameter is required.**Note:** `website` and `store_name` appear in order objects regardless of the `fields` selection; they may always be returned.
64+
/// Returns orders attributed to the authenticated affiliate across enrolled stores.**IMPORTANT:** `limit` and `offset` must be provided or the endpoint returns an empty result set (confirmed empirically on 2026-02).Date range filtering uses `created_at_min` / `created_at_max`, both accepting ISO-8601 UTC timestamps (e.g. `2026-02-24T00:00:00.000Z`). Preferred wire form is `yyyy-MM-ddTHH:mm:ss.000Z`.The `fields` parameter is required.**Note:** `website` and `store_name` appear in order objects regardless of the `fields` selection; they may always be returned.
6565
/// </summary>
6666
/// <returns>A <see cref="RequestInformation"/></returns>
6767
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
@@ -89,15 +89,15 @@ public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<gl
8989
return new global::GoAffPro.Client.Generated.User.Feed.Orders.OrdersRequestBuilder(rawUrl, RequestAdapter);
9090
}
9191
/// <summary>
92-
/// Returns orders attributed to the authenticated affiliate across enrolled stores.**IMPORTANT:** `limit` and `offset` must be provided or the endpoint returns an empty result set (confirmed empirically on 2026-02).Date range filtering uses `created_at_min` / `created_at_max`, both accepting ISO-8601 UTC timestamps (e.g. `2026-02-24T00:00:00.000Z`).The `fields` parameter is required.**Note:** `website` and `store_name` appear in order objects regardless of the `fields` selection; they may always be returned.
92+
/// Returns orders attributed to the authenticated affiliate across enrolled stores.**IMPORTANT:** `limit` and `offset` must be provided or the endpoint returns an empty result set (confirmed empirically on 2026-02).Date range filtering uses `created_at_min` / `created_at_max`, both accepting ISO-8601 UTC timestamps (e.g. `2026-02-24T00:00:00.000Z`). Preferred wire form is `yyyy-MM-ddTHH:mm:ss.000Z`.The `fields` parameter is required.**Note:** `website` and `store_name` appear in order objects regardless of the `fields` selection; they may always be returned.
9393
/// </summary>
9494
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
9595
public partial class OrdersRequestBuilderGetQueryParameters
9696
{
97-
/// <summary>Upper bound for `created_at` filtering. ISO-8601 UTC timestamp, e.g. `2026-02-28T23:59:59.000Z`.</summary>
97+
/// <summary>Upper bound for `created_at` filtering. ISO-8601 UTC timestamp, e.g. `2026-02-28T23:59:59.000Z`. Preferred wire form is `yyyy-MM-ddTHH:mm:ss.000Z`.</summary>
9898
[QueryParameter("created_at_max")]
9999
public DateTimeOffset? CreatedAtMax { get; set; }
100-
/// <summary>Lower bound for `created_at` filtering. ISO-8601 UTC timestamp, e.g. `2026-02-01T00:00:00.000Z`.</summary>
100+
/// <summary>Lower bound for `created_at` filtering. ISO-8601 UTC timestamp, e.g. `2026-02-01T00:00:00.000Z`. Preferred wire form is `yyyy-MM-ddTHH:mm:ss.000Z`.</summary>
101101
[QueryParameter("created_at_min")]
102102
public DateTimeOffset? CreatedAtMin { get; set; }
103103
/// <summary>Order fields to include in each result item.</summary>

src/GoAffPro.Client.Generated/Generated/User/Feed/Payouts/PayoutsRequestBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<gl
9494
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
9595
public partial class PayoutsRequestBuilderGetQueryParameters
9696
{
97-
/// <summary>End of the time window for filtering. ISO-8601 UTC timestamp, e.g. `2026-02-28T23:59:59.000Z`. Exact filtering behaviour has not been fully verified against the API.</summary>
97+
/// <summary>End of the time window for filtering. ISO-8601 UTC timestamp, e.g. `2026-02-28T23:59:59.000Z`. Preferred wire form is `yyyy-MM-ddTHH:mm:ss.000Z`. Exact filtering behaviour has not been fully verified against the API.</summary>
9898
[QueryParameter("end_time")]
9999
public DateTimeOffset? EndTime { get; set; }
100100
/// <summary>Maximum number of items to return per page.</summary>
@@ -116,7 +116,7 @@ public partial class PayoutsRequestBuilderGetQueryParameters
116116
[QueryParameter("site_ids")]
117117
public string SiteIds { get; set; }
118118
#endif
119-
/// <summary>Start of the time window for filtering. ISO-8601 UTC timestamp, e.g. `2026-02-01T00:00:00.000Z`. Exact filtering behaviour has not been fully verified against the API.</summary>
119+
/// <summary>Start of the time window for filtering. ISO-8601 UTC timestamp, e.g. `2026-02-01T00:00:00.000Z`. Preferred wire form is `yyyy-MM-ddTHH:mm:ss.000Z`. Exact filtering behaviour has not been fully verified against the API.</summary>
120120
[QueryParameter("start_time")]
121121
public DateTimeOffset? StartTime { get; set; }
122122
}

src/GoAffPro.Client.Generated/Generated/User/Feed/Rewards/RewardsRequestBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<gl
9696
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
9797
public partial class RewardsRequestBuilderGetQueryParameters
9898
{
99-
/// <summary>End of the time window for filtering. ISO-8601 UTC timestamp, e.g. `2026-02-28T23:59:59.000Z`. Exact filtering behaviour has not been fully verified against the API.</summary>
99+
/// <summary>End of the time window for filtering. ISO-8601 UTC timestamp, e.g. `2026-02-28T23:59:59.000Z`. Preferred wire form is `yyyy-MM-ddTHH:mm:ss.000Z`. Exact filtering behaviour has not been fully verified against the API.</summary>
100100
[QueryParameter("end_time")]
101101
public DateTimeOffset? EndTime { get; set; }
102102
/// <summary>Maximum number of items to return per page.</summary>
@@ -118,7 +118,7 @@ public partial class RewardsRequestBuilderGetQueryParameters
118118
[QueryParameter("site_ids")]
119119
public string SiteIds { get; set; }
120120
#endif
121-
/// <summary>Start of the time window for filtering. ISO-8601 UTC timestamp, e.g. `2026-02-01T00:00:00.000Z`. Exact filtering behaviour has not been fully verified against the API.</summary>
121+
/// <summary>Start of the time window for filtering. ISO-8601 UTC timestamp, e.g. `2026-02-01T00:00:00.000Z`. Preferred wire form is `yyyy-MM-ddTHH:mm:ss.000Z`. Exact filtering behaviour has not been fully verified against the API.</summary>
122122
[QueryParameter("start_time")]
123123
public DateTimeOffset? StartTime { get; set; }
124124
}

src/GoAffPro.Client.Generated/Generated/User/Feed/Traffic/TrafficRequestBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<gl
9494
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
9595
public partial class TrafficRequestBuilderGetQueryParameters
9696
{
97-
/// <summary>End of the time window for filtering. ISO-8601 UTC timestamp, e.g. `2026-02-28T23:59:59.000Z`. Exact filtering behaviour has not been fully verified against the API.</summary>
97+
/// <summary>End of the time window for filtering. ISO-8601 UTC timestamp, e.g. `2026-02-28T23:59:59.000Z`. Preferred wire form is `yyyy-MM-ddTHH:mm:ss.000Z`. Exact filtering behaviour has not been fully verified against the API.</summary>
9898
[QueryParameter("end_time")]
9999
public DateTimeOffset? EndTime { get; set; }
100100
/// <summary>Maximum number of items to return per page. **Required** — omitting this parameter causes the endpoint to return an empty result set.</summary>
@@ -116,7 +116,7 @@ public partial class TrafficRequestBuilderGetQueryParameters
116116
[QueryParameter("site_ids")]
117117
public string SiteIds { get; set; }
118118
#endif
119-
/// <summary>Start of the time window for filtering. ISO-8601 UTC timestamp, e.g. `2026-02-01T00:00:00.000Z`. Exact filtering behaviour has not been fully verified against the API.</summary>
119+
/// <summary>Start of the time window for filtering. ISO-8601 UTC timestamp, e.g. `2026-02-01T00:00:00.000Z`. Preferred wire form is `yyyy-MM-ddTHH:mm:ss.000Z`. Exact filtering behaviour has not been fully verified against the API.</summary>
120120
[QueryParameter("start_time")]
121121
public DateTimeOffset? StartTime { get; set; }
122122
}

src/GoAffPro.Client.Generated/Generated/User/Stats/Aggregate/AggregateRequestBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<gl
9494
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
9595
public partial class AggregateRequestBuilderGetQueryParameters
9696
{
97-
/// <summary>End of the time window for filtering. ISO-8601 UTC timestamp, e.g. `2026-02-28T23:59:59.000Z`. Exact filtering behaviour has not been fully verified against the API.</summary>
97+
/// <summary>End of the time window for filtering. ISO-8601 UTC timestamp, e.g. `2026-02-28T23:59:59.000Z`. Preferred wire form is `yyyy-MM-ddTHH:mm:ss.000Z`. Exact filtering behaviour has not been fully verified against the API.</summary>
9898
[QueryParameter("end_time")]
9999
public DateTimeOffset? EndTime { get; set; }
100100
/// <summary>Stat columns to include in each result item.</summary>
@@ -134,7 +134,7 @@ public partial class AggregateRequestBuilderGetQueryParameters
134134
[QueryParameter("site_ids")]
135135
public string SiteIds { get; set; }
136136
#endif
137-
/// <summary>Start of the time window for filtering. ISO-8601 UTC timestamp, e.g. `2026-02-01T00:00:00.000Z`. Exact filtering behaviour has not been fully verified against the API.</summary>
137+
/// <summary>Start of the time window for filtering. ISO-8601 UTC timestamp, e.g. `2026-02-01T00:00:00.000Z`. Preferred wire form is `yyyy-MM-ddTHH:mm:ss.000Z`. Exact filtering behaviour has not been fully verified against the API.</summary>
138138
[QueryParameter("start_time")]
139139
public DateTimeOffset? StartTime { get; set; }
140140
}

src/GoAffPro.Client.Generated/Generated/kiota-lock.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"descriptionHash": "EC334EBCCFB2CDD17332176F25E979B1DF759B7478DC69853BEF5377A4C57AA931A9DC57F19666FC436C0229AF8D12CF482F75E7DAC01B4012509A4923C1B1DE",
2+
"descriptionHash": "51953B4CB1356CCB86F076ED5877E1C9234A9341A27E55725690FA4D80CF2EA970EECB05A9F7CD5A0810CB298E14964708945B6083160ED327BAB6DC15490258",
33
"descriptionLocation": "../../../openapi/goaffpro-canonical.yaml",
44
"lockFileVersion": "1.0.0",
55
"kiotaVersion": "1.30.0",

src/GoAffPro.Client/GoAffProClient.cs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
using System.Globalization;
22
using System.Net;
33
using System.Net.Http.Headers;
4+
using System.Diagnostics.CodeAnalysis;
45
using GoAffPro.Client.Events;
56
using GoAffPro.Client.Exceptions;
67
using GoAffPro.Client.Generated.Models;
78
using GoAffPro.Client.Generated.User.Sites;
9+
using GoAffPro.Client.Kiota;
810
using GoAffPro.Client.Policies;
911
using Microsoft.Extensions.Http;
1012
using Microsoft.Kiota.Abstractions;
@@ -120,10 +122,7 @@ private GoAffProClient(HttpClient httpClient, GoAffProClientOptions options, boo
120122
?? throw new InvalidOperationException("HttpClient.BaseAddress was not initialized.");
121123

122124
IAuthenticationProvider authProvider = new AnonymousAuthenticationProvider();
123-
_requestAdapter = new HttpClientRequestAdapter(authProvider, httpClient: _httpClient)
124-
{
125-
BaseUrl = baseUrl.TrimEnd('/'),
126-
};
125+
_requestAdapter = CreateRequestAdapter(authProvider, _httpClient, baseUrl);
127126

128127
Api = new global::GoAffPro.Client.Generated.GoAffProApiClient(_requestAdapter);
129128

@@ -514,6 +513,21 @@ internal static Uri BuildBaseUri(Uri? baseUrl)
514513
return new Uri(normalized, UriKind.Absolute);
515514
}
516515

516+
[SuppressMessage(
517+
"Reliability",
518+
"CA2000:Dispose objects before losing scope",
519+
Justification = "Ownership of the inner adapter is transferred to StrictUtcDateQueryRequestAdapter, which disposes it.")]
520+
private static StrictUtcDateQueryRequestAdapter CreateRequestAdapter(
521+
IAuthenticationProvider authProvider,
522+
HttpClient httpClient,
523+
string baseUrl)
524+
{
525+
return new StrictUtcDateQueryRequestAdapter(new HttpClientRequestAdapter(authProvider, httpClient: httpClient)
526+
{
527+
BaseUrl = baseUrl.TrimEnd('/'),
528+
});
529+
}
530+
517531
private async Task<IReadOnlyList<UserOrderFeedItem>> PollOrdersAsync(
518532
DateTimeOffset from, DateTimeOffset to, int pageSize, CancellationToken ct)
519533
{

src/GoAffPro.Client/GoAffProUtils.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ namespace GoAffPro.Client;
1212
/// </remarks>
1313
public static class GoAffProUtils
1414
{
15+
private const string StrictUtcIsoTimestampQueryFormat = "yyyy-MM-dd'T'HH:mm:ss'.000Z'";
16+
1517
/// <summary>
1618
/// Parses a monetary decimal string returned by the GoAffPro API using
1719
/// invariant-culture rules.
@@ -62,4 +64,14 @@ public static class GoAffProUtils
6264
? result.ToUniversalTime()
6365
: null;
6466
}
67+
68+
/// <summary>
69+
/// Formats a timestamp using the preferred GoAffPro query-parameter wire form.
70+
/// </summary>
71+
/// <param name="value">Timestamp value to convert to UTC.</param>
72+
/// <returns>A UTC timestamp string formatted as <c>yyyy-MM-ddTHH:mm:ss.000Z</c>.</returns>
73+
public static string FormatTimestampQuery(DateTimeOffset value)
74+
{
75+
return value.ToUniversalTime().ToString(StrictUtcIsoTimestampQueryFormat, CultureInfo.InvariantCulture);
76+
}
6577
}

0 commit comments

Comments
 (0)