Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 26 additions & 2 deletions packages/typespec-ts/src/modular/helpers/operationHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1514,6 +1514,12 @@ function getSerializationExpressionForFlatten(
!isReadOnly(p) &&
!isMetadata(context.program, p.__raw!)
);

// If all properties are readonly, don't serialize this flatten property at all
if (validProps.length === 0) {
return "undefined";
}

const optionalPrefix = property.optional
? `${resolveReference(SerializationHelpers.areAllPropsUndefined)}(${propertyPath}, [${validProps
.map((p) => `"${p.name}"`)
Expand Down Expand Up @@ -1618,7 +1624,9 @@ export function getRequestModelMapping(
propertyPath,
overrides,
enableFlatten
).map(([name, value]) => `"${name}": ${value}`);
)
.filter(([_name, value]) => value !== "undefined")
.map(([name, value]) => `"${name}": ${value}`);
}

function getPropertySerializedName(
Expand Down Expand Up @@ -1684,8 +1692,24 @@ export function getResponseMapping(
const propertyName = normalizeModelPropertyName(context, property);
if (deserializeFunctionName) {
if (isSupportedFlatten) {
// Check if all properties of the flattened type are readonly
const flattenedProps = getAllProperties(
context,
property.type,
getAllAncestors(property.type)
).filter(
(p) => p.kind === "property" && !isMetadata(context.program, p.__raw!)
);
const allPropsReadonly = flattenedProps.every((p) => isReadOnly(p));

// For flatten properties in responses:
// - If all properties are readonly, they're flattened at the parent level
// - Otherwise, they're nested under the property name
const flattenPath = allPropsReadonly
? propertyPath || "item"
: restValue;
props.push(
`...${nullOrUndefinedPrefix}${deserializeFunctionName}(${restValue})`
`...${nullOrUndefinedPrefix}${deserializeFunctionName}(${flattenPath})`
);
} else {
props.push(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -507,9 +507,26 @@ function buildModelTypeSerializer(
return ${serializeContent}
`);
} else {
output.push(`
return item;
`);
// For flatten properties, if all properties are readonly, return empty object
// Otherwise, return the item itself
if (options.flatten) {
// Change parameter name to _item to indicate it's intentionally unused
serializerFunction.parameters = [
{
name: "_item",
type: options.flatten
? resolveReference(refkey(options.flatten.baseModel))
: resolveReference(refkey(type))
}
];
output.push(`
return {};
`);
} else {
output.push(`
return item;
`);
}
}
serializerFunction.statements = output;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,20 @@ export declare class FlattenPropertyClient {
private _client;
readonly pipeline: Pipeline;
constructor(options?: FlattenPropertyClientOptionalParams);
putFlattenReadOnlyModel(body: Solution, options?: PutFlattenReadOnlyModelOptionalParams): Promise<Solution>;
putFlattenUnknownModel(input: FlattenUnknownModel, options?: PutFlattenUnknownModelOptionalParams): Promise<FlattenUnknownModel>;
putNestedFlattenModel(input: NestedFlattenModel, options?: PutNestedFlattenModelOptionalParams): Promise<NestedFlattenModel>;
putFlattenModel(input: FlattenModel, options?: PutFlattenModelOptionalParams): Promise<FlattenModel>;
}

export declare interface FlattenPropertyClientOptionalParams extends ClientOptions {
}

export declare interface FlattenUnknownModel {
name: string;
properties?: any;
}

export declare interface NestedFlattenModel {
name: string;
summary: string;
Expand All @@ -39,7 +46,29 @@ export declare interface NestedFlattenModel {
export declare interface PutFlattenModelOptionalParams extends OperationOptions {
}

export declare interface PutFlattenReadOnlyModelOptionalParams extends OperationOptions {
}

export declare interface PutFlattenUnknownModelOptionalParams extends OperationOptions {
}

export declare interface PutNestedFlattenModelOptionalParams extends OperationOptions {
}

export declare interface Solution {
name: string;
readonly solutionId?: string;
readonly title?: string;
readonly content?: string;
readonly solutionIdPropertiesOptionalSolutionId?: string;
readonly titlePropertiesOptionalTitle?: string;
readonly contentPropertiesOptionalContent?: string;
}

export declare interface SolutionProperties {
readonly solutionId?: string;
readonly title?: string;
readonly content?: string;
}

export { }
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,23 @@ describe("Property Flatten Client", () => {
assert.strictEqual(result.properties.description, "foo");
assert.strictEqual(result.properties.age, 1);
});

it("Update and receive model with unknown properties flattening", async () => {
const result = await client.putFlattenUnknownModel({
name: "foo"
});
assert.strictEqual(result.name, "test");
assert.strictEqual(result.properties?.key1, "value1");
assert.strictEqual(result.properties?.key2, "value2");
});

it("Update and receive model with read-only properties flattening", async () => {
const result = await client.putFlattenReadOnlyModel({
name: "foo"
});
assert.strictEqual(result.name, "foo");
assert.strictEqual(result.solutionId, "solution1");
assert.strictEqual(result.title, "Solution Title");
assert.strictEqual(result.content, "Solution Content");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ needTCGC: true
## Models

```ts models
import { areAllPropsUndefined } from "../static-helpers/serialization/check-prop-undefined.js";

/**
* This file contains only generated model types and their (de)serializers.
* Disable the following rules for internal models with '_' prefix and deserializers which require 'any' for raw JSON input.
Expand All @@ -54,12 +52,7 @@ export interface Solution {
}

export function solutionSerializer(item: Solution): any {
return {
properties: _solutionPropertiesSerializer(item),
propertiesOptional: areAllPropsUndefined(item, [])
? undefined
: _solutionPropertiesOptionalSerializer(item),
};
return item;
}

/** model interface SolutionProperties */
Expand All @@ -73,11 +66,11 @@ export function solutionPropertiesSerializer(item: SolutionProperties): any {
return item;
}

export function _solutionPropertiesSerializer(item: Solution): any {
return item;
export function _solutionPropertiesSerializer(_item: Solution): any {
return {};
}

export function _solutionPropertiesOptionalSerializer(item: Solution): any {
return item;
export function _solutionPropertiesOptionalSerializer(_item: Solution): any {
return {};
}
```
Loading