In swagger, we have testmodeler
to extends m4 model to help to link swagger example file to corresponding operation, as well as map example value to its type schema. When we switch to TypeSpec, we currently still use swagger example file to show the payload example of an operation. Also, TCGC is the middle model layer for TypeSpec, just like m4 for swagger. So, TCGC need to do what testmodeler
do.
Current example/test generation way of all languages could be grouped into two kinds:
- Use example model in
testmodeler
to generate the example/test codes: Go MPG from swagger, JS HLC, .NET MPG from swagger - Use self-defined example model and do example value mapping/fake value generation by language's code generator: Java, .NET DPG, Python
So, in order to support two patten, TCGC will do the following works:
- Map example file to its operation schema. Since current example file is HTTP payload, the example will be mapped to
SdkHttpOperation
type in TCGC. - Map example's parameters and responses to
SdkHttpParameter
andSdkHttpResponse
. - Map each value of the example to corresponding TCGC types.
export interface SdkHttpOperation extends SdkServiceOperationBase {
__raw: HttpOperation;
kind: "http";
path: string;
verb: HttpVerb;
parameters: (SdkPathParameter | SdkQueryParameter | SdkHeaderParameter)[];
bodyParam?: SdkBodyParameter;
responses: Map<HttpStatusCodeRange | number, SdkHttpResponse>;
exceptions: Map<HttpStatusCodeRange | number | "*", SdkHttpResponse>;
examples?: SdkHttpOperationExample[];
}
export type SdkExampleBase = {
kind: string;
name: string;
description: string;
filePath: string;
rawExample: any;
};
export interface SdkHttpOperationExample extends SdkExampleBase {
kind: "http";
parameters: SdkHttpParameterExample[];
responses: Map<number, SdkHttpResponseExample>;
}
export interface SdkHttpParameterExample {
parameter: SdkHttpParameter;
value: SdkTypeExample;
}
export interface SdkHttpResponseExample {
response: SdkHttpResponse;
headers: SdkHttpResponseHeaderExample[];
value?: SdkTypeExample;
}
export interface SdkHttpResponseHeaderExample {
header: SdkServiceResponseHeader;
value: SdkTypeExample;
}
export type SdkTypeExample =
| SdkStringExample
| SdkNumberExample
| SdkBooleanExample
| SdkNullExample
| SdkAnyExample
| SdkArrayExample
| SdkDictionaryExample
| SdkUnionExample
| SdkModelExample;
export interface SdkExampleTypeBase {
kind: string;
type: SdkType | SdkModelPropertyType;
value: unknown;
}
export interface SdkStringExample extends SdkExampleTypeBase {
kind: "string";
type: SdkBuiltInType | SdkDatetimeType | SdkDurationType | SdkEnumType | SdkConstantType;
value: string;
}
export interface SdkNumberExample extends SdkExampleTypeBase {
kind: "number";
type: SdkBuiltInType | SdkDatetimeType | SdkDurationType | SdkEnumType | SdkConstantType;
value: number;
}
export interface SdkBooleanExample extends SdkExampleTypeBase {
kind: "boolean";
type: SdkBuiltInType | SdkConstantType;
value: boolean;
}
export interface SdkNullExample extends SdkExampleTypeBase {
kind: "null";
type: SdkNullableType;
value: null;
}
export interface SdkAnyExample extends SdkExampleTypeBase {
kind: "any";
type: SdkBuiltInType;
value: unknown;
}
export interface SdkArrayExample extends SdkExampleTypeBase {
kind: "array";
type: SdkArrayType;
value: SdkTypeExample[];
}
export interface SdkDictionaryExample extends SdkExampleTypeBase {
kind: "dict";
type: SdkDictionaryType;
value: Record<string, SdkTypeExample>;
}
export interface SdkUnionExample extends SdkExampleTypeBase {
kind: "union";
type: SdkUnionType;
value: unknown;
}
export interface SdkModelExample extends SdkExampleTypeBase {
kind: "model";
type: SdkModelType;
value: Record<string, SdkTypeExample>;
additionalProperties?: Record<string, SdkTypeExample>;
}
- For
SdkUnionType
, since it is hard to do mapping to the union variant, TCGC will just map the value to the union type, not to the union variant type. - For discriminated models, TCGC will map the value to the exact subtype based on the discriminator.
- For additional properties, TCGC will only map the the outermost model with additional properties, not to find the model with the right type.
function getExamples(operation: HttpOperation): SdkHttpOperationExample[] {}
If example value type is not right, add warnings to diagnostics.
Use same config from typespec-autorest
emitter: examples-directory
.
{
"kind": "http",
"name": "TestResources_TestAction",
"description": "Action example for specific test resource",
"parameters": [
{
"parameter": {
"kind": "path",
"serializedName": "subscriptionId"
},
"value": {
"kind": "string",
"type": {
"kind": "string"
},
"value": "00000000-0000-0000-0000-000000000000"
}
},
{
"parameter": {
"kind": "body",
},
"value": {
"kind": "model",
"type": {
"kind": "model",
"name": "Foo",
"properties": []
},
"value": {
"prop": {
{
"kind": "string",
"type": {
"kind": "string"
},
"value": "test"
}
}
},
}
}
],
"responses": {
200: {
"response": {
"kind": "http",
},
"value": {
"kind": "number",
"type": {
"kind": "int32"
},
"value": 3
}
}
}
}
Since example is based on http payload, so I map it to
SdkServiceOperation
. You could easily map it back toSdkServiceMethod
.Got it.