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

add option --explicit-types to allow customize tagged types in exported code model. #3976

Merged
merged 8 commits into from
Aug 22, 2022
Merged
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
18 changes: 18 additions & 0 deletions tools/sdk-testgen/packages/autorest.testmodeler/CHANGELOG.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
{
"name": "@autorest/testmodeler",
"entries": [
{
"version": "2.3.2",
"tag": "@autorest/testmodeler_v2.3.2",
"date": "Mon, 22 Aug 2022 03:25:13 GMT",
"comments": {
"patch": [
{
"comment": "add option --explicit-types to allow customize tagged types in exported code model."
},
{
"comment": "fix bugs in loading remote api scenario"
},
{
"comment": "fix bug in outputVariableModel generation when there is no response in scenario step"
}
]
}
},
{
"version": "2.3.1",
"tag": "@autorest/testmodeler_v2.3.1",
Expand Down
11 changes: 10 additions & 1 deletion tools/sdk-testgen/packages/autorest.testmodeler/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
# Change Log - @autorest/testmodeler

This log was last generated on Fri, 05 Aug 2022 09:25:55 GMT and should not be manually modified.
This log was last generated on Mon, 22 Aug 2022 03:25:13 GMT and should not be manually modified.

## 2.3.2
Mon, 22 Aug 2022 03:25:13 GMT

### Patches

- add option --explicit-types to allow customize tagged types in exported code model.
- fix bugs in loading remote api scenario
- fix bug in outputVariableModel generation when there is no response in scenario step

## 2.3.1
Fri, 05 Aug 2022 09:25:55 GMT
Expand Down
12 changes: 12 additions & 0 deletions tools/sdk-testgen/packages/autorest.testmodeler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,18 @@ isDataAction: false
count: 64
```

### --testmodeler.explicit-types

A list for types need to explicitly tagged when export-explicit-type is true. The default explicitTypes are ['bool', 'int', 'float', 'timestamp']. Test generators can change it like below:

```
testmodeler:
explicit-types:
- bool
- int
- float
```

## Autorest Pipeline Configurations

```yaml
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@autorest/testmodeler",
"version": "2.3.1",
"version": "2.3.2",
"description": "Autorest extension for testmodeler",
"main": "dist/index.js",
"scripts": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export enum Config {
addArmTemplatePayloadString = 'testmodeler.add-armtemplate-payload-string',
apiScenarioLoaderOption = 'testmodeler.api-scenario-loader-option',
exportExplicitType = 'testmodeler.export-explicit-type',
explicitTypes = 'testmodeler.explicit-types',
}

export const configDefaults = {
Expand All @@ -26,6 +27,7 @@ export const configDefaults = {
[Config.useExampleModel]: true,
[Config.addArmTemplatePayloadString]: false,
[Config.exportExplicitType]: false,
[Config.explicitTypes]: ['bool', 'int', 'float', 'timestamp'],
};

export enum TestScenarioVariableNames {
Expand Down
37 changes: 21 additions & 16 deletions tools/sdk-testgen/packages/autorest.testmodeler/src/core/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,15 @@ export enum OutputVariableModelType {
object = 'object',
}

function findResponseSchema(operation: Operation, statusCode: string): SchemaResponse {
for (const response of operation.responses || []) {
if ((response.protocol.http?.statusCodes || []).indexOf(statusCode) >= 0) {
return response as SchemaResponse;
}
}
return undefined;
}

export class TestCodeModeler {
public static instance: TestCodeModeler;
public testConfig: TestConfig;
Expand Down Expand Up @@ -338,18 +347,9 @@ export class TestCodeModeler {
}
}

function findResponseSchema(statusCode: string): SchemaResponse {
for (const response of operation.responses || []) {
if ((response.protocol.http?.statusCodes || []).indexOf(statusCode) >= 0) {
return response as SchemaResponse;
}
}
return undefined;
}

for (const [statusCode, response] of Object.entries(exampleExtension.responses)) {
const exampleExtensionResponse = response;
const schemaResponse = findResponseSchema(statusCode);
const schemaResponse = findResponseSchema(operation, statusCode);
if (schemaResponse) {
exampleModel.responses[statusCode] = ExampleResponse.createInstance(session, exampleExtensionResponse, schemaResponse.schema, schemaResponse.language);
}
Expand Down Expand Up @@ -486,7 +486,7 @@ export class TestCodeModeler {
// JsonPointer use '/' to seperate the token and only can point to one value. Token is a number or a string.
const valueParts = variableConfig.fromResponse.split('/');
// The root schema is from the http body. We only get value from the '200' response for now.
let currentSchema = step.exampleModel.responses['200'].body.schema;
let currentSchema = findResponseSchema(operation, '200')?.schema;
step.outputVariablesModel[variableName] = [];
for (let i = 1; i < valueParts.length; i++) {
const valuePart = valueParts[i];
Expand Down Expand Up @@ -583,14 +583,17 @@ export class TestCodeModeler {

public async loadTestResources(session: Session<TestCodeModel>) {
try {
const fileRoot = this.testConfig.getSwaggerFolder();
let fileRoot = this.testConfig.getSwaggerFolder() || '';
if (fileRoot.endsWith('/') || fileRoot.endsWith('\\')) {
fileRoot = fileRoot.substring(0, fileRoot.length - 1);
}
if (Array.isArray(this.testConfig.config[Config.testResources])) {
await this.loadTestResourcesFromConfig(session, fileRoot);
} else {
await this.loadAvailableTestResources(session, fileRoot);
}
} catch (error) {
session.warning('Exception occured when load test resource scenario: ${error.stack}', ['Test Modeler']);
session.warning(`Exception occured when load test resource scenario: ${error.stack}`, ['Test Modeler']);
}
}

Expand All @@ -606,14 +609,16 @@ export class TestCodeModeler {
}

public async loadTestResourcesFromConfig(session: Session<TestCodeModel>, fileRoot: string) {
const codemodelRestCallOnly = this.testConfig.getValue(Config.scenarioCodeModelRestCallOnly);
for (const testResource of this.testConfig.getValue(Config.testResources)) {
const testFile = typeof testResource === 'string' ? testResource : testResource[Config.test];
try {
const loader = ApiScenarioLoader.create(this.createApiScenarioLoaderOption(fileRoot));
const testDef = (await loader.load(testResource[Config.test])) as TestDefinitionModel;
this.initiateTestDefinition(session, testDef);
const testDef = (await loader.load(testFile)) as TestDefinitionModel;
this.initiateTestDefinition(session, testDef, codemodelRestCallOnly);
this.codeModel.testModel.scenarioTests.push(testDef);
} catch (error) {
session.warning(`Exception occured when load testdef ${testResource[Config.test]}: ${error}`, ['Test Modeler']);
session.warning(`Exception occured when load testdef ${testFile}: ${error}`, ['Test Modeler']);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export async function processRequest(host: AutorestExtensionHost): Promise<void>
codeModel.genMockTests(session);
await codeModel.loadTestResources(session);

await Helper.outputToModelerfour(host, session, config.getValue(Config.exportExplicitType));
await Helper.outputToModelerfour(host, session, config.getValue(Config.exportExplicitType), config.getValue(Config.explicitTypes));
if (config.getValue(Config.exportCodemodel)) {
Helper.addCodeModelDump(session, 'test-modeler.yaml', false);
if (config.getValue(Config.exportExplicitType)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,24 @@ import { comment, serialize } from '@azure-tools/codegen';

export class Helper {
static dumpBuf: Record<string, any> = {};
public static async outputToModelerfour(host: AutorestExtensionHost, session: Session<CodeModel>, exportExplicitTypes: boolean): Promise<void> {
public static async outputToModelerfour(
host: AutorestExtensionHost,
session: Session<CodeModel>,
exportExplicitTypes: boolean,
explicitTypes: string[] = undefined,
): Promise<void> {
// write the final result first which is hardcoded in the Session class to use to build the model..
// overwrite the modelerfour which should be fine considering our change is backward compatible
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const modelerfourOptions = await session.getValue('modelerfour', {});
if (modelerfourOptions['emit-yaml-tags'] !== false) {
if (exportExplicitTypes) {
codeModelSchema.explicit = (codeModelSchema.explicit || []).concat(codeModelSchema.implicit);
codeModelSchema.implicit = [];
codeModelSchema.compiledExplicit = (codeModelSchema.compiledExplicit || []).concat(codeModelSchema.compiledImplicit);
codeModelSchema.compiledImplicit = [];
codeModelSchema.explicit = codeModelSchema.explicit.concat(codeModelSchema.implicit.filter((t) => Helper.isExplicitTypes(t.tag, explicitTypes)));
codeModelSchema.implicit = codeModelSchema.implicit.filter((t) => !Helper.isExplicitTypes(t.tag, explicitTypes));
codeModelSchema.compiledExplicit = codeModelSchema.compiledExplicit.concat(
codeModelSchema.compiledImplicit.filter((t) => Helper.isExplicitTypes(t.tag, explicitTypes)),
);
codeModelSchema.compiledImplicit = codeModelSchema.compiledImplicit.filter((t) => !Helper.isExplicitTypes(t.tag, explicitTypes));
}
host.writeFile({
filename: 'code-model-v4.yaml',
Expand All @@ -35,6 +42,10 @@ export class Helper {
}
}

private static isExplicitTypes(tag: string, explicitTypes: string[] = undefined): boolean {
return tag && (explicitTypes || []).some((t) => tag.endsWith(t));
}

public static addCodeModelDump(session: Session<CodeModel>, fileName: string, withTags: boolean, debugOnly = true) {
this.dumpBuf[(debugOnly ? '__debug/' : '') + fileName] = withTags ? serialize(session.model, { schema: codeModelSchema }) : serialize(session.model);
}
Expand Down
Loading