Skip to content

Commit 57e3190

Browse files
SDK regeneration (#182)
* [fern-generated] Update SDK Generated by Fern CLI Version: unknown Generators: - fernapi/fern-csharp-sdk: 2.13.0 * reporting: reapply polling helper + tests on regenerated SDK; align tests to flat LoadResponse * build: derive AssemblyVersion/FileVersion from numeric version core so RC prerelease builds compile (CS7034) * docs: reference FER-11399 in AssemblyVersion workaround comment --------- Co-authored-by: fern-api <115122769+fern-api[bot]@users.noreply.github.com> Co-authored-by: fern-support <support@buildwithfern.com>
1 parent 499a454 commit 57e3190

47 files changed

Lines changed: 2311 additions & 4 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.fern/metadata.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@
1515
"inline-path-parameters": true,
1616
"use-discriminated-unions": true
1717
},
18-
"sdkVersion": "44.0.0"
18+
"sdkVersion": "44.1.0-rc.0"
1919
}

.fernignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ SquareWithLegacy.sln
1414
src/Square.sln
1515
src/Square/Square.Custom.props
1616
src/Square/WebhooksHelper.cs
17+
src/Square/Reporting/ReportingHelper.cs
1718
src/Square/Core/Public/SquareApiException.cs
1819
src/Square.Test/WebhooksHelperTests.cs
1920
src/Square.Test/Unit/SquareErrorTest.cs
2021
src/Square.Test/Unit/WebhooksHelperTests.cs
22+
src/Square.Test/Unit/ReportingHelperTests.cs
2123
src/Square.Test/Unit/MockServer/BatchDeleteTest.cs
2224
src/Square.Test/Integration
2325
src/Square.Test/Files

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ jobs:
5050
continue-on-error: true
5151
env:
5252
TEST_SQUARE_TOKEN: ${{ secrets.TEST_SQUARE_TOKEN }}
53+
TEST_SQUARE_REPORTING: ${{ secrets.TEST_SQUARE_REPORTING }}
5354

5455
- name: Publish to nuget.org
5556
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') && steps.build.outcome == 'success'

