fix variable patches (#841)
* fix variable patches * add test case * update changelog * update snapshot
This commit is contained in:
Родитель
f37550d3cb
Коммит
c1996477d6
|
@ -1,6 +1,7 @@
|
||||||
# Change Log - oav
|
# Change Log - oav
|
||||||
|
|
||||||
## 07/20/2022 3.0.4
|
## 07/20/2022 3.0.4
|
||||||
|
|
||||||
- GenerateExamples - Support data generation in byte format
|
- GenerateExamples - Support data generation in byte format
|
||||||
- ModelValidator - Support data validation in byte format
|
- ModelValidator - Support data validation in byte format
|
||||||
- API Scenario
|
- API Scenario
|
||||||
|
@ -8,8 +9,10 @@
|
||||||
- Fix step variable unresolved in newman collection
|
- Fix step variable unresolved in newman collection
|
||||||
- Fix bugs about html report
|
- Fix bugs about html report
|
||||||
- Aggregate reports into one per scenario file
|
- Aggregate reports into one per scenario file
|
||||||
|
- Fix bug of object variables and patches
|
||||||
|
|
||||||
## 07/06/2022 3.0.3
|
## 07/06/2022 3.0.3
|
||||||
|
|
||||||
- Generate high quality examples from API Scenario tests
|
- Generate high quality examples from API Scenario tests
|
||||||
|
|
||||||
## 06/30/2022 3.0.2
|
## 06/30/2022 3.0.2
|
||||||
|
|
|
@ -336,7 +336,7 @@ export class ApiScenarioLoader implements Loader<ScenarioDefinition> {
|
||||||
throw new Error("Invalid step");
|
throw new Error("Invalid step");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`Failed to load step ${rawStep.step}: ${(error as any).message}`);
|
throw new Error(`Failed to load step ${JSON.stringify(rawStep)}: ${(error as any).message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (step.outputVariables) {
|
if (step.outputVariables) {
|
||||||
|
@ -381,28 +381,40 @@ export class ApiScenarioLoader implements Loader<ScenarioDefinition> {
|
||||||
|
|
||||||
ctx.stepTracking.set(step.step, step);
|
ctx.stepTracking.set(step.step, step);
|
||||||
|
|
||||||
const getVariable = (name: string): Variable => {
|
const getVariable = (
|
||||||
const variable =
|
name: string,
|
||||||
step.variables[name] ?? ctx.scenario?.variables[name] ?? ctx.scenarioDef.variables[name];
|
...scopes: Array<VariableScope | undefined>
|
||||||
if (variable === undefined) {
|
): Variable | undefined => {
|
||||||
const requiredVariables =
|
if (!scopes || scopes.length === 0) {
|
||||||
ctx.scenario?.requiredVariables ?? ctx.scenarioDef.requiredVariables;
|
scopes = [step, ctx.scenario, ctx.scenarioDef];
|
||||||
if (
|
}
|
||||||
requiredVariables.includes(name) ||
|
for (const scope of scopes) {
|
||||||
(ctx.scenarioDef.scope === "ResourceGroup" &&
|
if (scope && scope.variables[name]) {
|
||||||
["subscriptionId", "resourceGroupName", "location"].indexOf(name) >= 0)
|
return scope.variables[name];
|
||||||
) {
|
}
|
||||||
|
}
|
||||||
|
for (const scope of scopes) {
|
||||||
|
if (scope && scope.requiredVariables.includes(name)) {
|
||||||
return {
|
return {
|
||||||
type: "string",
|
type: "string",
|
||||||
value: `$(${name})`,
|
value: `$(${name})`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return variable;
|
if (
|
||||||
|
ctx.scenarioDef.scope === "ResourceGroup" &&
|
||||||
|
["subscriptionId", "resourceGroupName", "location"].includes(name)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
type: "string",
|
||||||
|
value: `$(${name})`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const requireVariable = (name: string) => {
|
const requireVariable = (name: string) => {
|
||||||
if (["resourceGroupName"].includes(name)) {
|
if (ctx.scenarioDef.scope === "ResourceGroup" && ["resourceGroupName"].includes(name)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const requiredVariables =
|
const requiredVariables =
|
||||||
|
@ -441,7 +453,13 @@ export class ApiScenarioLoader implements Loader<ScenarioDefinition> {
|
||||||
|
|
||||||
if (value.type === "object" || value.type === "secureObject" || value.type === "array") {
|
if (value.type === "object" || value.type === "secureObject" || value.type === "array") {
|
||||||
if (value.patches) {
|
if (value.patches) {
|
||||||
const obj = cloneDeep(getVariable(name));
|
const variable = ctx.scenario
|
||||||
|
? getVariable(name, ctx.scenario, ctx.scenarioDef)
|
||||||
|
: getVariable(name, ctx.scenarioDef);
|
||||||
|
if (!variable) {
|
||||||
|
throw new Error(`Variable ${name} not found in step ${step.step}`);
|
||||||
|
}
|
||||||
|
const obj = cloneDeep(variable);
|
||||||
if (typeof obj !== "object") {
|
if (typeof obj !== "object") {
|
||||||
// TODO dynamic json patch
|
// TODO dynamic json patch
|
||||||
throw new Error(`Can not Json Patch on ${name}, type of ${typeof obj}`);
|
throw new Error(`Can not Json Patch on ${name}, type of ${typeof obj}`);
|
||||||
|
@ -737,6 +755,11 @@ const convertVariables = (rawVariables: RawVariableScope["variables"]) => {
|
||||||
if (val.value === undefined && val.prefix === undefined) {
|
if (val.value === undefined && val.prefix === undefined) {
|
||||||
result.requiredVariables.push(key);
|
result.requiredVariables.push(key);
|
||||||
}
|
}
|
||||||
|
} else if (
|
||||||
|
(val.type === "object" || val.type === "secureObject" || val.type === "array") &&
|
||||||
|
val.patches !== undefined
|
||||||
|
) {
|
||||||
|
// ok
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Only string and secureString type is supported in environment variables, please specify value for: ${key}`
|
`Only string and secureString type is supported in environment variables, please specify value for: ${key}`
|
||||||
|
|
|
@ -37,7 +37,7 @@ export class TemplateGenerator {
|
||||||
|
|
||||||
public exampleParameterConvention(
|
public exampleParameterConvention(
|
||||||
step: Pick<StepRestCall, "parameters" | "responses" | "operation">,
|
step: Pick<StepRestCall, "parameters" | "responses" | "operation">,
|
||||||
variables: (name: string) => any,
|
getVariable: (name: string) => any,
|
||||||
operation: Operation
|
operation: Operation
|
||||||
) {
|
) {
|
||||||
const toMatch: string[] = [];
|
const toMatch: string[] = [];
|
||||||
|
@ -45,7 +45,7 @@ export class TemplateGenerator {
|
||||||
|
|
||||||
const parameters = cloneDeep(step.parameters);
|
const parameters = cloneDeep(step.parameters);
|
||||||
for (const paramName of Object.keys(parameters)) {
|
for (const paramName of Object.keys(parameters)) {
|
||||||
if (variables(paramName) === undefined) {
|
if (getVariable(paramName) === undefined) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -771,6 +771,111 @@ Object {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Object {
|
||||||
|
"_scenarioDef": [Circular],
|
||||||
|
"description": "",
|
||||||
|
"requiredVariables": Array [
|
||||||
|
"subscriptionId",
|
||||||
|
"location",
|
||||||
|
],
|
||||||
|
"scenario": "patchVariables",
|
||||||
|
"secretVariables": Array [],
|
||||||
|
"shareScope": true,
|
||||||
|
"steps": Array [
|
||||||
|
Object {
|
||||||
|
"description": undefined,
|
||||||
|
"operation": Object {},
|
||||||
|
"operationId": "StorageAccounts_Create",
|
||||||
|
"outputVariables": Object {},
|
||||||
|
"parameters": Object {
|
||||||
|
"accountName": "$(accountName)",
|
||||||
|
"api-version": "2021-08-01",
|
||||||
|
"parameters": Object {
|
||||||
|
"properties": Object {
|
||||||
|
"kind": "StorageV2",
|
||||||
|
"location": "$(location)",
|
||||||
|
"patches": Array [
|
||||||
|
Object {
|
||||||
|
"add": "/properties/kind",
|
||||||
|
"value": "StorageV2",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"properties": Object {
|
||||||
|
"encryption": Object {
|
||||||
|
"keySource": "Microsoft.Storage",
|
||||||
|
"services": Object {
|
||||||
|
"blob": Object {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sku": "Standard",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"resourceGroupName": "$(resourceGroupName)",
|
||||||
|
"subscriptionId": "$(subscriptionId)",
|
||||||
|
},
|
||||||
|
"requiredVariables": Array [],
|
||||||
|
"responses": Object {},
|
||||||
|
"secretVariables": Array [],
|
||||||
|
"step": "createStorageAccount",
|
||||||
|
"type": "restCall",
|
||||||
|
"variables": Object {
|
||||||
|
"parameters": Object {
|
||||||
|
"type": "object",
|
||||||
|
"value": Object {
|
||||||
|
"properties": Object {
|
||||||
|
"kind": "StorageV2",
|
||||||
|
"location": "$(location)",
|
||||||
|
"patches": Array [
|
||||||
|
Object {
|
||||||
|
"add": "/properties/kind",
|
||||||
|
"value": "StorageV2",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"properties": Object {
|
||||||
|
"encryption": Object {
|
||||||
|
"keySource": "Microsoft.Storage",
|
||||||
|
"services": Object {
|
||||||
|
"blob": Object {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sku": "Standard",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"variables": Object {
|
||||||
|
"parameters": Object {
|
||||||
|
"type": "object",
|
||||||
|
"value": Object {
|
||||||
|
"properties": Object {
|
||||||
|
"kind": "StorageV2",
|
||||||
|
"location": "$(location)",
|
||||||
|
"patches": Array [
|
||||||
|
Object {
|
||||||
|
"add": "/properties/kind",
|
||||||
|
"value": "StorageV2",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"properties": Object {
|
||||||
|
"encryption": Object {
|
||||||
|
"keySource": "Microsoft.Storage",
|
||||||
|
"services": Object {
|
||||||
|
"blob": Object {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sku": Object {
|
||||||
|
"name": "Standard_LRS",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
"scope": "ResourceGroup",
|
"scope": "ResourceGroup",
|
||||||
"secretVariables": Array [],
|
"secretVariables": Array [],
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -28,7 +28,7 @@ scenarios:
|
||||||
kind: StorageV2
|
kind: StorageV2
|
||||||
location: $(location)
|
location: $(location)
|
||||||
properties:
|
properties:
|
||||||
encryption: {"services": {"blob": {}}, "keySource": "Microsoft.Storage"}
|
encryption: { "services": { "blob": {} }, "keySource": "Microsoft.Storage" }
|
||||||
- scenario: overrideVariable
|
- scenario: overrideVariable
|
||||||
variables:
|
variables:
|
||||||
accountName: $(accountName)2
|
accountName: $(accountName)2
|
||||||
|
@ -52,7 +52,7 @@ scenarios:
|
||||||
kind: StorageV2
|
kind: StorageV2
|
||||||
location: $(location)
|
location: $(location)
|
||||||
properties:
|
properties:
|
||||||
encryption: {"services": {"blob": {}}, "keySource": "Microsoft.Storage"}
|
encryption: { "services": { "blob": {} }, "keySource": "Microsoft.Storage" }
|
||||||
- scenario: primitiveParameter
|
- scenario: primitiveParameter
|
||||||
variables:
|
variables:
|
||||||
accountName:
|
accountName:
|
||||||
|
@ -78,7 +78,7 @@ scenarios:
|
||||||
kind: StorageV2
|
kind: StorageV2
|
||||||
location: $(location)
|
location: $(location)
|
||||||
properties:
|
properties:
|
||||||
encryption: {"services": {"blob": {}}, "keySource": "Microsoft.Storage"}
|
encryption: { "services": { "blob": {} }, "keySource": "Microsoft.Storage" }
|
||||||
- step: DeletedAccounts_Get
|
- step: DeletedAccounts_Get
|
||||||
operationId: DeletedAccounts_Get
|
operationId: DeletedAccounts_Get
|
||||||
parameters:
|
parameters:
|
||||||
|
@ -87,3 +87,27 @@ scenarios:
|
||||||
deletedAccountName:
|
deletedAccountName:
|
||||||
type: string
|
type: string
|
||||||
prefix: foobar
|
prefix: foobar
|
||||||
|
- scenario: patchVariables
|
||||||
|
variables:
|
||||||
|
parameters:
|
||||||
|
type: object
|
||||||
|
value:
|
||||||
|
properties:
|
||||||
|
sku:
|
||||||
|
name: Standard_LRS
|
||||||
|
kind: StorageV2
|
||||||
|
location: $(location)
|
||||||
|
properties:
|
||||||
|
encryption: { "services": { "blob": {} }, "keySource": "Microsoft.Storage" }
|
||||||
|
patches:
|
||||||
|
- add: /properties/kind
|
||||||
|
value: StorageV2
|
||||||
|
steps:
|
||||||
|
- step: createStorageAccount
|
||||||
|
operationId: StorageAccounts_Create
|
||||||
|
variables:
|
||||||
|
parameters:
|
||||||
|
type: object
|
||||||
|
patches:
|
||||||
|
- replace: /properties/sku
|
||||||
|
value: Standard
|
||||||
|
|
Загрузка…
Ссылка в новой задаче