Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tcgc] new spread behavior #886

Merged
merged 35 commits into from
Jul 2, 2024
Merged

[tcgc] new spread behavior #886

merged 35 commits into from
Jul 2, 2024

Conversation

tadelesh
Copy link
Member

@tadelesh tadelesh commented May 21, 2024

resolve: #772

several cases related with spread:

op test(a: string, b: string, c: string): void;
model Test {
  prop: string
}
op test(...Test): void;
op test(@body body: {prop: string}): void;
model Test {
  prop: string
}
op test(@body body: {...Test}): void;

tcgc will do http body spread for 1 and 2, not for 3 and 4. and for http body model, 1, 2 will have a model TestRequest with spread usage and internal access.

Adoption guideline for emitters:

For emitters that have already adopted getAllOperations, if an operation has spread parameters:

  1. The parameters of SdkServiceMethod are spread.
  2. The body parameter of SdkServiceOperation is an anonymous model with Usage.Spread usage and Access.Internal access. And the correspondingMethodParams of the body will be list of the parameters of SdkServiceMethod.
  3. Emitters could rely on the above info to generate clients' code.

For emitters that have not adopted getAllOperations, emitters need to do:

  1. Use getHttpOperationWithCache to prevent different body type resolving for spread cases.
  2. Refer this code:
    // Special logic for spread body model:
    // If body is from spread, then it should be an anonymous model.
    // Also all model properties should be
    // either equal to one of operation parameters (for case spread from model without property with metadata decorator)
    // or its source property equal to one of operation parameters (for case spread from model with property with metadata decorator)
    if (
    httpBody.type.kind === "Model" &&
    httpBody.type.name === "" &&
    [...httpBody.type.properties.keys()].every(
    (k) =>
    operation.parameters.properties.has(k) &&
    (operation.parameters.properties.get(k) ===
    (httpBody.type as Model).properties.get(k) ||
    operation.parameters.properties.get(k) ===
    (httpBody.type as Model).properties.get(k)?.sourceProperty)
    to change emitter's logic of handling operations with spread parameters.
  3. When call getClientType from the body type of operation with spread, the return model will be an anonymous model with Usage.Spread usage and Access.Internal access. DO NOT CALL getEffectivePayloadType for body type if it is from spread.

@azure-sdk
Copy link
Collaborator

azure-sdk commented May 21, 2024

All changed packages have been documented.

  • @azure-tools/typespec-client-generator-core
Show changes

@azure-tools/typespec-client-generator-core - breaking ✏️

always spread models and aliases with ...

@azure-sdk
Copy link
Collaborator

Copy link
Contributor

@iscai-msft iscai-msft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @tadelesh

@haolingdong-msft
Copy link
Member

Could we add some guidance on how should emitter adopt this?

@tadelesh
Copy link
Member Author

tadelesh commented Jul 1, 2024

Could we add some guidance on how should emitter adopt this?

added in description.

@haolingdong-msft
Copy link
Member

Overall looks good to me. Just to confirm: so emitter will use body param's access and usage of SdkServiceOperation
to determine whether it's spread body or not, if it is spread body, then read from SdkServiceOperation.body.correspondingMethodParams to get the spread parameter list? So seems emitters don't need to interact with SdkServiceMethod?

@weidongxu-microsoft
Copy link
Member

Tested on Java emitter, should be good now.

@tadelesh tadelesh added this pull request to the merge queue Jul 2, 2024
Merged via the queue into main with commit 56a4f7a Jul 2, 2024
21 checks passed
@tadelesh tadelesh deleted the new_spread branch July 2, 2024 07:47
@@ -64,7 +64,6 @@ import {
getCrossLanguageDefinitionId,
getCrossLanguagePackageId,
getDefaultApiVersion,
getEffectivePayloadType,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deco getEffectivePayloadType?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think java won't use this. So, agree on deprecation.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is still used in response.

deepStrictEqual(op.bodyParam.correspondingMethodParams, [documentMethodParam]);
});

it("anonymous model with @body should not be spread", async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a case for alias? I believe it is the same as spread model?

alias Test = {
  prop: string
};
op test(...Test): void;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about this one? Also spread case, right?

model Test {
  @header
  foo: string;
  prop: string
};
op test(...Test): void;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as spread model, i think it has been covered by another case.

Copy link
Member

@weidongxu-microsoft weidongxu-microsoft Jul 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I take is

  1. if there is @body or @bodyRoot anywhere, it is explicit body, and it is not spread
  2. otherwise, it is implicit body, and it is spread

model or alias no longer matter.

@@ -598,4 +598,6 @@ export enum UsageFlags {
JsonMergePatch = 1 << 4,
// Input will also be set when MultipartFormData is set.
MultipartFormData = 1 << 5,
// Used in spread.
Spread = 1 << 6,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe not relevant to this pr, just want to clarify UsageFlags, if a model is used in

  • patch operation for input
  • another operation for spread input
  • another operation for output

How many medels we will get from TCGC?

Copy link
Member Author

@tadelesh tadelesh Jul 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

two. one original model with path|output, one anonymous model with spread.

for (const sdkType of context.modelsMap?.values() ?? []) {
// if a type only has spread usage, then it could be internal
if (sdkType.usage === UsageFlags.Spread) {
sdkType.access = "internal";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a model is only used for spread, does that mean the TCGC would not return that model in getAllModels by default?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it will return it but with access internal

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[tcgc] switch ... to always mean spreading, regardless of model or alias
7 participants