README.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,75 @@ public static async Task CheckWebhooksEvent(
187187

188188
In .NET 6 and above, there are also overloads using spans for allocation free webhook verification.
189189

190+
## Reporting API
191+
192+
The [Square Reporting API](https://developer.squareup.com/docs/reporting-api/overview) (beta)
193+
exposes a Cube-based analytics layer through two endpoints. Call `GetMetadataAsync` to discover
194+
the queryable schema (cubes, measures, dimensions, and segments), then `LoadAsync` to run a query:
195+
196+
```csharp
197+
using Square;
198+
199+
var client = new SquareClient();
200+
201+
// Discover what you can query.
202+
var metadata = await client.Reporting.GetMetadataAsync();
203+
204+
// Run a query.
205+
var response = await client.Reporting.LoadAsync(
206+
new LoadRequest
207+
{
208+
Query = new Query { Measures = new List<string> { "Orders.count" } },
209+
}
210+
);
211+
```
212+
213+
### Polling with `LoadAndWaitAsync`
214+
215+
`/reporting/v1/load` is asynchronous: while a query is still being computed it returns an HTTP 200
216+
whose body is `{ "error": "Continue wait" }` instead of results. The expected behavior is to re-send
217+
the identical request, with backoff, until results arrive.
218+
219+
`LoadAndWaitAsync` owns that retry loop for you and returns the resolved `LoadResponse` — it never
220+
hands back the "Continue wait" sentinel:
221+
222+
```csharp
223+
using Square;
224+
225+
var response = await client.Reporting.LoadAndWaitAsync(
226+
new LoadRequest
227+
{
228+
Query = new Query { Measures = new List<string> { "Orders.count" } },
229+
}
230+
);
231+
232+
// The resolved payload is exposed on the flat `Data` property.
233+
var data = response.Data;
234+
// ...
235+
```
236+
237+
The backoff is configurable, and the loop honors a `CancellationToken`:
238+
239+
```csharp
240+
var response = await client.Reporting.LoadAndWaitAsync(
241+
request,
242+
new LoadAndWaitOptions
243+
{
244+
MaxAttempts = 20, // give up after this many polls
245+
InitialDelay = TimeSpan.FromSeconds(2), // delay before the first retry
246+
MaxDelay = TimeSpan.FromSeconds(20), // upper bound on the backoff
247+
BackoffFactor = 2, // multiplier applied after each attempt
248+
},
249+
cancellationToken
250+
);
251+
```
252+
253+
If the query does not resolve within `MaxAttempts`, a `SquareException` is thrown. The same logic is
254+
also available as a static helper, `ReportingHelper.LoadAndWaitAsync(client.Reporting, request, ...)`.
255+
256+
> **Note:** The Reporting API is served only from production (`connect.squareup.com/reporting`); it is
257+
> not available in the sandbox environment.
258+
190259
## Legacy SDK
191260

192261
While the new SDK has a lot of improvements, we at Square understand that it takes time to upgrade when there are breaking changes.

reference.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12050,6 +12050,100 @@ await client.Vendors.UpdateAsync(
1205012050
</dl>
1205112051

1205212052

12053+
</dd>
12054+
</dl>
12055+
</details>
12056+
12057+
## Reporting
12058+
<details><summary><code>client.Reporting.<a href="/src/Square/Reporting/ReportingClient.cs">GetMetadataAsync</a>() -> MetadataResponse</code></summary>
12059+
<dl>
12060+
<dd>
12061+
12062+
#### 📝 Description
12063+
12064+
<dl>
12065+
<dd>
12066+
12067+
<dl>
12068+
<dd>
12069+
12070+
Describes the data available to query: the cubes, views, measures, dimensions, and segments you can reference in a reporting query. Call this first to discover the schema, then pass the members you need to `load`.
12071+
</dd>
12072+
</dl>
12073+
</dd>
12074+
</dl>
12075+
12076+
#### 🔌 Usage
12077+
12078+
<dl>
12079+
<dd>
12080+
12081+
<dl>
12082+
<dd>
12083+
12084+
```csharp
12085+
await client.Reporting.GetMetadataAsync();
12086+
```
12087+
</dd>
12088+
</dl>
12089+
</dd>
12090+
</dl>
12091+
12092+
12093+
</dd>
12094+
</dl>
12095+
</details>
12096+
12097+
<details><summary><code>client.Reporting.<a href="/src/Square/Reporting/ReportingClient.cs">LoadAsync</a>(LoadRequest { ... }) -> LoadResponse</code></summary>
12098+
<dl>
12099+
<dd>
12100+
12101+
#### 📝 Description
12102+
12103+
<dl>
12104+
<dd>
12105+
12106+
<dl>
12107+
<dd>
12108+
12109+
Runs a reporting query against the discovered schema and returns the aggregated results. Long-running queries may return a "Continue wait" response while processing — retry the same request until results are ready.
12110+
</dd>
12111+
</dl>
12112+
</dd>
12113+
</dl>
12114+
12115+
#### 🔌 Usage
12116+
12117+
<dl>
12118+
<dd>
12119+
12120+
<dl>
12121+
<dd>
12122+
12123+
```csharp
12124+
await client.Reporting.LoadAsync(new LoadRequest());
12125+
```
12126+
</dd>
12127+
</dl>
12128+
</dd>
12129+
</dl>
12130+
12131+
#### ⚙️ Parameters
12132+
12133+
<dl>
12134+
<dd>
12135+
12136+
<dl>
12137+
<dd>
12138+
12139+
**request:** `LoadRequest`
12140+
12141+
</dd>
12142+
</dl>
12143+
</dd>
12144+
</dl>
12145+
12146+
1205312147
</dd>
1205412148
</dl>
1205512149
</details>
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
using System.Text.Json;
2+
using NUnit.Framework;
3+
using Square;
4+
5+
namespace Square.Test.Integration;
6+
7+
// The Reporting API is a beta, bespoke offering served ONLY from production
8+
// (connect.squareup.com/reporting) — it is not routed on sandbox (returns 404 there).
9+
// Validating it live therefore needs a production, reporting-provisioned access token,
10+
// supplied via TEST_SQUARE_REPORTING (distinct from the sandbox TEST_SQUARE_TOKEN used by
11+
// the rest of the integration suite). This suite is gated behind TEST_SQUARE_REPORTING and
12+
// skips by default — keeping CI green. The endpoints are read-only (schema discovery +
13+
// queries). The polling *logic* is covered without a live account in
14+
// Unit/ReportingHelperTests.cs.
15+
//
16+
// Run it against a real prod account:
17+
// TEST_SQUARE_REPORTING=<prod-reporting-token> \
18+
// dotnet test --filter FullyQualifiedName~Integration.ReportingTests
19+
// # override the host with TEST_SQUARE_BASE_URL=<url> if reporting moves.
20+
[TestFixture]
21+
public class ReportingTests
22+
{
23+
private SquareClient _client = null!;
24+
25+
[SetUp]
26+
public void SetUp()
27+
{
28+
var token = Environment.GetEnvironmentVariable("TEST_SQUARE_REPORTING");
29+
if (string.IsNullOrEmpty(token))
30+
{
31+
Assert.Ignore(
32+
"Set TEST_SQUARE_REPORTING to a production, reporting-provisioned access token to run the reporting integration suite."
33+
);
34+
}
35+
// Reporting only exists on production; allow overriding the host via TEST_SQUARE_BASE_URL.
36+
var baseUrl =
37+
Environment.GetEnvironmentVariable("TEST_SQUARE_BASE_URL") ?? SquareEnvironment.Production;
38+
TestContext.Out.WriteLine(
39+
$"[reporting] base URL: {baseUrl} -> {baseUrl}/reporting/v1/{{meta,load}}"
40+
);
41+
_client = new SquareClient(token!, new ClientOptions { BaseUrl = baseUrl });
42+
}
43+
44+
// Resolves the first queryable measure from the live schema, e.g. "Orders.count".
45+
private async Task<string> FirstMeasureNameAsync()
46+
{
47+
var metadata = await _client.Reporting.GetMetadataAsync();
48+
var measure = metadata.Cubes?.FirstOrDefault()?.Measures.FirstOrDefault()?.Name;
49+
if (string.IsNullOrEmpty(measure))
50+
{
51+
throw new Exception(
52+
"No cubes/measures are available on the reporting schema for this account."
53+
);
54+
}
55+
return measure!;
56+
}
57+
58+
[Test]
59+
public async Task GetMetadataReturnsTheQueryableSchema()
60+
{
61+
var metadata = await _client.Reporting.GetMetadataAsync();
62+
63+
Assert.That(metadata.Cubes, Is.Not.Null);
64+
Assert.That(metadata.Cubes, Is.Not.Empty);
65+
66+
// Surface the live schema so a developer can see what is queryable.
67+
var summary = (metadata.Cubes ?? new List<Cube>())
68+
.Take(5)
69+
.Select(cube => new
70+
{
71+
cube = cube.Name,
72+
measures = cube.Measures.Take(5).Select(measure => measure.Name).ToList(),
73+
});
74+
TestContext.Out.WriteLine(
75+
"Reporting schema (first 5 cubes): "
76+
+ JsonSerializer.Serialize(summary, new JsonSerializerOptions { WriteIndented = true })
77+
);
78+
}
79+
80+
[Test]
81+
public async Task LoadReturnsResultsOrTheContinueWaitSentinel()
82+
{
83+
var measure = await FirstMeasureNameAsync();
84+
var response = await _client.Reporting.LoadAsync(
85+
new LoadRequest { Query = new Query { Measures = new List<string> { measure } } }
86+
);
87+
88+
if (response.AdditionalProperties.TryGetValue("error", out var sentinel))
89+
{
90+
// Documented async behavior: a still-processing query comes back as HTTP 200
91+
// with { "error": "Continue wait" } instead of results.
92+
Assert.That(sentinel.GetString(), Is.EqualTo("Continue wait"));
93+
}
94+
else
95+
{
96+
// Flat LoadResponse: a resolved query carries its payload on `Data`.
97+
Assert.That(response.Data, Is.Not.Null);
98+
}
99+
}
100+
101+
[Test]
102+
public async Task LoadAndWaitResolvesAQueryEndToEndWithoutSurfacingContinueWait()
103+
{
104+
var measure = await FirstMeasureNameAsync();
105+
106+
var response = await _client.Reporting.LoadAndWaitAsync(
107+
new LoadRequest { Query = new Query { Measures = new List<string> { measure } } },
108+
new LoadAndWaitOptions
109+
{
110+
MaxAttempts = 20,
111+
InitialDelay = TimeSpan.FromSeconds(2),
112+
MaxDelay = TimeSpan.FromSeconds(20),
113+
}
114+
);
115+
116+
// The polling helper must never hand back the raw "Continue wait" sentinel.
117+
Assert.That(response.AdditionalProperties.ContainsKey("error"), Is.False);
118+
// Flat LoadResponse: the resolved query carries its payload on `Data`.
119+
Assert.That(response.Data, Is.Not.Null);
120+
}
121+
}

0 commit comments

Comments
 (0)