microsoft / microsoft/kiota

Typescript client generation doesn't handle allOf correctly when serializing, again in 1.28.0

Open
#6,777 1 comment 3 reactions 0 assignees View on GitHub
type:bug TypeScript
Dominant language
C#
Stars
3.8k
Forks
333
Avg merge
16h 29m
Merged PRs (30d)
116

Description

### What are you generating using Kiota, clients or plugins?

API Client/SDK

### In what context or format are you using Kiota?

Nuget tool

### Client library/SDK language

TypeScript

### Describe the bug

This is the same issue described in https://github.com/microsoft/kiota/issues/6511.

Actual behaviour is that base type properties are serialized, but derived type properties aren't included.

Keeping the details in this one succinct, most of the details are the same as the issue above. The root of the bug is in the `serializeDerivedType` functions.

Here are the new typescript generated files in the 1.28.0 version for base type serialization.

Here is the base type serializer, which is called first when serializing a derived type.
```ts
/**
* Serializes information the current object
* @param BaseType The instance to serialize from.
* @param isSerializingDerivedType A boolean indicating whether the serialization is for a derived type.
* @param writer Serialization writer to use to serialize this model
*/
// @ts-ignore
export function serializeBaseType(writer: SerializationWriter, baseType: Partial | undefined | null = {}, isSerializingDerivedType: boolean = false) : void {
if (!baseType || isSerializingDerivedType) { return; }
writer.writeStringValue("derivedType", baseType.derivedType);
switch (baseType.derivedType) {
case "A":
serializeDerivedTypeA(writer, baseType, true);
break;
case "B":
serializeDerivedTypeB(writer, baseType, true);
break;
}
}
```

It then calls the `serializeDerivedType` functions.
Specifically, note the usage of `isSerializingDerivedType`.
Following the control flow, if it is `true`, it will return out of the function and never serialize the derived type properties.
```ts
/**
* Serializes information the current object
* @param DerivedTypeA The instance to serialize from.
* @param isSerializingDerivedType A boolean indicating whether the serialization is for a derived type.
* @param writer Serialization writer to use to serialize this model
*/
// @ts-ignore
export function serializeDerivedTypeA(writer: SerializationWriter, derivedTypeA: Partial | undefined | null = {}, isSerializingDerivedType: boolean = false) : void {
if (!derivedTypeA || isSerializingDerivedType) { return; }
serializeBaseType(writer, derivedTypeA, isSerializingDerivedType)
writer.writeStringValue("typeAProperty", derivedTypeA.typeAProperty);
writer.writeAdditionalData(derivedTypeA.additionalData);
}
/**
* Serializes information the current object
* @param DerivedTypeB The instance to serialize from.
* @param isSerializingDerivedType A boolean indicating whether the serialization is for a derived type.
* @param writer Serialization writer to use to serialize this model
*/
// @ts-ignore
export function serializeDerivedTypeB(writer: SerializationWriter, derivedTypeB: Partial | undefined | null = {}, isSerializingDerivedType: boolean = false) : void {
if (!derivedTypeB || isSerializingDerivedType) { return; }
serializeBaseType(writer, derivedTypeB, isSerializingDerivedType)
writer.writeStringValue("typeBProperty", derivedTypeB.typeBProperty);
writer.writeAdditionalData(derivedTypeB.additionalData);
}
/* tslint:enable */
/* eslint-enable */
```

### Expected behavior

