Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7e41eaf
fix(csharp): use runtime .NET version detection for query param URL-e…
sagadira May 8, 2026
401bba6
minor refactoring (avoid another variable declaration) (#23718)
wing328 May 8, 2026
930fc30
[typescript-fetch] Fix `instanceOf` type guards for discriminated uni…
jasperpatterson May 8, 2026
4f1bc49
build(deps): bump fast-uri from 3.1.0 to 3.1.2 in /website (#23730)
dependabot[bot] May 9, 2026
f87ef87
build(deps): bump fast-uri (#23721)
dependabot[bot] May 9, 2026
dd59b06
build(deps-dev): bump fast-uri (#23722)
dependabot[bot] May 9, 2026
bbca9cf
build(deps-dev): bump @babel/plugin-transform-modules-systemjs (#23723)
dependabot[bot] May 9, 2026
800646d
build(deps-dev): bump @babel/plugin-transform-modules-systemjs (#23724)
dependabot[bot] May 9, 2026
21b0969
build(deps-dev): bump fast-uri (#23725)
dependabot[bot] May 9, 2026
389f52c
build(deps-dev): bump hono (#23735)
dependabot[bot] May 9, 2026
2c46d6b
build(deps-dev): bump hono in /samples/client/others/typescript-angul…
dependabot[bot] May 9, 2026
2afbbbf
build(deps-dev): bump hono (#23736)
dependabot[bot] May 9, 2026
dfdc5ae
build(deps-dev): bump fast-uri (#23733)
dependabot[bot] May 9, 2026
0a1225e
build(deps-dev): bump fast-uri (#23731)
dependabot[bot] May 9, 2026
578c9d9
build(deps-dev): bump @babel/plugin-transform-modules-systemjs (#23729)
dependabot[bot] May 9, 2026
af5b49d
build(deps-dev): bump fast-uri (#23728)
dependabot[bot] May 9, 2026
aceeabb
build(deps): bump fast-uri (#23727)
dependabot[bot] May 9, 2026
e6e66c0
build(deps-dev): bump @babel/plugin-transform-modules-systemjs (#23726)
dependabot[bot] May 9, 2026
a3bdef7
build(deps): bump ip-address and express-rate-limit (#23709)
dependabot[bot] May 9, 2026
2ae3b13
build(deps-dev): bump fast-uri (#23732)
dependabot[bot] May 9, 2026
4a2601d
build(deps-dev): bump hono (#23737)
dependabot[bot] May 9, 2026
0a549a8
build(deps-dev): bump hono (#23740)
dependabot[bot] May 9, 2026
400a0d6
build(deps-dev): bump hono (#23741)
dependabot[bot] May 9, 2026
44b6862
build(deps-dev): bump hono (#23742)
dependabot[bot] May 9, 2026
9cac97a
build(deps-dev): bump @babel/plugin-transform-modules-systemjs (#23739)
dependabot[bot] May 9, 2026
9b6c308
build(deps-dev): bump hono (#23738)
dependabot[bot] May 9, 2026
442a1eb
build(deps-dev): bump @babel/plugin-transform-modules-systemjs (#23747)
dependabot[bot] May 9, 2026
14540e1
build(deps-dev): bump @babel/plugin-transform-modules-systemjs (#23746)
dependabot[bot] May 9, 2026
e83d3ba
build(deps-dev): bump @babel/plugin-transform-modules-systemjs (#23745)
dependabot[bot] May 9, 2026
6a0da9b
build(deps-dev): bump @babel/plugin-transform-modules-systemjs (#23744)
dependabot[bot] May 9, 2026
c22c0f5
build(deps): bump @babel/plugin-transform-modules-systemjs in /websit…
dependabot[bot] May 9, 2026
7a14a43
Kotlin multiplatform delete body (#23754)
walterbrebels May 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1401,6 +1401,7 @@ public ExtendedCodegenProperty(CodegenProperty cp) {
this.xmlName = cp.xmlName;
this.xmlNamespace = cp.xmlNamespace;
this.isXmlWrapped = cp.isXmlWrapped;
this.setHasSanitizedName(cp.getHasSanitizedName());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ namespace {{packageName}}.Client
{
HashAlgorithm = HashAlgorithmName.SHA256;
SigningAlgorithm = "PKCS1-v15";
_skipUrlEncode = RuntimeInformation.FrameworkDescription.StartsWith(".NET ") &&
int.TryParse(RuntimeInformation.FrameworkDescription.Substring(5).Split('.')[0], out int fwMajor) && fwMajor >= 9;
}

/// <summary>
Expand Down Expand Up @@ -67,6 +69,14 @@ namespace {{packageName}}.Client
/// </summary>
public int SignatureValidityPeriod { get; set; }

// On .NET 9+, HttpUtility.ParseQueryString already URL-encodes keys internally,
// so calling UrlEncode again would cause double-encoding and produce a signature
// that does not match the actual request sent by RestSharp 112+.
// On .NET 8 and earlier, keys must be explicitly URL-encoded so that special
// characters (e.g. '$' in OData params like $filter) are encoded the same way
// in the signature as they are in the outgoing HTTP request.
private readonly bool _skipUrlEncode;

private enum PrivateKeyType
{
None = 0,
Expand Down Expand Up @@ -133,8 +143,7 @@ namespace {{packageName}}.Client
foreach (var parameter in requestOptions.QueryParameters)
{
#if (NETCOREAPP)
string framework = RuntimeInformation.FrameworkDescription;
string key = framework.StartsWith(".NET 9") ? parameter.Key : {{#net90OrLater}}HttpUtility.UrlEncode({{/net90OrLater}}parameter.Key{{#net90OrLater}}){{/net90OrLater}};
string key = _skipUrlEncode ? parameter.Key : HttpUtility.UrlEncode(parameter.Key);
if (parameter.Value.Count > 1)
{ // array
foreach (var value in parameter.Value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ import {{packageName}}.auth.*
}
this.method = requestConfig.method.httpMethod
headers.filter { header -> !UNSAFE_HEADERS.contains(header.key) }.forEach { header -> this.header(header.key, header.value) }
if (requestConfig.method in listOf(RequestMethod.PUT, RequestMethod.POST, RequestMethod.PATCH)) {
if (requestConfig.method in listOf(RequestMethod.PUT, RequestMethod.POST, RequestMethod.PATCH, RequestMethod.DELETE)) {
val contentType = (requestConfig.headers[HttpHeaders.ContentType]?.let { ContentType.parse(it) }
?: ContentType.Application.Json)
this.contentType(contentType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ import {{packageName}}.auth.*
}
this.method = requestConfig.method.httpMethod
headers.filter { header -> !UNSAFE_HEADERS.contains(header.key) }.forEach { header -> this.header(header.key, header.value) }
if (requestConfig.method in listOf(RequestMethod.PUT, RequestMethod.POST, RequestMethod.PATCH)) {
if (requestConfig.method in listOf(RequestMethod.PUT, RequestMethod.POST, RequestMethod.PATCH, RequestMethod.DELETE)) {
val contentType = (requestConfig.headers[HttpHeaders.ContentType]?.let { ContentType.parse(it) }
?: ContentType.Application.Json)
this.contentType(contentType)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,30 @@ import { type {{modelName}}, {{modelName}}FromJSONTyped, {{modelName}}ToJSON, {{
export function instanceOf{{classname}}(value: object): value is {{classname}} {
{{#vars}}
{{#required}}
{{#hasSanitizedName}}
if ((!('{{name}}' in value) && !('{{baseName}}' in value)) || (value['{{name}}'] === undefined && value['{{baseName}}'] === undefined)) return false;
{{/hasSanitizedName}}
{{^hasSanitizedName}}
if (!('{{name}}' in value) || value['{{name}}'] === undefined) return false;
{{/hasSanitizedName}}
{{#isEnum}}
{{#allowableValues}}
{{#values}}
{{#-first}}
{{#-last}}
{{#hasSanitizedName}}
{{#isString}}if (value['{{name}}'] !== '{{.}}' && value['{{baseName}}'] !== '{{.}}') return false;{{/isString}}
{{^isString}}if (value['{{name}}'] !== {{.}} && value['{{baseName}}'] !== {{.}}) return false;{{/isString}}
{{/hasSanitizedName}}
{{^hasSanitizedName}}
{{#isString}}if (value['{{name}}'] !== '{{.}}') return false;{{/isString}}
{{^isString}}if (value['{{name}}'] !== {{.}}) return false;{{/isString}}
{{/hasSanitizedName}}
{{/-last}}
{{/-first}}
{{/values}}
{{/allowableValues}}
{{/isEnum}}
{{/required}}
{{/vars}}
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export function {{classname}}ToJSONTyped(value?: {{classname}} | null, ignoreDis
switch (value['{{discriminator.propertyName}}']) {
{{#discriminator.mappedModels}}
case '{{mappingName}}':
return Object.assign({}, {{modelName}}ToJSON(value), { {{discriminator.propertyName}}: '{{mappingName}}' } as const);
return Object.assign({}, {{modelName}}ToJSON(value), { '{{discriminator.propertyBaseName}}': '{{mappingName}}' } as const);
{{/discriminator.mappedModels}}
default:
return value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,66 @@ public void testOneOfModelsImportNonPrimitiveTypes() throws IOException {
TestUtils.assertFileContains(testResponse, "import type { OptionThree } from './OptionThree'");
}

@Test(description = "Verify instanceOf checks discriminator value for single-value enums")
public void testInstanceOfChecksDiscriminatorValue() throws IOException {
File output = generate(Collections.emptyMap(), "src/test/resources/3_0/typescript-fetch/oneOf.yaml");

// OptionOne should check discriminator value
Path optionOne = Paths.get(output + "/models/OptionOne.ts");
TestUtils.assertFileExists(optionOne);
TestUtils.assertFileContains(optionOne, "value['discriminatorField'] !== 'optionOne'");

// OptionTwo should check discriminator value
Path optionTwo = Paths.get(output + "/models/OptionTwo.ts");
TestUtils.assertFileExists(optionTwo);
TestUtils.assertFileContains(optionTwo, "value['discriminatorField'] !== 'optionTwo'");

// TestA should NOT have a value check (foo is a plain string, not a single-value enum)
Path testA = Paths.get(output + "/models/TestA.ts");
TestUtils.assertFileExists(testA);
TestUtils.assertFileNotContains(testA, "value['foo'] !==");

// SnakeOptionOne: discriminator_field (snake_case baseName) vs discriminatorField (camelCase name)
// instanceOf should check both casings for field presence and discriminator value
Path snakeOptionOne = Paths.get(output + "/models/SnakeOptionOne.ts");
TestUtils.assertFileExists(snakeOptionOne);
TestUtils.assertFileContains(snakeOptionOne, "'discriminatorField' in value");
TestUtils.assertFileContains(snakeOptionOne, "'discriminator_field' in value");
TestUtils.assertFileContains(snakeOptionOne, "value['discriminatorField'] !== 'snakeOptionOne'");
TestUtils.assertFileContains(snakeOptionOne, "value['discriminator_field'] !== 'snakeOptionOne'");
// Also verify the non-enum required field checks both casings
TestUtils.assertFileContains(snakeOptionOne, "'someProperty' in value");
TestUtils.assertFileContains(snakeOptionOne, "'some_property' in value");

// DashedOptionOne: discriminator-field (dashed baseName) vs discriminatorField (camelCase name)
Path dashedOptionOne = Paths.get(output + "/models/DashedOptionOne.ts");
TestUtils.assertFileExists(dashedOptionOne);
TestUtils.assertFileContains(dashedOptionOne, "'discriminatorField' in value");
TestUtils.assertFileContains(dashedOptionOne, "'discriminator-field' in value");
TestUtils.assertFileContains(dashedOptionOne, "value['discriminatorField'] !== 'dashedOptionOne'");
TestUtils.assertFileContains(dashedOptionOne, "value['discriminator-field'] !== 'dashedOptionOne'");
TestUtils.assertFileContains(dashedOptionOne, "'someProperty' in value");
TestUtils.assertFileContains(dashedOptionOne, "'some-property' in value");

// Numeric singleton enum: value check must NOT quote the literal
Path numericModel = Paths.get(output + "/models/NumericSingletonEnumModel.ts");
TestUtils.assertFileExists(numericModel);
TestUtils.assertFileContains(numericModel, "value['kind'] !== 42");
TestUtils.assertFileNotContains(numericModel, "value['kind'] !== '42'");

// ToJSONTyped of discriminated oneOf must emit the wire-format discriminator key
// (propertyBaseName), not the camelCase TS property name
Path dashedDiscriminatorResponse = Paths.get(output + "/models/TestDashedDiscriminatorResponse.ts");
TestUtils.assertFileExists(dashedDiscriminatorResponse);
TestUtils.assertFileContains(dashedDiscriminatorResponse, "{ 'discriminator-field': 'dashedOptionOne' }");
TestUtils.assertFileContains(dashedDiscriminatorResponse, "{ 'discriminator-field': 'dashedOptionTwo' }");

Path snakeDiscriminatorResponse = Paths.get(output + "/models/TestSnakeCaseDiscriminatorResponse.ts");
TestUtils.assertFileExists(snakeDiscriminatorResponse);
TestUtils.assertFileContains(snakeDiscriminatorResponse, "{ 'discriminator_field': 'snakeOptionOne' }");
TestUtils.assertFileContains(snakeDiscriminatorResponse, "{ 'discriminator_field': 'snakeOptionTwo' }");
}

@Test(description = "Verify validationAttributes works with withoutRuntimeChecks=true")
public void testValidationAttributesWithWithoutRuntimeChecks() throws IOException {
Map<String, Object> properties = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,26 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/TestDiscriminatorResponse'
/test-snake-case-discriminator:
get:
operationId: testSnakeCaseDiscriminator
responses:
200:
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/TestSnakeCaseDiscriminatorResponse'
/test-dashed-discriminator:
get:
operationId: testDashedDiscriminator
responses:
200:
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/TestDashedDiscriminatorResponse'
components:
schemas:
TestArrayResponse:
Expand Down Expand Up @@ -93,4 +113,79 @@ components:
- "optionTwo"
type: string
required:
- discriminatorField
- discriminatorField
NumericSingletonEnumModel:
type: object
properties:
kind:
type: integer
enum:
- 42
required:
- kind
TestSnakeCaseDiscriminatorResponse:
discriminator:
propertyName: discriminator_field
mapping:
snakeOptionOne: "#/components/schemas/SnakeOptionOne"
snakeOptionTwo: "#/components/schemas/SnakeOptionTwo"
oneOf:
- $ref: "#/components/schemas/SnakeOptionOne"
- $ref: "#/components/schemas/SnakeOptionTwo"
SnakeOptionOne:
type: object
properties:
discriminator_field:
enum:
- "snakeOptionOne"
type: string
some_property:
type: string
required:
- discriminator_field
- some_property
SnakeOptionTwo:
type: object
properties:
discriminator_field:
enum:
- "snakeOptionTwo"
type: string
some_property:
type: string
required:
- discriminator_field
- some_property
TestDashedDiscriminatorResponse:
discriminator:
propertyName: discriminator-field
mapping:
dashedOptionOne: "#/components/schemas/DashedOptionOne"
dashedOptionTwo: "#/components/schemas/DashedOptionTwo"
oneOf:
- $ref: "#/components/schemas/DashedOptionOne"
- $ref: "#/components/schemas/DashedOptionTwo"
DashedOptionOne:
type: object
properties:
discriminator-field:
enum:
- "dashedOptionOne"
type: string
some-property:
type: string
required:
- discriminator-field
- some-property
DashedOptionTwo:
type: object
properties:
discriminator-field:
enum:
- "dashedOptionTwo"
type: string
some-property:
type: string
required:
- discriminator-field
- some-property
12 changes: 6 additions & 6 deletions samples/client/others/typescript-angular-v20/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading