Skip to content

Commit 4360f69

Browse files
fern-supportdevin-ai-integration[bot]willkendall01
authored
feat(csharp): add default-timeout-in-seconds generator config (#15801)
Adds a new `default-timeout-in-seconds` option to the C# SDK generator custom config. When set, the generated `ClientOptions.Timeout` defaults to the configured value (or `Timeout.InfiniteTimeSpan` when set to "infinity") instead of the previously hardcoded 30 seconds. SDK users can still override the timeout per-request via `RequestOptions.Timeout`. This mirrors the existing config option in the Java/TypeScript/Python generators. Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: will.kendall@buildwithfern.com <wpk235@gmail.com>
1 parent f9ca0af commit 4360f69

5 files changed

Lines changed: 75 additions & 2 deletions

File tree

generators/csharp/codegen/src/context/generation-info.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ export class Generation {
247247
maxRetries: () => this.customConfig.maxRetries,
248248
/** Controls which HTTP status codes trigger automatic retries. Default: "legacy". */
249249
retryStatusCodes: () => this.customConfig.retryStatusCodes ?? "legacy",
250+
/** Override the default request timeout (in seconds) for the generated SDK client. `"infinity"` disables the default timeout. Default: 30. */
251+
defaultTimeoutInSeconds: () => this.customConfig["default-timeout-in-seconds"],
250252
/**
251253
* Output path configuration for generated files.
252254
* Returns normalized paths for library, test, solution, and other files.

generators/csharp/codegen/src/custom-config/CsharpConfigSchema.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,13 @@ export const CsharpConfigSchema = z.object({
117117
// "slnx" (default) generates only the modern .slnx format.
118118
"sln-format": z.enum(["sln", "slnx"]).optional(),
119119
maxRetries: z.number().int().min(0).optional(),
120-
retryStatusCodes: z.optional(z.enum(["legacy", "recommended"]))
120+
retryStatusCodes: z.optional(z.enum(["legacy", "recommended"])),
121+
"default-timeout-in-seconds": z
122+
.union([z.number().positive(), z.literal("infinity")])
123+
.optional()
124+
.describe(
125+
"The default timeout for network requests, in seconds. Set to `infinity` to disable the default timeout. SDK users can still override this per-request via request options."
126+
)
121127
});
122128

123129
export type CsharpConfigSchema = z.infer<typeof CsharpConfigSchema>;
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { describe, expect, it } from "vitest";
2+
import { CsharpConfigSchema } from "../CsharpConfigSchema.js";
3+
4+
describe("C# default-timeout-in-seconds config", () => {
5+
it("should accept a positive number", () => {
6+
const result = CsharpConfigSchema.safeParse({ "default-timeout-in-seconds": 60 });
7+
expect(result.success).toBe(true);
8+
if (result.success) {
9+
expect(result.data["default-timeout-in-seconds"]).toBe(60);
10+
}
11+
});
12+
13+
it("should accept fractional seconds", () => {
14+
const result = CsharpConfigSchema.safeParse({ "default-timeout-in-seconds": 1.5 });
15+
expect(result.success).toBe(true);
16+
if (result.success) {
17+
expect(result.data["default-timeout-in-seconds"]).toBe(1.5);
18+
}
19+
});
20+
21+
it("should accept the literal 'infinity'", () => {
22+
const result = CsharpConfigSchema.safeParse({ "default-timeout-in-seconds": "infinity" });
23+
expect(result.success).toBe(true);
24+
if (result.success) {
25+
expect(result.data["default-timeout-in-seconds"]).toBe("infinity");
26+
}
27+
});
28+
29+
it("should accept config without the option (optional)", () => {
30+
const result = CsharpConfigSchema.safeParse({});
31+
expect(result.success).toBe(true);
32+
if (result.success) {
33+
expect(result.data["default-timeout-in-seconds"]).toBeUndefined();
34+
}
35+
});
36+
37+
it("should reject zero", () => {
38+
const result = CsharpConfigSchema.safeParse({ "default-timeout-in-seconds": 0 });
39+
expect(result.success).toBe(false);
40+
});
41+
42+
it("should reject negative numbers", () => {
43+
const result = CsharpConfigSchema.safeParse({ "default-timeout-in-seconds": -1 });
44+
expect(result.success).toBe(false);
45+
});
46+
47+
it("should reject other strings", () => {
48+
const result = CsharpConfigSchema.safeParse({ "default-timeout-in-seconds": "60" });
49+
expect(result.success).toBe(false);
50+
});
51+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# yaml-language-server: $schema=../../../../../fern-changes-yml.schema.json
2+
3+
- summary: |
4+
Add a `default-timeout-in-seconds` config option to the C# SDK generator.
5+
When set, the generated `ClientOptions.Timeout` defaults to the configured
6+
value (or `Timeout.InfiniteTimeSpan` when set to `"infinity"`) instead of
7+
the previously hardcoded 30 seconds. SDK users can still override the
8+
timeout per-request via `RequestOptions.Timeout`.
9+
type: feat

generators/csharp/sdk/src/options/BaseOptionsGenerator.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,18 @@ export class BaseOptionsGenerator extends WithGeneration {
9494

9595
public getTimeoutField(classOrInterface: ast.Interface | ast.Class, { optional, includeInitializer }: OptionArgs) {
9696
const type = this.System.TimeSpan;
97+
const configured = this.settings.defaultTimeoutInSeconds;
98+
const initializer =
99+
configured === "infinity"
100+
? this.csharp.codeblock("System.Threading.Timeout.InfiniteTimeSpan")
101+
: this.csharp.codeblock(`TimeSpan.FromSeconds(${configured ?? 30})`);
97102
classOrInterface.addField({
98103
origin: classOrInterface.explicit("Timeout"),
99104
access: ast.Access.Public,
100105
get: true,
101106
init: true,
102107
type: optional ? type.asOptional() : type,
103-
initializer: includeInitializer ? this.csharp.codeblock("TimeSpan.FromSeconds(30)") : undefined,
108+
initializer: includeInitializer ? initializer : undefined,
104109
summary: "The timeout for the request."
105110
});
106111
}

0 commit comments

Comments
 (0)