In the issue discussion, the [proposed solution from the previous issue](https://github.com/microsoft/kiota/issues/6511#issuecomment-2963433451) suggests that the `isSerializingDerivedType` parameter should _not_ be used in the serializeDerivedType in its control flow.

```ts
/**
* Serializes information the current object
* @param writer Serialization writer to use to serialize this model
*/
// @ts-ignore
export function serializeBaseType(writer: SerializationWriter, baseType: Partial | undefined | null = {}, derivedTypeSerialization: bool = false) : void {
if (baseType === undefined || baseType === null) return;

if (!derivedTypeSerialization) {
writer.writeStringValue("derivedType", baseType.derivedType);
switch (baseType.derivedType) {
case "A":
serializeDerivedTypeA(writer, baseType as DerivedTypeA, true);
break;
case "B":
serializeDerivedTypeB(writer, baseType as DerivedTypeB, true);
break;
}
}
}
/**
* Serializes information the current object
* @param writer Serialization writer to use to serialize this model
*/
// @ts-ignore
export function serializeDerivedTypeA(writer: SerializationWriter, derivedTypeA: Partial | undefined | null = {}, derivedTypeSerialization: bool = false) : void {
if (derivedTypeA) {
serializeBaseType(writer, derivedTypeA, derivedTypeSerialization);
writer.writeStringValue("typeAProperty", derivedTypeA.typeAProperty);
writer.writeAdditionalData(derivedTypeA.additionalData);
}
}
/**
* Serializes information the current object
* @param writer Serialization writer to use to serialize this model
*/
// @ts-ignore
export function serializeDerivedTypeB(writer: SerializationWriter, derivedTypeB: Partial | undefined | null = {}, derivedTypeSerialization: bool = false) : void {
if (derivedTypeB) {
serializeBaseType(writer, derivedTypeB, derivedTypeSerialization);
writer.writeStringValue("typeBProperty", derivedTypeB.typeBProperty);
writer.writeAdditionalData(derivedTypeB.additionalData);
}
}
```

### How to reproduce

1. Unzip the attached swagger.yml file to a folder
2. In that folder, run `kiota generate --openapi swagger.yml --language typescript --output ./typescript`
3. Open ./typescript/models/index.ts and observe the serializeBaseType method is generated as described

### Open API description file

This is same test OpenAPI file from the mentioned [issue](https://github.com/microsoft/kiota/issues/6511).

[swagger.zip](https://github.com/user-attachments/files/21337923/swagger.zip)

### Kiota Version

1.28.0+57130b1b1db3bc5c060498682f41e20c8ae089f2

### Latest Kiota version known to work for scenario above?(Not required)

_No response_

### Known Workarounds

I can manually patch the generated code to remove the condition in the `serializeDerivedType` functions.

### Configuration

- OS: **MacOS Sequoia**
- Architecture: **ARM64**
- Do you know whether it is specific to that configuration? **Unlikely**

### Debug output

Click to expand log
```

Warning: the TypeScript language is in preview (Preview) some features are not fully supported and source breaking changes will happen with future updates.
dbug: Kiota.Builder.KiotaBuilder[0]
kiota version 1.28.0
info: Kiota.Builder.KiotaBuilder[0]
loaded description from local source
dbug: Kiota.Builder.KiotaBuilder[0]
step 1 - reading the stream - took 00:00:00.0059106
warn: Kiota.Builder.KiotaBuilder[0]
OpenAPI warning: #/ - A servers entry (v3) or host + basePath + schemes properties (v2) was not present in the OpenAPI description. The root URL will need to be set manually with the request adapter.
dbug: Kiota.Builder.KiotaBuilder[0]
step 2 - parsing the document - took 00:00:00.0797016
dbug: Kiota.Builder.KiotaBuilder[0]
step 3 - updating generation configuration from kiota extension - took 00:00:00.0000685
dbug: Kiota.Builder.KiotaBuilder[0]
step 4 - filtering API paths with patterns - took 00:00:00.0073357
warn: Kiota.Builder.KiotaBuilder[0]
No server url found in the OpenAPI document. The base url will need to be set when using the client.
dbug: Kiota.Builder.KiotaBuilder[0]
step 5 - checking whether the output should be updated - took 00:00:00.0190937
dbug: Kiota.Builder.KiotaBuilder[0]
step 6 - create uri space - took 00:00:00.0022537
dbug: Kiota.Builder.KiotaBuilder[0]
InitializeInheritanceIndex 00:00:00.0030670
dbug: Kiota.Builder.KiotaBuilder[0]
CreateRequestBuilderClass 00:00:00
dbug: Kiota.Builder.KiotaBuilder[0]
MapTypeDefinitions 00:00:00.0033668
dbug: Kiota.Builder.KiotaBuilder[0]
TrimInheritedModels 00:00:00
dbug: Kiota.Builder.KiotaBuilder[0]
CleanUpInternalState 00:00:00
dbug: Kiota.Builder.KiotaBuilder[0]
step 7 - create source model - took 00:00:00.0560537
dbug: Kiota.Builder.KiotaBuilder[0]
34ms: Language refinement applied
dbug: Kiota.Builder.KiotaBuilder[0]
step 8 - refine by language - took 00:00:00.0353032
dbug: Kiota.Builder.KiotaBuilder[0]
step 9 - writing files - took 00:00:00.0255322
info: Kiota.Builder.KiotaBuilder[0]
loaded description from local source
dbug: Kiota.Builder.KiotaBuilder[0]
step 10 - writing lock file - took 00:00:00.0142002
Generation completed successfully
dbug: Kiota.Builder.KiotaBuilder[0]
Api manifest path: /kiota-test/apimanifest.json

Hint: use the info command to get the list of dependencies you need to add to your project.
Example: kiota info -d "/kiota-test/swagger.yml" -l TypeScript

Hint: use the --include-path and --exclude-path options with glob patterns to filter the paths generated.
Example: kiota generate --include-path "**/foo" -d "/kiota-test/swagger.yml"

```

### Other information

_No response_

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the issue with the attached swagger.yml using the documented kiota generate command, then inspect ./typescript/models/index.ts and trace the generated serializeBaseType and serializeDerivedType functions. Compare the generated control flow with the expected behavior in the issue; done means derived type properties serialize alongside base type properties.

Written by the indexing model from the issue text.

Assessment

Tech stack
openapi, typescript
Domain
api, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.