Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save haolingdong-msft/2d941f452551a6f96e141adbc38ac483 to your computer and use it in GitHub Desktop.
Save haolingdong-msft/2d941f452551a6f96e141adbc38ac483 to your computer and use it in GitHub Desktop.
Adopting TCGC usage and access for spread and flatten body cases

Adopting TCGC usage and access for spread and flatten body cases

Terminology

As flatten or spread concepts are quite confusing, this document will use below definition:

  • spread body case: it means spreading a model using ..., e.g. op(...AModel)
  • flatten body case: it means defining request body without @body and not using spread syntax ..., e.g. op(a: AModel)

TCGC logic

If a body parameter is defined without @body, TCGC will create an anonymous model with spread usage for it, unless it is like case1, where there is only single spread body param, it will not create anonymous model, but reuse the spreaded model.

Cases

playground

We reuse below definitions in below casese

model A {
    p1: string;
    p2: string;  
}

model APatch {
    p1?: string;
    p2?: string;
}

case1: single spread body param

op op1(...A);

TCGC return:

A: {
  access: internal
  usage: spread
}

code-model expected return:

A: {
    usage: ["internal", "input"]
}

client method:

void op1(String p1, String p2);

case2: single flatten body param

op op1(a: A);

TCGC return:

Op1Request: {
    access: internal
    usage: spread
}

A: {
    access: public
    usage: input
}

code-model expected return:

A: {
    usage: ["public", "input"]
}

Op1Request: {
    usage: ["internal", "input"]
}

client method:

void op1(A a);

case3: spread body + flatten body

op op1(s: string, ...A);

TCGC return:

Op1Request: {
    access: internal
    usage: spread
}

code-model expected return:

Op1Request: {
    usage: ["internal", "input"]
}

client method:

void op1(String, s, String p1, String p2);

case4: multiple flatten body param

op op1(s: string, a: A);

TCGC return:

Op1Request: {
    access: internal
    usage: spread
}

A: {
    access: public
    usage: input
}

code-model expected return:

Op1Request: {
    usage: ["internal", "input"]
}

A: {
    access: public
    usage: input
}

client method:

void op1(String s, A a);

case5: single spread body param + parameter

op op1(@path id: string, ...A);

TCGC return:

A: {
  access: internal
  usage: spread
}

code-model expected return:

A: {
    usage: ["internal", "input"]
}

client method:

op1(String id, String p1, String p2);

case6: single flatten body + parameter

op op1(@path id: string, a: A);

TCGC return:

Op1Request: {
  access: internal
  usage: spread
}

A: {
    access: public
    usage: input
}

code-model expected return:

Op1Request: {
    usage: ["internal", "input"]
}

A: {
    usage: ["public", "input"]
}

client method:

void op1(String id, A a);

Case7: flatten body + json-merge-patch

op op1(
    @header contentType: "application/merge-patch+json",
    patch: APatch);

TCGC return:

Op1Request: {
    access: internal
    usage: spread
}

APatch: {
    access: public
    usage: [input, json-merge-patch]
}

code-model expected return:

Op1PatchRequest: {
    access: public
    usage: input
}

APatch: {
    access: public
    usage: input
}

client method:

void op1(Op1PatchRequest patch);

Case8: flatten body + multipart

op op1(
    @header contentType: "multipart/form-data",
    body: bytes);

TCGC return:

Op1Request: {
    access: internal
    usage: [spread, multipart-form-data]
}

code-model expected return:

BodyFileDetails: {
    access: public
    usage: input
}

Op1Request: {
    access: internal
    usage: input
}

client method:

void op1(BodyFileDetails body);

Case9: combination of spread body op and flattened body op

op op1(...A);
op op2(a: A);

TCGC return:

A: {
  access: public
  usage: [input, spread]
}

Op2Request: {
   access: internal
   usage: spread
}

code-model expected return:

A: {
    usage: ["public", "input"]
}

Op2Request: {
   usage: ["internal", "internal"]
}

client method:

void op1(String p1, String p2);
void op2(A a);

Remaining issues

We could treat TCGC returned spread usage on model type as input, but there remains two issues:

  1. case 7: flatten body + json-merge-patch, we don't know the access for Op1PatchRequest from operation.
  2. case 8: flatten body + multipart, as we create our own multipart model BodyFileDetails, and tcgc returns bytes type for the body param, we can't know what is the access.

Solutions

Short term solution:

To unblock our adopting, for both cases, we can do two times processing. Logics like below:

  1. When processing operation, append the operation's access to the operation's body schema's access
  2. When processing model, append TCGC's access to the schema's access.
  3. Do a post processing to calculate the real access.

Long term solution:

  1. case7 json-merge-patch: Haoling will check with .Net to see if they need the access info for json-merge-patch model as well, if they also need to have it, we can create an issue for TCGC to do it. The logic for TCGC is: If Op1Request's usage is spread+json-merge-path, and if it is Java or .Net, then TCGC mark Op1Request model as public.
  2. case8 multipart: I checked with .Net that they will no generate a model for the bytes body, so we don't ask TCGC to handle it. Srikanta will check the azure-core's progress. Finally we will use azure-core's model, and the issue does not exist.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment