apply prettier fix
This commit is contained in:
Родитель
666fb77fef
Коммит
1ce2f33612
|
@ -47,4 +47,4 @@ rules:
|
|||
- warn
|
||||
- max: 2
|
||||
maxBOF: 0
|
||||
maxEOF: 1
|
||||
maxEOF: 1
|
||||
|
|
|
@ -2,4 +2,4 @@ trailingComma: "all"
|
|||
printWidth: 120
|
||||
quoteProps: "consistent"
|
||||
endOfLine: "auto"
|
||||
arrowParens: always
|
||||
arrowParens: always
|
||||
|
|
|
@ -3,33 +3,33 @@ trigger: none
|
|||
pr: none
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
vmImage: "ubuntu-latest"
|
||||
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: '12.x'
|
||||
displayName: 'Install Node.js'
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: "12.x"
|
||||
displayName: "Install Node.js"
|
||||
|
||||
- script: |
|
||||
npm install -g npm
|
||||
npm cache clear --force
|
||||
- script: |
|
||||
npm install -g npm
|
||||
npm cache clear --force
|
||||
|
||||
npx @microsoft/rush update
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
npx @microsoft/rush rebuild
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
npx @microsoft/rush update
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
npx @microsoft/rush rebuild
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
|
||||
displayName: 'Build Modelerfour'
|
||||
displayName: "Build Modelerfour"
|
||||
|
||||
- script: |
|
||||
npm install -g autorest
|
||||
git clone --no-tags --depth=1 https://github.com/Azure/azure-rest-api-specs
|
||||
node .scripts/run-benchmark.js ./azure-rest-api-specs ./output
|
||||
displayName: Benchmark Azure REST API Specs
|
||||
- script: |
|
||||
npm install -g autorest
|
||||
git clone --no-tags --depth=1 https://github.com/Azure/azure-rest-api-specs
|
||||
node .scripts/run-benchmark.js ./azure-rest-api-specs ./output
|
||||
displayName: Benchmark Azure REST API Specs
|
||||
|
||||
- task: PublishPipelineArtifact@1
|
||||
inputs:
|
||||
targetPath: '$(System.DefaultWorkingDirectory)/output'
|
||||
artifactName: benchmark-output
|
||||
displayName: 'Upload Results'
|
||||
- task: PublishPipelineArtifact@1
|
||||
inputs:
|
||||
targetPath: "$(System.DefaultWorkingDirectory)/output"
|
||||
artifactName: benchmark-output
|
||||
displayName: "Upload Results"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const { spawn } = require('child_process');
|
||||
const { readFileSync } = require('fs');
|
||||
const { resolve } = require('path');
|
||||
const { spawn } = require("child_process");
|
||||
const { readFileSync } = require("fs");
|
||||
const { resolve } = require("path");
|
||||
|
||||
function read(filename) {
|
||||
const txt = readFileSync(filename, "utf8")
|
||||
|
@ -17,8 +17,6 @@ const repo = `${__dirname}/..`;
|
|||
const rush = read(`${repo}/rush.json`);
|
||||
const pjs = {};
|
||||
|
||||
|
||||
|
||||
function forEachProject(onEach) {
|
||||
// load all the projects
|
||||
for (const each of rush.projects) {
|
||||
|
@ -42,28 +40,33 @@ function npmForEach(cmd) {
|
|||
const proc = spawn("npm", ["--silent", "run", cmd], { cwd: location, shell: true, stdio: "inherit" });
|
||||
procs.push(proc);
|
||||
result[name] = {
|
||||
name, location, project, proc,
|
||||
name,
|
||||
location,
|
||||
project,
|
||||
proc,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
procs.forEach(proc => proc.on("close", (code, signal) => {
|
||||
count--;
|
||||
exitCode += code;
|
||||
procs.forEach((proc) =>
|
||||
proc.on("close", (code, signal) => {
|
||||
count--;
|
||||
exitCode += code;
|
||||
|
||||
if (count === 0) {
|
||||
const t2 = process.uptime() * 100;
|
||||
if (count === 0) {
|
||||
const t2 = process.uptime() * 100;
|
||||
|
||||
console.log('---------------------------------------------------------');
|
||||
if (exitCode !== 0) {
|
||||
console.log(` Done : command '${cmd}' - ${Math.floor(t2 - t1) / 100} s -- Errors ${exitCode} `)
|
||||
} else {
|
||||
console.log(` Done : command '${cmd}' - ${Math.floor(t2 - t1) / 100} s -- No Errors `)
|
||||
console.log("---------------------------------------------------------");
|
||||
if (exitCode !== 0) {
|
||||
console.log(` Done : command '${cmd}' - ${Math.floor(t2 - t1) / 100} s -- Errors ${exitCode} `);
|
||||
} else {
|
||||
console.log(` Done : command '${cmd}' - ${Math.floor(t2 - t1) / 100} s -- No Errors `);
|
||||
}
|
||||
console.log("---------------------------------------------------------");
|
||||
process.exit(exitCode);
|
||||
}
|
||||
console.log('---------------------------------------------------------');
|
||||
process.exit(exitCode);
|
||||
}
|
||||
}));
|
||||
}),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
// Runs the npm run command on each project that has it.
|
||||
require('./for-each').npm(process.argv[2]);
|
||||
require("./for-each").npm(process.argv[2]);
|
||||
|
|
|
@ -4,59 +4,58 @@
|
|||
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
|
||||
|
||||
trigger:
|
||||
- master
|
||||
- master
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
vmImage: "ubuntu-latest"
|
||||
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: '10.x'
|
||||
displayName: 'Install Node.js'
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: "10.x"
|
||||
displayName: "Install Node.js"
|
||||
|
||||
- script: |
|
||||
# ensure latest npm is installed
|
||||
npm install -g npm
|
||||
npm install -g publish-release
|
||||
- script: |
|
||||
# ensure latest npm is installed
|
||||
npm install -g npm
|
||||
npm install -g publish-release
|
||||
|
||||
# make sure the versions are all synchronized and pull in dependencies
|
||||
npx @microsoft/rush sync-versions
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
# make sure the versions are all synchronized and pull in dependencies
|
||||
npx @microsoft/rush sync-versions
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
|
||||
npx @microsoft/rush update
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
npx @microsoft/rush update
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
|
||||
# set the actual package versions and update again
|
||||
npx @microsoft/rush set-versions
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
# set the actual package versions and update again
|
||||
npx @microsoft/rush set-versions
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
|
||||
npx @microsoft/rush sync-versions
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
npx @microsoft/rush sync-versions
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
|
||||
npx @microsoft/rush update
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
npx @microsoft/rush update
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
|
||||
|
||||
# compile the code
|
||||
npx @microsoft/rush rebuild
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
# compile the code
|
||||
npx @microsoft/rush rebuild
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
|
||||
# build the packages
|
||||
npx @microsoft/rush publish --publish --pack --include-all --tag latest
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
# build the packages
|
||||
npx @microsoft/rush publish --publish --pack --include-all --tag latest
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
|
||||
# publish autorest cli and beta (@autorest/autorest)
|
||||
cd modelerfour
|
||||
v=`node -e "console.log(require('./package.json').version)"`
|
||||
publish-release --token $(azuresdk-github-pat) --repo autorest.modelerfour --owner azure --name v$v --tag v$v --notes='prerelease build' --prerelease --editRelease false --assets ../common/temp/artifacts/packages/autorest-modelerfour-$v.tgz --target_commitish $(Build.SourceBranchName)
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
cd ..
|
||||
|
||||
# publish the packages (tag as preview by default)
|
||||
# echo "//registry.npmjs.org/:_authToken=$(azure-sdk-npm-token)" > ./.npmrc
|
||||
# for file in common/temp/artifacts/packages/*.tgz
|
||||
# do
|
||||
# common/temp/pnpm-local/node_modules/.bin/pnpm publish $file --tag latest --access public || echo no-worries
|
||||
# done
|
||||
# publish autorest cli and beta (@autorest/autorest)
|
||||
cd modelerfour
|
||||
v=`node -e "console.log(require('./package.json').version)"`
|
||||
publish-release --token $(azuresdk-github-pat) --repo autorest.modelerfour --owner azure --name v$v --tag v$v --notes='prerelease build' --prerelease --editRelease false --assets ../common/temp/artifacts/packages/autorest-modelerfour-$v.tgz --target_commitish $(Build.SourceBranchName)
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
cd ..
|
||||
|
||||
# publish the packages (tag as preview by default)
|
||||
# echo "//registry.npmjs.org/:_authToken=$(azure-sdk-npm-token)" > ./.npmrc
|
||||
# for file in common/temp/artifacts/packages/*.tgz
|
||||
# do
|
||||
# common/temp/pnpm-local/node_modules/.bin/pnpm publish $file --tag latest --access public || echo no-worries
|
||||
# done
|
||||
|
|
|
@ -5,59 +5,59 @@ debug: false
|
|||
specs:
|
||||
- specRootPath: ../modelerfour/node_modules/@microsoft.azure/autorest.testserver/swagger
|
||||
specPaths:
|
||||
- additionalProperties.json
|
||||
- azure-parameter-grouping.json
|
||||
- azure-report.json
|
||||
- azure-resource-x.json
|
||||
- azure-resource.json
|
||||
- azure-special-properties.json
|
||||
- body-array.json
|
||||
- body-boolean.json
|
||||
- body-boolean.quirks.json
|
||||
- body-byte.json
|
||||
- body-complex.json
|
||||
- body-date.json
|
||||
- body-datetime-rfc1123.json
|
||||
- body-datetime.json
|
||||
- body-duration.json
|
||||
- body-dictionary.json
|
||||
- body-file.json
|
||||
# Not yet supported by AutoRest v3 and Modelerfour
|
||||
# - body-formdata-urlencoded.json
|
||||
# - body-formdata.json
|
||||
- body-integer.json
|
||||
- body-number.json
|
||||
- body-number.quirks.json
|
||||
- body-string.json
|
||||
- body-string.quirks.json
|
||||
- complex-model.json
|
||||
- custom-baseUrl.json
|
||||
- custom-baseUrl-more-options.json
|
||||
- custom-baseUrl-paging.json
|
||||
- extensible-enums-swagger.json
|
||||
- head-exceptions.json
|
||||
- head.json
|
||||
- header.json
|
||||
- httpInfrastructure.json
|
||||
- httpInfrastructure.quirks.json
|
||||
- lro.json
|
||||
- media_types.json
|
||||
- model-flattening.json
|
||||
- multiapi-v1.json
|
||||
- multiapi-v2.json
|
||||
- multiapi-v3.json
|
||||
- object-type.json
|
||||
- paging.json
|
||||
- parameter-flattening.json
|
||||
- report.json
|
||||
- required-optional.json
|
||||
- storage.json
|
||||
- subscriptionId-apiVersion.json
|
||||
- url-multi-collectionFormat.json
|
||||
- url.json
|
||||
- validation.json
|
||||
- xml-service.json
|
||||
- xms-error-responses.json
|
||||
- additionalProperties.json
|
||||
- azure-parameter-grouping.json
|
||||
- azure-report.json
|
||||
- azure-resource-x.json
|
||||
- azure-resource.json
|
||||
- azure-special-properties.json
|
||||
- body-array.json
|
||||
- body-boolean.json
|
||||
- body-boolean.quirks.json
|
||||
- body-byte.json
|
||||
- body-complex.json
|
||||
- body-date.json
|
||||
- body-datetime-rfc1123.json
|
||||
- body-datetime.json
|
||||
- body-duration.json
|
||||
- body-dictionary.json
|
||||
- body-file.json
|
||||
# Not yet supported by AutoRest v3 and Modelerfour
|
||||
# - body-formdata-urlencoded.json
|
||||
# - body-formdata.json
|
||||
- body-integer.json
|
||||
- body-number.json
|
||||
- body-number.quirks.json
|
||||
- body-string.json
|
||||
- body-string.quirks.json
|
||||
- complex-model.json
|
||||
- custom-baseUrl.json
|
||||
- custom-baseUrl-more-options.json
|
||||
- custom-baseUrl-paging.json
|
||||
- extensible-enums-swagger.json
|
||||
- head-exceptions.json
|
||||
- head.json
|
||||
- header.json
|
||||
- httpInfrastructure.json
|
||||
- httpInfrastructure.quirks.json
|
||||
- lro.json
|
||||
- media_types.json
|
||||
- model-flattening.json
|
||||
- multiapi-v1.json
|
||||
- multiapi-v2.json
|
||||
- multiapi-v3.json
|
||||
- object-type.json
|
||||
- paging.json
|
||||
- parameter-flattening.json
|
||||
- report.json
|
||||
- required-optional.json
|
||||
- storage.json
|
||||
- subscriptionId-apiVersion.json
|
||||
- url-multi-collectionFormat.json
|
||||
- url.json
|
||||
- validation.json
|
||||
- xml-service.json
|
||||
- xms-error-responses.json
|
||||
languages:
|
||||
- language: python
|
||||
outputPath: ../modelerfour/test/regression/python
|
||||
|
@ -73,7 +73,7 @@ languages:
|
|||
outputPath: ../modelerfour/test/regression/typescript
|
||||
excludeSpecs:
|
||||
# The paging spec currently fails, will re-enable once it's fixed
|
||||
- paging.json
|
||||
- paging.json
|
||||
oldArgs:
|
||||
- --v3
|
||||
- --package-name:test-package
|
||||
|
|
|
@ -32,10 +32,7 @@ function executeBenchmark(readmePath) {
|
|||
|
||||
fs.mkdirSync(outputPath, { recursive: true });
|
||||
|
||||
const fileStream = fs.openSync(
|
||||
path.join(outputPath, "autorest-output.txt"),
|
||||
"w"
|
||||
);
|
||||
const fileStream = fs.openSync(path.join(outputPath, "autorest-output.txt"), "w");
|
||||
|
||||
console.log(relativePath);
|
||||
|
||||
|
@ -53,7 +50,7 @@ function executeBenchmark(readmePath) {
|
|||
--inspector.output-folder="./${outputPath}" \
|
||||
--output-folder="./${outputPath}" \
|
||||
"${readmePath}"`,
|
||||
{ stdio: ["inherit", fileStream, fileStream] }
|
||||
{ stdio: ["inherit", fileStream, fileStream] },
|
||||
);
|
||||
} catch (err) {
|
||||
result = "failed";
|
||||
|
@ -61,16 +58,14 @@ function executeBenchmark(readmePath) {
|
|||
}
|
||||
|
||||
const elapsedTime = process.hrtime(startTime);
|
||||
const elapsedSeconds = (elapsedTime[0] + elapsedTime[1] / 1000000000).toFixed(
|
||||
3
|
||||
);
|
||||
const elapsedSeconds = (elapsedTime[0] + elapsedTime[1] / 1000000000).toFixed(3);
|
||||
console.log(` └ ${resultColor}${result} in ${elapsedSeconds}`, "\033[0m\n");
|
||||
|
||||
return {
|
||||
specPath: readmePath,
|
||||
outputPath,
|
||||
succeeded: result === "succeeded",
|
||||
time: parseFloat(elapsedSeconds)
|
||||
time: parseFloat(elapsedSeconds),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -79,9 +74,7 @@ console.log("");
|
|||
|
||||
const readmePaths = getReadmesRecursively(specsPath);
|
||||
|
||||
const results = readmePaths
|
||||
.map(executeBenchmark)
|
||||
.sort((a, b) => b.time - a.time);
|
||||
const results = readmePaths.map(executeBenchmark).sort((a, b) => b.time - a.time);
|
||||
|
||||
let aggregate = results.reduce(
|
||||
(totals, result) => {
|
||||
|
@ -89,31 +82,23 @@ let aggregate = results.reduce(
|
|||
totals.time += result.time;
|
||||
return totals;
|
||||
},
|
||||
{ time: 0.0, success: 0 }
|
||||
{ time: 0.0, success: 0 },
|
||||
);
|
||||
|
||||
const successPercentage = ((aggregate.success / results.length) * 100).toFixed(
|
||||
2
|
||||
);
|
||||
const successPercentage = ((aggregate.success / results.length) * 100).toFixed(2);
|
||||
|
||||
console.log(
|
||||
`${aggregate.success} out of ${
|
||||
results.length
|
||||
} succeeded (${successPercentage}%), ${aggregate.time.toFixed(
|
||||
3
|
||||
)}s total time\n`
|
||||
`${aggregate.success} out of ${results.length} succeeded (${successPercentage}%), ${aggregate.time.toFixed(
|
||||
3,
|
||||
)}s total time\n`,
|
||||
);
|
||||
|
||||
const topCount = Math.min(5, results.length);
|
||||
console.log(`Top ${topCount} longest runs:\n`);
|
||||
results
|
||||
.slice(0, topCount)
|
||||
.forEach(r => console.log(`${r.time}s ${r.specPath}`));
|
||||
results.slice(0, topCount).forEach((r) => console.log(`${r.time}s ${r.specPath}`));
|
||||
|
||||
// Write out aggregate results file
|
||||
const resultsFile = fs.writeFileSync(
|
||||
path.join(outputBasePath, "autorest-benchmark-results.csv"),
|
||||
results
|
||||
.map(r => `${r.specPath},${r.succeeded ? "succeeded" : "failed"},${r.time}`)
|
||||
.join("\n")
|
||||
results.map((r) => `${r.specPath},${r.succeeded ? "succeeded" : "failed"},${r.time}`).join("\n"),
|
||||
);
|
||||
|
|
|
@ -1,44 +1,42 @@
|
|||
const { exec } = require('child_process');
|
||||
const { writeFileSync } = require('fs');
|
||||
const { forEachProject, projectCount } = require('./for-each');
|
||||
const { exec } = require("child_process");
|
||||
const { writeFileSync } = require("fs");
|
||||
const { forEachProject, projectCount } = require("./for-each");
|
||||
|
||||
let count = projectCount;
|
||||
|
||||
|
||||
function updateVersion(name, project, location, patch) {
|
||||
const origJson = JSON.stringify(project, null, 2);
|
||||
|
||||
// update the third digit
|
||||
const verInfo = project.version.split('.');
|
||||
const verInfo = project.version.split(".");
|
||||
verInfo[2] = patch;
|
||||
project.version = verInfo.join('.');
|
||||
project.version = verInfo.join(".");
|
||||
|
||||
// write the file if it's changed
|
||||
const newJson = JSON.stringify(project, null, 2);
|
||||
if (origJson !== newJson) {
|
||||
console.log(`Writing project '${name}' version to '${project.version}' in '${location}'`);
|
||||
writeFileSync(`${location}/package.json`, newJson)
|
||||
writeFileSync(`${location}/package.json`, newJson);
|
||||
}
|
||||
|
||||
count--;
|
||||
if (count === 0) {
|
||||
// last one!
|
||||
// call sync-versions
|
||||
require('./sync-versions');
|
||||
require("./sync-versions");
|
||||
}
|
||||
}
|
||||
|
||||
if (process.argv[2] === '--reset') {
|
||||
if (process.argv[2] === "--reset") {
|
||||
forEachProject((name, location, project) => {
|
||||
updateVersion(name, project, location, 0);
|
||||
})
|
||||
});
|
||||
} else {
|
||||
// Sets the patch version on each package.json in the project.
|
||||
forEachProject((name, location, project) => {
|
||||
exec(`git rev-list --parents HEAD --count --full-history .`, { cwd: location }, (o, stdout) => {
|
||||
const patch = (parseInt(stdout.trim()) + (Number(project.patchOffset) || -1));
|
||||
const patch = parseInt(stdout.trim()) + (Number(project.patchOffset) || -1);
|
||||
updateVersion(name, project, location, patch);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
const { readFileSync, writeFileSync } = require('fs');
|
||||
const { readFileSync, writeFileSync } = require("fs");
|
||||
|
||||
function read(filename) {
|
||||
const txt = readFileSync(filename, 'utf8')
|
||||
.replace(/\r/gm, '')
|
||||
.replace(/\n/gm, '«')
|
||||
.replace(/\/\*.*?\*\//gm, '')
|
||||
.replace(/«/gm, '\n')
|
||||
.replace(/\s+\/\/.*/g, '');
|
||||
const txt = readFileSync(filename, "utf8")
|
||||
.replace(/\r/gm, "")
|
||||
.replace(/\n/gm, "«")
|
||||
.replace(/\/\*.*?\*\//gm, "")
|
||||
.replace(/«/gm, "\n")
|
||||
.replace(/\s+\/\/.*/g, "");
|
||||
return JSON.parse(txt);
|
||||
}
|
||||
|
||||
|
@ -15,26 +15,29 @@ const rush = read(`${__dirname}/../rush.json`);
|
|||
const pjs = {};
|
||||
|
||||
function writeIfChanged(filename, content) {
|
||||
const orig = JSON.parse(readFileSync(filename))
|
||||
const orig = JSON.parse(readFileSync(filename));
|
||||
const origJson = JSON.stringify(orig, null, 2);
|
||||
const json = JSON.stringify(content, null, 2);
|
||||
|
||||
if (origJson !== json) {
|
||||
console.log(`Writing updated file '${filename}'`)
|
||||
writeFileSync(filename, json)
|
||||
console.log(`Writing updated file '${filename}'`);
|
||||
writeFileSync(filename, json);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function versionToInt(ver) {
|
||||
let v = ver.replace(/[^\d\.]/g, '').split('.').slice(0, 3);
|
||||
let v = ver
|
||||
.replace(/[^\d\.]/g, "")
|
||||
.split(".")
|
||||
.slice(0, 3);
|
||||
while (v.length < 3) {
|
||||
v.unshift(0);
|
||||
}
|
||||
let n = 0;
|
||||
for (let i = 0; i < v.length; i++) {
|
||||
n = n + ((2 ** (i * 16)) * parseInt(v[v.length - 1 - i]))
|
||||
n = n + 2 ** (i * 16) * parseInt(v[v.length - 1 - i]);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
@ -51,7 +54,6 @@ function setPeerDependencies(dependencies) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
function recordDeps(dependencies) {
|
||||
for (const packageName in dependencies) {
|
||||
const packageVersion = dependencies[packageName];
|
||||
|
@ -71,7 +73,6 @@ function recordDeps(dependencies) {
|
|||
}
|
||||
const v2 = versionToInt(packageList[packageName]);
|
||||
if (v > v2) {
|
||||
|
||||
packageList[packageName] = packageVersion;
|
||||
}
|
||||
} else {
|
||||
|
@ -82,7 +83,7 @@ function recordDeps(dependencies) {
|
|||
function fixDeps(pj, dependencies) {
|
||||
for (const packageName in dependencies) {
|
||||
if (dependencies[packageName] !== packageList[packageName]) {
|
||||
console.log(`updating ${pj}:${packageName} from '${dependencies[packageName]}' to '${packageList[packageName]}'`)
|
||||
console.log(`updating ${pj}:${packageName} from '${dependencies[packageName]}' to '${packageList[packageName]}'`);
|
||||
dependencies[packageName] = packageList[packageName];
|
||||
}
|
||||
}
|
||||
|
@ -100,8 +101,8 @@ for (const pj of Object.getOwnPropertyNames(pjs)) {
|
|||
const each = pjs[pj];
|
||||
setPeerDependencies(each.dependencies);
|
||||
setPeerDependencies(each.devDependencies);
|
||||
if (each['static-link']) {
|
||||
setPeerDependencies(each['static-link'].dependencies);
|
||||
if (each["static-link"]) {
|
||||
setPeerDependencies(each["static-link"].dependencies);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,8 +112,8 @@ for (const pj of Object.getOwnPropertyNames(pjs)) {
|
|||
const each = pjs[pj];
|
||||
recordDeps(each.dependencies);
|
||||
recordDeps(each.devDependencies);
|
||||
if (each['static-link']) {
|
||||
recordDeps(each['static-link'].dependencies);
|
||||
if (each["static-link"]) {
|
||||
recordDeps(each["static-link"].dependencies);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,8 +121,8 @@ for (const pj of Object.getOwnPropertyNames(pjs)) {
|
|||
const each = pjs[pj];
|
||||
fixDeps(pj, each.dependencies);
|
||||
fixDeps(pj, each.devDependencies);
|
||||
if (each['static-link']) {
|
||||
fixDeps(pj, each['static-link'].dependencies);
|
||||
if (each["static-link"]) {
|
||||
fixDeps(pj, each["static-link"].dependencies);
|
||||
}
|
||||
}
|
||||
var changed = 0;
|
||||
|
@ -138,5 +139,5 @@ for (const each of rush.projects) {
|
|||
if (changed) {
|
||||
console.log(`Updated ${changed} files.`);
|
||||
} else {
|
||||
console.log('No changes made')
|
||||
}
|
||||
console.log("No changes made");
|
||||
}
|
||||
|
|
|
@ -4,92 +4,92 @@
|
|||
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
|
||||
|
||||
trigger:
|
||||
- master
|
||||
- master
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
vmImage: "ubuntu-latest"
|
||||
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: '10.x'
|
||||
displayName: 'Install Node.js'
|
||||
|
||||
- script: |
|
||||
npm install -g npm
|
||||
npm cache clear --force
|
||||
- task: NodeTool@0
|
||||
inputs:
|
||||
versionSpec: "10.x"
|
||||
displayName: "Install Node.js"
|
||||
|
||||
npx @microsoft/rush update
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
npx @microsoft/rush rebuild
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
npx @microsoft/rush test
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
- script: |
|
||||
npm install -g npm
|
||||
npm cache clear --force
|
||||
|
||||
# check to see if any files changed after the test run.
|
||||
git status
|
||||
npx @microsoft/rush update
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
npx @microsoft/rush rebuild
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
npx @microsoft/rush test
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
|
||||
# set the actual package versions
|
||||
npx @microsoft/rush set-versions
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
# check to see if any files changed after the test run.
|
||||
git status
|
||||
|
||||
npx @microsoft/rush publish --publish --pack --include-all --tag latest
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
# set the actual package versions
|
||||
npx @microsoft/rush set-versions
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
|
||||
v=`node -e "console.log(require('./modelerfour/package.json').version)"`
|
||||
echo "##vso[task.setvariable variable=artver]$v"
|
||||
npx @microsoft/rush publish --publish --pack --include-all --tag latest
|
||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||
|
||||
displayName: 'Rush install, build and test'
|
||||
v=`node -e "console.log(require('./modelerfour/package.json').version)"`
|
||||
echo "##vso[task.setvariable variable=artver]$v"
|
||||
|
||||
- task: PublishPipelineArtifact@1
|
||||
inputs:
|
||||
targetPath: '$(Build.SourcesDirectory)/common/temp/artifacts/packages/autorest-modelerfour-$(artver).tgz'
|
||||
artifact: 'packages'
|
||||
publishLocation: 'pipeline'
|
||||
displayName: "Rush install, build and test"
|
||||
|
||||
- pwsh: |
|
||||
$url = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$env:SYSTEM_TEAMPROJECTID/_apis/build/builds/$(Build.BuildId)/artifacts?artifactName=packages&api-version=5.1"
|
||||
$buildPipeline= Invoke-RestMethod -Uri $url -Headers @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" } -Method Get
|
||||
$ArtifactDownloadURL= $buildPipeline.resource.downloadUrl
|
||||
$ArtifactDownloadURL = $ArtifactDownloadURL -replace "format=zip","format=file&subPath=%2Fautorest-modelerfour-$(artver).tgz"
|
||||
echo "Raw Artifact Url: '$ArtifactDownloadURL'"
|
||||
$downurl = (iwr "http://tinyurl.com/api-create.php?url=$( [System.Web.HttpUtility]::UrlEncode($ArtifactDownloadURL))" ).Content
|
||||
echo "##vso[task.setvariable variable=DOWNURL]$downurl"
|
||||
- task: PublishPipelineArtifact@1
|
||||
inputs:
|
||||
targetPath: "$(Build.SourcesDirectory)/common/temp/artifacts/packages/autorest-modelerfour-$(artver).tgz"
|
||||
artifact: "packages"
|
||||
publishLocation: "pipeline"
|
||||
|
||||
- task: GitHubComment@0
|
||||
inputs:
|
||||
gitHubConnection: 'Azure'
|
||||
repositoryName: '$(Build.Repository.Name)'
|
||||
comment: |
|
||||
You may test this build by running `autorest --reset` and then either:
|
||||
<hr>
|
||||
- pwsh: |
|
||||
$url = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$env:SYSTEM_TEAMPROJECTID/_apis/build/builds/$(Build.BuildId)/artifacts?artifactName=packages&api-version=5.1"
|
||||
$buildPipeline= Invoke-RestMethod -Uri $url -Headers @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" } -Method Get
|
||||
$ArtifactDownloadURL= $buildPipeline.resource.downloadUrl
|
||||
$ArtifactDownloadURL = $ArtifactDownloadURL -replace "format=zip","format=file&subPath=%2Fautorest-modelerfour-$(artver).tgz"
|
||||
echo "Raw Artifact Url: '$ArtifactDownloadURL'"
|
||||
$downurl = (iwr "http://tinyurl.com/api-create.php?url=$( [System.Web.HttpUtility]::UrlEncode($ArtifactDownloadURL))" ).Content
|
||||
echo "##vso[task.setvariable variable=DOWNURL]$downurl"
|
||||
|
||||
add `--use:` to the command line:
|
||||
|
||||
`autorest --use:$(DOWNURL) ` ...
|
||||
<hr>
|
||||
- task: GitHubComment@0
|
||||
inputs:
|
||||
gitHubConnection: "Azure"
|
||||
repositoryName: "$(Build.Repository.Name)"
|
||||
comment: |
|
||||
You may test this build by running `autorest --reset` and then either:
|
||||
<hr>
|
||||
|
||||
or use the following in your autorest configuration:
|
||||
|
||||
``` yaml
|
||||
use-extension:
|
||||
"@autorest/modelerfour": "$(DOWNURL)"
|
||||
```
|
||||
<hr>
|
||||
add `--use:` to the command line:
|
||||
|
||||
If this build is good for you, give this comment a thumbs up. (👍)
|
||||
`autorest --use:$(DOWNURL) ` ...
|
||||
<hr>
|
||||
|
||||
And you should run `autorest --reset` again once you're finished testing to remove it.
|
||||
or use the following in your autorest configuration:
|
||||
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
versionSpec: '3.x'
|
||||
``` yaml
|
||||
use-extension:
|
||||
"@autorest/modelerfour": "$(DOWNURL)"
|
||||
```
|
||||
<hr>
|
||||
|
||||
- script: npm install -g @autorest/compare@~0.3.0
|
||||
displayName: Install autorest-compare
|
||||
If this build is good for you, give this comment a thumbs up. (👍)
|
||||
|
||||
- script: autorest-compare --compare:.scripts/regression-compare.yaml --language:python
|
||||
displayName: Regression Test - @autorest/python
|
||||
And you should run `autorest --reset` again once you're finished testing to remove it.
|
||||
|
||||
- script: autorest-compare --compare:.scripts/regression-compare.yaml --language:typescript
|
||||
displayName: Regression Test - @autorest/typescript
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
versionSpec: "3.x"
|
||||
|
||||
- script: npm install -g @autorest/compare@~0.3.0
|
||||
displayName: Install autorest-compare
|
||||
|
||||
- script: autorest-compare --compare:.scripts/regression-compare.yaml --language:python
|
||||
displayName: Regression Test - @autorest/python
|
||||
|
||||
- script: autorest-compare --compare:.scripts/regression-compare.yaml --language:typescript
|
||||
displayName: Regression Test - @autorest/typescript
|
||||
|
|
|
@ -1,25 +1,23 @@
|
|||
var cp = require('child_process');
|
||||
var cp = require("child_process");
|
||||
|
||||
require('./for-each').forEachProject((packageName, projectFolder, project) => {
|
||||
require("./for-each").forEachProject((packageName, projectFolder, project) => {
|
||||
if (project.scripts.watch) {
|
||||
console.log(`npm run watch {cwd: ${__dirname}/../${projectFolder}}`);
|
||||
const proc = cp.spawn('npm', ['run', 'watch'], { cwd: projectFolder, shell: true, stdio: "inherit" });
|
||||
const proc = cp.spawn("npm", ["run", "watch"], { cwd: projectFolder, shell: true, stdio: "inherit" });
|
||||
proc.on("error", (c, s) => {
|
||||
console.log(packageName);
|
||||
console.error(c);
|
||||
console.error(s);
|
||||
});
|
||||
proc.on('exit', (c, s) => {
|
||||
proc.on("exit", (c, s) => {
|
||||
console.log(packageName);
|
||||
console.error(c);
|
||||
console.error(s);
|
||||
});
|
||||
proc.on('message', (c, s) => {
|
||||
proc.on("message", (c, s) => {
|
||||
console.log(packageName);
|
||||
console.error(c);
|
||||
console.error(s);
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
# configure plugins first
|
||||
parser: "@typescript-eslint/parser"
|
||||
plugins:
|
||||
- "@typescript-eslint"
|
||||
- "@typescript-eslint"
|
||||
|
||||
# then inherit the common settings
|
||||
extends:
|
||||
- "../.default-eslintrc.yaml"
|
||||
- "../.default-eslintrc.yaml"
|
||||
|
|
|
@ -1,46 +1,70 @@
|
|||
import { CodeModel, Schema, ObjectSchema, isObjectSchema, SchemaType, Property, ParameterLocation, Operation, Parameter, VirtualParameter, getAllProperties, ImplementationLocation, DictionarySchema } from '@azure-tools/codemodel';
|
||||
import { Session } from '@azure-tools/autorest-extension-base';
|
||||
import { values, items, length, Dictionary, refCount, clone } from '@azure-tools/linq';
|
||||
import {
|
||||
CodeModel,
|
||||
Schema,
|
||||
ObjectSchema,
|
||||
isObjectSchema,
|
||||
SchemaType,
|
||||
Property,
|
||||
ParameterLocation,
|
||||
Operation,
|
||||
Parameter,
|
||||
VirtualParameter,
|
||||
getAllProperties,
|
||||
ImplementationLocation,
|
||||
DictionarySchema,
|
||||
} from "@azure-tools/codemodel";
|
||||
import { Session } from "@azure-tools/autorest-extension-base";
|
||||
import { values, items, length, Dictionary, refCount, clone } from "@azure-tools/linq";
|
||||
|
||||
export class Checker {
|
||||
codeModel: CodeModel
|
||||
codeModel: CodeModel;
|
||||
options: Dictionary<any> = {};
|
||||
|
||||
constructor(protected session: Session<CodeModel>) {
|
||||
this.codeModel = session.model;// shadow(session.model, filename);
|
||||
this.codeModel = session.model; // shadow(session.model, filename);
|
||||
}
|
||||
|
||||
async init() {
|
||||
// get our configuration for this run.
|
||||
this.options = await this.session.getValue('modelerfour', {});
|
||||
this.options = await this.session.getValue("modelerfour", {});
|
||||
return this;
|
||||
}
|
||||
|
||||
checkOperationGroups() {
|
||||
for (const dupe of values(this.codeModel.operationGroups).select(each => each.language.default.name).duplicates()) {
|
||||
for (const dupe of values(this.codeModel.operationGroups)
|
||||
.select((each) => each.language.default.name)
|
||||
.duplicates()) {
|
||||
this.session.error(`Duplicate Operation group '${dupe}' detected .`, []);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
checkOperations() {
|
||||
for (const group of this.codeModel.operationGroups) {
|
||||
for (const dupe of values(group.operations).select(each => each.language.default.name).duplicates()) {
|
||||
for (const dupe of values(group.operations)
|
||||
.select((each) => each.language.default.name)
|
||||
.duplicates()) {
|
||||
this.session.error(`Duplicate Operation '${dupe}' detected.`, []);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkSchemas() {
|
||||
const allSchemas = values(<Dictionary<Schema[]>><any>this.codeModel.schemas).selectMany(schemas => Array.isArray(schemas) ? values(schemas) : []).toArray();
|
||||
const allSchemas = values(<Dictionary<Schema[]>>(<any>this.codeModel.schemas))
|
||||
.selectMany((schemas) => (Array.isArray(schemas) ? values(schemas) : []))
|
||||
.toArray();
|
||||
|
||||
for (const each of values(allSchemas).where(each => !each.language.default.name)) {
|
||||
for (const each of values(allSchemas).where((each) => !each.language.default.name)) {
|
||||
this.session.warning(`Schema Missing Name '${JSON.stringify(each)}'.`, []);
|
||||
}
|
||||
|
||||
const types = values(<Schema[]>this.codeModel.schemas.objects).concat(values(this.codeModel.schemas.groups)).concat(values(this.codeModel.schemas.choices)).concat(values(this.codeModel.schemas.sealedChoices)).toArray()
|
||||
for (const dupe of values(types).duplicates(each => each.language.default.name)) {
|
||||
const types = values(<Schema[]>this.codeModel.schemas.objects)
|
||||
.concat(values(this.codeModel.schemas.groups))
|
||||
.concat(values(this.codeModel.schemas.choices))
|
||||
.concat(values(this.codeModel.schemas.sealedChoices))
|
||||
.toArray();
|
||||
for (const dupe of values(types).duplicates((each) => each.language.default.name)) {
|
||||
this.session.error(`Duplicate object schemas with '${dupe.language.default.name}' name detected.`, []);
|
||||
};
|
||||
}
|
||||
|
||||
/* for (const dupe of values(this.codeModel.schemas.numbers).select(each => each.type).duplicates()) {
|
||||
this.session.error(`Duplicate '${dupe}' detected.`, []);
|
||||
|
@ -48,7 +72,7 @@ export class Checker {
|
|||
}
|
||||
|
||||
process() {
|
||||
if (this.options['additional-checks'] !== false) {
|
||||
if (this.options["additional-checks"] !== false) {
|
||||
this.checkOperationGroups();
|
||||
|
||||
this.checkOperations();
|
||||
|
@ -57,4 +81,4 @@ export class Checker {
|
|||
}
|
||||
return this.codeModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,17 +3,17 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { serialize } from '@azure-tools/codegen';
|
||||
import { Host, startSession } from '@azure-tools/autorest-extension-base';
|
||||
import { codeModelSchema, CodeModel } from '@azure-tools/codemodel';
|
||||
import { Checker } from './checker';
|
||||
import { serialize } from "@azure-tools/codegen";
|
||||
import { Host, startSession } from "@azure-tools/autorest-extension-base";
|
||||
import { codeModelSchema, CodeModel } from "@azure-tools/codemodel";
|
||||
import { Checker } from "./checker";
|
||||
|
||||
export async function processRequest(host: Host) {
|
||||
const debug = await host.GetValue('debug') || false;
|
||||
const debug = (await host.GetValue("debug")) || false;
|
||||
|
||||
try {
|
||||
const session = await startSession<CodeModel>(host, {}, codeModelSchema);
|
||||
const options = <any>await session.getValue('modelerfour', {});
|
||||
const options = <any>await session.getValue("modelerfour", {});
|
||||
|
||||
// process
|
||||
const plugin = await new Checker(session).init();
|
||||
|
@ -22,19 +22,18 @@ export async function processRequest(host: Host) {
|
|||
const result = plugin.process();
|
||||
|
||||
// throw on errors.
|
||||
if (!await session.getValue('no-errors', false)) {
|
||||
if (!(await session.getValue("no-errors", false))) {
|
||||
session.checkpoint();
|
||||
}
|
||||
|
||||
// output the model to the pipeline
|
||||
if (options['emit-yaml-tags'] !== false) {
|
||||
host.WriteFile('code-model-v4.yaml', serialize(result, codeModelSchema), undefined, 'code-model-v4');
|
||||
if (options["emit-yaml-tags"] !== false) {
|
||||
host.WriteFile("code-model-v4.yaml", serialize(result, codeModelSchema), undefined, "code-model-v4");
|
||||
}
|
||||
|
||||
if (options['emit-yaml-tags'] !== true) {
|
||||
host.WriteFile('code-model-v4-no-tags.yaml', serialize(result), undefined, 'code-model-v4-no-tags');
|
||||
if (options["emit-yaml-tags"] !== true) {
|
||||
host.WriteFile("code-model-v4-no-tags.yaml", serialize(result), undefined, "code-model-v4-no-tags");
|
||||
}
|
||||
|
||||
} catch (E) {
|
||||
if (debug) {
|
||||
console.error(`${__filename} - FAILURE ${JSON.stringify(E)} ${E.stack}`);
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
#!/usr/bin/env node
|
||||
// load modules from static linker filesystem.
|
||||
try {
|
||||
if (process.argv.indexOf('--no-static-loader') === -1 && process.env['no-static-loader'] === undefined && require('fs').existsSync(`${__dirname}/../dist/static-loader.js`)) {
|
||||
if (
|
||||
process.argv.indexOf("--no-static-loader") === -1 &&
|
||||
process.env["no-static-loader"] === undefined &&
|
||||
require("fs").existsSync(`${__dirname}/../dist/static-loader.js`)
|
||||
) {
|
||||
require(`${__dirname}/../dist/static-loader.js`).load(`${__dirname}/../dist/static_modules.fs`);
|
||||
}
|
||||
require(`${__dirname}/../dist/main.js`);
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
import { CodeModel } from '@azure-tools/codemodel';
|
||||
import { Session } from '@azure-tools/autorest-extension-base';
|
||||
import { CodeModel } from "@azure-tools/codemodel";
|
||||
import { Session } from "@azure-tools/autorest-extension-base";
|
||||
|
||||
export class Example {
|
||||
codeModel: CodeModel
|
||||
codeModel: CodeModel;
|
||||
|
||||
constructor(protected session: Session<CodeModel>) {
|
||||
this.codeModel = session.model;// shadow(session.model, filename);
|
||||
|
||||
this.codeModel = session.model; // shadow(session.model, filename);
|
||||
}
|
||||
|
||||
process() {
|
||||
return this.codeModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,14 +3,13 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { serialize } from '@azure-tools/codegen';
|
||||
import { Host, startSession } from '@azure-tools/autorest-extension-base';
|
||||
import { codeModelSchema, CodeModel } from '@azure-tools/codemodel';
|
||||
import { Example } from './example';
|
||||
|
||||
import { serialize } from "@azure-tools/codegen";
|
||||
import { Host, startSession } from "@azure-tools/autorest-extension-base";
|
||||
import { codeModelSchema, CodeModel } from "@azure-tools/codemodel";
|
||||
import { Example } from "./example";
|
||||
|
||||
export async function processRequest(host: Host) {
|
||||
const debug = await host.GetValue('debug') || false;
|
||||
const debug = (await host.GetValue("debug")) || false;
|
||||
|
||||
try {
|
||||
const session = await startSession<CodeModel>(host, {}, codeModelSchema);
|
||||
|
@ -22,9 +21,8 @@ export async function processRequest(host: Host) {
|
|||
const result = plugin.process();
|
||||
|
||||
// output the model to the pipeline
|
||||
host.WriteFile('code-model-v4.yaml', serialize(result, codeModelSchema), undefined, 'code-model-v4');
|
||||
host.WriteFile('code-model-v4-no-tags.yaml', serialize(result), undefined, 'code-model-v4-no-tags');
|
||||
|
||||
host.WriteFile("code-model-v4.yaml", serialize(result, codeModelSchema), undefined, "code-model-v4");
|
||||
host.WriteFile("code-model-v4-no-tags.yaml", serialize(result), undefined, "code-model-v4-no-tags");
|
||||
} catch (E) {
|
||||
if (debug) {
|
||||
console.error(`${__filename} - FAILURE ${JSON.stringify(E)} ${E.stack}`);
|
||||
|
|
|
@ -1,31 +1,49 @@
|
|||
import { CodeModel, Schema, ObjectSchema, isObjectSchema, SchemaType, Property, ParameterLocation, Operation, Parameter, VirtualParameter, getAllProperties, ImplementationLocation, Request } from '@azure-tools/codemodel';
|
||||
import { Session } from '@azure-tools/autorest-extension-base';
|
||||
import { values, items, length, Dictionary, refCount, clone } from '@azure-tools/linq';
|
||||
import {
|
||||
CodeModel,
|
||||
Schema,
|
||||
ObjectSchema,
|
||||
isObjectSchema,
|
||||
SchemaType,
|
||||
Property,
|
||||
ParameterLocation,
|
||||
Operation,
|
||||
Parameter,
|
||||
VirtualParameter,
|
||||
getAllProperties,
|
||||
ImplementationLocation,
|
||||
Request,
|
||||
} from "@azure-tools/codemodel";
|
||||
import { Session } from "@azure-tools/autorest-extension-base";
|
||||
import { values, items, length, Dictionary, refCount, clone } from "@azure-tools/linq";
|
||||
|
||||
const xmsThreshold = 'x-ms-payload-flattening-threshold';
|
||||
const xmsFlatten = 'x-ms-client-flatten';
|
||||
const isCurrentlyFlattening = 'x-ms-flattening';
|
||||
const hasBeenFlattened = 'x-ms-flattened';
|
||||
const xmsThreshold = "x-ms-payload-flattening-threshold";
|
||||
const xmsFlatten = "x-ms-client-flatten";
|
||||
const isCurrentlyFlattening = "x-ms-flattening";
|
||||
const hasBeenFlattened = "x-ms-flattened";
|
||||
|
||||
export class Flattener {
|
||||
codeModel: CodeModel
|
||||
codeModel: CodeModel;
|
||||
options: Dictionary<any> = {};
|
||||
threshold: number = 0;
|
||||
recursePayload: boolean = false;
|
||||
|
||||
constructor(protected session: Session<CodeModel>) {
|
||||
this.codeModel = session.model;// shadow(session.model, filename);
|
||||
this.codeModel = session.model; // shadow(session.model, filename);
|
||||
}
|
||||
|
||||
async init() {
|
||||
// get our configuration for this run.
|
||||
this.options = await this.session.getValue('modelerfour', {});
|
||||
this.threshold = await this.session.getValue('payload-flattening-threshold', 0);
|
||||
this.recursePayload = await this.session.getValue('recursive-payload-flattening', false);
|
||||
this.options = await this.session.getValue("modelerfour", {});
|
||||
this.threshold = await this.session.getValue("payload-flattening-threshold", 0);
|
||||
this.recursePayload = await this.session.getValue("recursive-payload-flattening", false);
|
||||
return this;
|
||||
}
|
||||
|
||||
*getFlattenedParameters(parameter: Parameter, property: Property, path: Array<Property> = []): Iterable<VirtualParameter> {
|
||||
*getFlattenedParameters(
|
||||
parameter: Parameter,
|
||||
property: Property,
|
||||
path: Array<Property> = [],
|
||||
): Iterable<VirtualParameter> {
|
||||
if (property.readOnly) {
|
||||
// skip read-only properties
|
||||
return;
|
||||
|
@ -35,29 +53,34 @@ export class Flattener {
|
|||
yield* this.getFlattenedParameters(parameter, child, [...path, property]);
|
||||
}
|
||||
} else {
|
||||
const vp = new VirtualParameter(property.language.default.name, property.language.default.description, property.schema, {
|
||||
...property,
|
||||
implementation: ImplementationLocation.Method,
|
||||
originalParameter: parameter,
|
||||
targetProperty: property,
|
||||
pathToProperty: path
|
||||
});
|
||||
const vp = new VirtualParameter(
|
||||
property.language.default.name,
|
||||
property.language.default.description,
|
||||
property.schema,
|
||||
{
|
||||
...property,
|
||||
implementation: ImplementationLocation.Method,
|
||||
originalParameter: parameter,
|
||||
targetProperty: property,
|
||||
pathToProperty: path,
|
||||
},
|
||||
);
|
||||
delete (<any>vp).serializedName;
|
||||
delete (<any>vp).readOnly;
|
||||
delete (<any>vp).isDiscriminator;
|
||||
delete (<any>vp).flattenedNames;
|
||||
|
||||
|
||||
// if the parameter has "x-ms-parameter-grouping" extension, (and this is a top level parameter) then we should copy that to the vp.
|
||||
if (path.length === 0 && parameter.extensions?.['x-ms-parameter-grouping']) {
|
||||
(vp.extensions = vp.extensions || {})['x-ms-parameter-grouping'] = parameter.extensions?.['x-ms-parameter-grouping'];
|
||||
if (path.length === 0 && parameter.extensions?.["x-ms-parameter-grouping"]) {
|
||||
(vp.extensions = vp.extensions || {})["x-ms-parameter-grouping"] =
|
||||
parameter.extensions?.["x-ms-parameter-grouping"];
|
||||
}
|
||||
|
||||
yield vp;
|
||||
}
|
||||
// ·
|
||||
}
|
||||
/**
|
||||
/**
|
||||
* This flattens an request's parameters (ie, takes the parameters from an operation and if they are objects will attempt to create inline versions of them)
|
||||
*/
|
||||
flattenPayload(request: Request, parameter: Parameter, schema: ObjectSchema) {
|
||||
|
@ -73,7 +96,6 @@ export class Flattener {
|
|||
request.parameters?.push(vp);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,14 +112,16 @@ export class Flattener {
|
|||
|
||||
if (state === true) {
|
||||
// in progress.
|
||||
throw new Error(`Circular reference encountered during processing of x-ms-client flatten ('${schema.language.default.name}')`);
|
||||
throw new Error(
|
||||
`Circular reference encountered during processing of x-ms-client flatten ('${schema.language.default.name}')`,
|
||||
);
|
||||
}
|
||||
|
||||
// hasn't started yet.
|
||||
schema.extensions = schema.extensions || {};
|
||||
schema.extensions[isCurrentlyFlattening] = true;
|
||||
|
||||
// ensure that parent schemas are done first -- this should remove
|
||||
// ensure that parent schemas are done first -- this should remove
|
||||
// the problem when the order isn't just right.
|
||||
for (const parent of values(schema.parents?.immediate)) {
|
||||
if (isObjectSchema(parent)) {
|
||||
|
@ -107,7 +131,6 @@ export class Flattener {
|
|||
|
||||
if (schema.properties) {
|
||||
for (const { key: index, value: property } of items(schema.properties).toArray().reverse()) {
|
||||
|
||||
if (isObjectSchema(property.schema) && property.extensions?.[xmsFlatten]) {
|
||||
// first, ensure tha the child is pre-flattened
|
||||
this.flattenSchema(property.schema);
|
||||
|
@ -115,20 +138,30 @@ export class Flattener {
|
|||
// remove that property from the scheama
|
||||
schema.properties.splice(index, 1);
|
||||
|
||||
// copy all of the properties from the child into this
|
||||
// schema
|
||||
// copy all of the properties from the child into this
|
||||
// schema
|
||||
for (const childProperty of values(getAllProperties(property.schema))) {
|
||||
schema.addProperty(new Property(childProperty.language.default.name, childProperty.language.default.description, childProperty.schema, {
|
||||
...(<any>childProperty),
|
||||
flattenedNames: [property.serializedName, ...childProperty.flattenedNames ? childProperty.flattenedNames : [childProperty.serializedName]],
|
||||
required: property.required && childProperty.required
|
||||
}));
|
||||
schema.addProperty(
|
||||
new Property(
|
||||
childProperty.language.default.name,
|
||||
childProperty.language.default.description,
|
||||
childProperty.schema,
|
||||
{
|
||||
...(<any>childProperty),
|
||||
flattenedNames: [
|
||||
property.serializedName,
|
||||
...(childProperty.flattenedNames ? childProperty.flattenedNames : [childProperty.serializedName]),
|
||||
],
|
||||
required: property.required && childProperty.required,
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// remove the extension
|
||||
delete property.extensions[xmsFlatten];
|
||||
if (length(property.extensions) === 0) {
|
||||
delete property['extensions'];
|
||||
delete property["extensions"];
|
||||
}
|
||||
// and mark the child class as 'do-not-generate' ?
|
||||
(property.schema.extensions = property.schema.extensions || {})[hasBeenFlattened] = true;
|
||||
|
@ -143,18 +176,17 @@ export class Flattener {
|
|||
// support 'x-ms-payload-flattening-threshold' per-operation
|
||||
// support '--payload-flattening-threshold:X' global setting
|
||||
|
||||
if (this.options['flatten-models'] === true) {
|
||||
|
||||
if (this.options["flatten-models"] === true) {
|
||||
for (const schema of values(this.codeModel.schemas.objects)) {
|
||||
this.flattenSchema(schema);
|
||||
}
|
||||
|
||||
if (!this.options['keep-unused-flattened-models']) {
|
||||
if (!this.options["keep-unused-flattened-models"]) {
|
||||
let dirty = false;
|
||||
do {
|
||||
// reset on every pass
|
||||
dirty = false;
|
||||
// remove unreferenced models
|
||||
// remove unreferenced models
|
||||
for (const { key, value: schema } of items(this.codeModel.schemas.objects).toArray()) {
|
||||
// only remove unreferenced models that have been flattened.
|
||||
if (!schema.extensions?.[hasBeenFlattened]) {
|
||||
|
@ -162,12 +194,12 @@ export class Flattener {
|
|||
}
|
||||
|
||||
if (schema.discriminatorValue || schema.discriminator) {
|
||||
// it's polymorphic -- I don't think we can remove this
|
||||
// it's polymorphic -- I don't think we can remove this
|
||||
continue;
|
||||
}
|
||||
|
||||
if (schema.children?.all || schema.parents?.all) {
|
||||
// it's got either a parent or child schema.
|
||||
// it's got either a parent or child schema.
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -180,34 +212,33 @@ export class Flattener {
|
|||
} while (dirty);
|
||||
}
|
||||
|
||||
|
||||
for (const schema of values(this.codeModel.schemas.objects)) {
|
||||
if (schema.extensions) {
|
||||
delete schema.extensions[isCurrentlyFlattening];
|
||||
// don't want this until I have removed the unreferenced models.
|
||||
// delete schema.extensions[hasBeenFlattened];
|
||||
if (length(schema.extensions) === 0) {
|
||||
delete schema['extensions'];
|
||||
delete schema["extensions"];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.options['flatten-payloads'] === true) {
|
||||
if (this.options["flatten-payloads"] === true) {
|
||||
/**
|
||||
* BodyParameter Payload Flattening
|
||||
*
|
||||
* BodyParameter Payload Flattening
|
||||
*
|
||||
* A body parameter is flattened (one level) when:
|
||||
*
|
||||
*
|
||||
* - the body parameter schema is an object
|
||||
* - the body parameter schema is not polymorphic (is this true?)
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* and one of:
|
||||
* - the body parameter has x-ms-client-flatten: true
|
||||
* - the operation has x-ms-payload-flattening-threshold greater than zero and the property count in the body parameter is lessthan or equal to that.
|
||||
* - the global configuration option payload-flattening-threshold is greater than zero and the property count in the body parameter is lessthan or equal to that
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
// flatten payloads
|
||||
|
@ -216,12 +247,15 @@ export class Flattener {
|
|||
for (const operation of group.operations) {
|
||||
// when there are multiple requests in an operation
|
||||
// and the generator asks not to flatten them
|
||||
if (length(operation.requests) > 1 && this.options['multiple-request-parameter-flattening'] === false) {
|
||||
if (length(operation.requests) > 1 && this.options["multiple-request-parameter-flattening"] === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const request of values(operation.requests)) {
|
||||
const body = values(request.parameters).first(p => p.protocol.http?.in === ParameterLocation.Body && p.implementation === ImplementationLocation.Method);
|
||||
const body = values(request.parameters).first(
|
||||
(p) =>
|
||||
p.protocol.http?.in === ParameterLocation.Body && p.implementation === ImplementationLocation.Method,
|
||||
);
|
||||
|
||||
if (body && isObjectSchema(body.schema)) {
|
||||
const schema = <ObjectSchema>body.schema;
|
||||
|
@ -236,12 +270,16 @@ export class Flattener {
|
|||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (!flattenOperationPayload) {
|
||||
const threshold = <number>operation.extensions?.[xmsThreshold] ?? this.threshold;
|
||||
if (threshold > 0) {
|
||||
// get the count of the (non-readonly) properties in the schema
|
||||
flattenOperationPayload = length(values(getAllProperties(schema)).where(property => property.readOnly !== true && property.schema.type !== SchemaType.Constant)) <= threshold;
|
||||
flattenOperationPayload =
|
||||
length(
|
||||
values(getAllProperties(schema)).where(
|
||||
(property) => property.readOnly !== true && property.schema.type !== SchemaType.Constant,
|
||||
),
|
||||
) <= threshold;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,18 +3,17 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { serialize } from '@azure-tools/codegen';
|
||||
import { Host, startSession } from '@azure-tools/autorest-extension-base';
|
||||
import { codeModelSchema, CodeModel } from '@azure-tools/codemodel';
|
||||
import { Flattener } from './flattener';
|
||||
|
||||
import { serialize } from "@azure-tools/codegen";
|
||||
import { Host, startSession } from "@azure-tools/autorest-extension-base";
|
||||
import { codeModelSchema, CodeModel } from "@azure-tools/codemodel";
|
||||
import { Flattener } from "./flattener";
|
||||
|
||||
export async function processRequest(host: Host) {
|
||||
const debug = await host.GetValue('debug') || false;
|
||||
const debug = (await host.GetValue("debug")) || false;
|
||||
|
||||
try {
|
||||
const session = await startSession<CodeModel>(host, {}, codeModelSchema);
|
||||
const options = <any>await session.getValue('modelerfour', {});
|
||||
const options = <any>await session.getValue("modelerfour", {});
|
||||
|
||||
// process
|
||||
const plugin = await new Flattener(session).init();
|
||||
|
@ -23,14 +22,13 @@ export async function processRequest(host: Host) {
|
|||
const result = plugin.process();
|
||||
|
||||
// output the model to the pipeline
|
||||
if (options['emit-yaml-tags'] !== false) {
|
||||
host.WriteFile('code-model-v4.yaml', serialize(result, codeModelSchema), undefined, 'code-model-v4');
|
||||
if (options["emit-yaml-tags"] !== false) {
|
||||
host.WriteFile("code-model-v4.yaml", serialize(result, codeModelSchema), undefined, "code-model-v4");
|
||||
}
|
||||
|
||||
if (options['emit-yaml-tags'] !== true) {
|
||||
host.WriteFile('code-model-v4-no-tags.yaml', serialize(result), undefined, 'code-model-v4-no-tags');
|
||||
if (options["emit-yaml-tags"] !== true) {
|
||||
host.WriteFile("code-model-v4-no-tags.yaml", serialize(result), undefined, "code-model-v4-no-tags");
|
||||
}
|
||||
|
||||
} catch (E) {
|
||||
if (debug) {
|
||||
console.error(`${__filename} - FAILURE ${JSON.stringify(E)} ${E.stack}`);
|
||||
|
|
|
@ -1,32 +1,45 @@
|
|||
import { CodeModel, Schema, GroupSchema, isObjectSchema, SchemaType, GroupProperty, ParameterLocation, Operation, Parameter, VirtualParameter, getAllProperties, ImplementationLocation, OperationGroup, Request, SchemaContext } from '@azure-tools/codemodel';
|
||||
import { Session } from '@azure-tools/autorest-extension-base';
|
||||
import { values, items, length, Dictionary, refCount, clone } from '@azure-tools/linq';
|
||||
import { pascalCase, camelCase } from '@azure-tools/codegen';
|
||||
|
||||
const mergeReponseHeaders = 'merge-response-headers';
|
||||
const enableParameterGrouping = 'group-parameters';
|
||||
const xmsParameterGrouping = 'x-ms-parameter-grouping'
|
||||
import {
|
||||
CodeModel,
|
||||
Schema,
|
||||
GroupSchema,
|
||||
isObjectSchema,
|
||||
SchemaType,
|
||||
GroupProperty,
|
||||
ParameterLocation,
|
||||
Operation,
|
||||
Parameter,
|
||||
VirtualParameter,
|
||||
getAllProperties,
|
||||
ImplementationLocation,
|
||||
OperationGroup,
|
||||
Request,
|
||||
SchemaContext,
|
||||
} from "@azure-tools/codemodel";
|
||||
import { Session } from "@azure-tools/autorest-extension-base";
|
||||
import { values, items, length, Dictionary, refCount, clone } from "@azure-tools/linq";
|
||||
import { pascalCase, camelCase } from "@azure-tools/codegen";
|
||||
|
||||
const mergeReponseHeaders = "merge-response-headers";
|
||||
const enableParameterGrouping = "group-parameters";
|
||||
const xmsParameterGrouping = "x-ms-parameter-grouping";
|
||||
|
||||
export class Grouper {
|
||||
codeModel: CodeModel
|
||||
codeModel: CodeModel;
|
||||
options: Dictionary<any> = {};
|
||||
groups: Dictionary<GroupSchema> = {};
|
||||
|
||||
constructor(protected session: Session<CodeModel>) {
|
||||
this.codeModel = session.model;// shadow(session.model, filename);
|
||||
this.codeModel = session.model; // shadow(session.model, filename);
|
||||
}
|
||||
|
||||
async init() {
|
||||
// get our configuration for this run.
|
||||
this.options = await this.session.getValue('modelerfour', {});
|
||||
this.options = await this.session.getValue("modelerfour", {});
|
||||
return this;
|
||||
}
|
||||
|
||||
process() {
|
||||
|
||||
if (this.options[enableParameterGrouping] === true) {
|
||||
|
||||
for (const group of this.codeModel.operationGroups) {
|
||||
for (const operation of group.operations) {
|
||||
for (const request of values(operation.requests)) {
|
||||
|
@ -53,17 +66,26 @@ export class Grouper {
|
|||
|
||||
proposedName(group: OperationGroup, operation: Operation, parameter: Parameter) {
|
||||
const xmsp = parameter.extensions?.[xmsParameterGrouping];
|
||||
if (xmsp.name && typeof xmsp.name === 'string') {
|
||||
if (xmsp.name && typeof xmsp.name === "string") {
|
||||
return xmsp.name;
|
||||
}
|
||||
|
||||
const postfix = xmsp.postfix && typeof xmsp.postfix === 'string' ? xmsp.postfix : 'Parameters';
|
||||
const postfix = xmsp.postfix && typeof xmsp.postfix === "string" ? xmsp.postfix : "Parameters";
|
||||
|
||||
return pascalCase(`${group.$key} ${operation.language.default.name} ${postfix}`);
|
||||
}
|
||||
|
||||
processParameterGroup(group: OperationGroup, operation: Operation, request: Request) {
|
||||
const grouped = [...values(operation.parameters).concat(values(request.parameters)).where(parameter => parameter.extensions?.[xmsParameterGrouping] && parameter.schema.type !== SchemaType.Constant && parameter.implementation !== ImplementationLocation.Client)];
|
||||
const grouped = [
|
||||
...values(operation.parameters)
|
||||
.concat(values(request.parameters))
|
||||
.where(
|
||||
(parameter) =>
|
||||
parameter.extensions?.[xmsParameterGrouping] &&
|
||||
parameter.schema.type !== SchemaType.Constant &&
|
||||
parameter.implementation !== ImplementationLocation.Client,
|
||||
),
|
||||
];
|
||||
|
||||
if (grouped.length > 0) {
|
||||
// create a parameter group object schema for the selected parameters.
|
||||
|
@ -75,33 +97,45 @@ export class Grouper {
|
|||
// see if we've started the schema for this yet.
|
||||
if (!this.groups[groupName]) {
|
||||
// create a new object schema for this group
|
||||
const schema = new GroupSchema(groupName, 'Parameter group');
|
||||
const schema = new GroupSchema(groupName, "Parameter group");
|
||||
schema.usage = [SchemaContext.Input];
|
||||
this.groups[groupName] = schema;
|
||||
this.codeModel.schemas.add(schema);
|
||||
}
|
||||
const schema = this.groups[groupName];
|
||||
|
||||
// see if the group has this parameter.
|
||||
const existingProperty = values(schema.properties).first(each => each.language.default.name === parameter.language.default.name);
|
||||
// see if the group has this parameter.
|
||||
const existingProperty = values(schema.properties).first(
|
||||
(each) => each.language.default.name === parameter.language.default.name,
|
||||
);
|
||||
if (existingProperty) {
|
||||
// we have a property by this name one already
|
||||
// mark the groupproperty with this parameter (so we can find it if needed)
|
||||
existingProperty.originalParameter.push(parameter);
|
||||
} else {
|
||||
// create a property for this parameter.
|
||||
const gp = new GroupProperty(parameter.language.default.name, parameter.language.default.description, parameter.schema, {
|
||||
required: parameter.required,
|
||||
});
|
||||
const gp = new GroupProperty(
|
||||
parameter.language.default.name,
|
||||
parameter.language.default.description,
|
||||
parameter.schema,
|
||||
{
|
||||
required: parameter.required,
|
||||
},
|
||||
);
|
||||
gp.originalParameter.push(parameter);
|
||||
schema.add(gp);
|
||||
}
|
||||
|
||||
// check if this groupSchema has been added as a parameter for this operation yet.
|
||||
if (!addedGroupedParameters.has(schema)) {
|
||||
addedGroupedParameters.set(schema, request.addParameter(new Parameter(camelCase(schema.language.default.name), schema.language.default.description, schema, {
|
||||
implementation: ImplementationLocation.Method,
|
||||
})));
|
||||
addedGroupedParameters.set(
|
||||
schema,
|
||||
request.addParameter(
|
||||
new Parameter(camelCase(schema.language.default.name), schema.language.default.description, schema, {
|
||||
implementation: ImplementationLocation.Method,
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// make sure that it's not optional if any parameter are not optional.
|
||||
|
@ -115,7 +149,7 @@ export class Grouper {
|
|||
if (parameter.extensions) {
|
||||
delete parameter.extensions[xmsParameterGrouping];
|
||||
if (length(parameter.extensions) === 0) {
|
||||
delete parameter['extensions'];
|
||||
delete parameter["extensions"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -123,6 +157,6 @@ export class Grouper {
|
|||
}
|
||||
|
||||
processResponseHeaders(operation: Operation) {
|
||||
throw new Error('Method not implemented.');
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,18 +3,17 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { serialize } from '@azure-tools/codegen';
|
||||
import { Host, startSession } from '@azure-tools/autorest-extension-base';
|
||||
import { codeModelSchema, CodeModel } from '@azure-tools/codemodel';
|
||||
import { Grouper } from './grouper';
|
||||
|
||||
import { serialize } from "@azure-tools/codegen";
|
||||
import { Host, startSession } from "@azure-tools/autorest-extension-base";
|
||||
import { codeModelSchema, CodeModel } from "@azure-tools/codemodel";
|
||||
import { Grouper } from "./grouper";
|
||||
|
||||
export async function processRequest(host: Host) {
|
||||
const debug = await host.GetValue('debug') || false;
|
||||
const debug = (await host.GetValue("debug")) || false;
|
||||
|
||||
try {
|
||||
const session = await startSession<CodeModel>(host, {}, codeModelSchema);
|
||||
const options = <any>await session.getValue('modelerfour', {});
|
||||
const options = <any>await session.getValue("modelerfour", {});
|
||||
|
||||
// process
|
||||
const plugin = await new Grouper(session).init();
|
||||
|
@ -23,14 +22,13 @@ export async function processRequest(host: Host) {
|
|||
const result = plugin.process();
|
||||
|
||||
// output the model to the pipeline
|
||||
if (options['emit-yaml-tags'] !== false) {
|
||||
host.WriteFile('code-model-v4.yaml', serialize(result, codeModelSchema), undefined, 'code-model-v4');
|
||||
if (options["emit-yaml-tags"] !== false) {
|
||||
host.WriteFile("code-model-v4.yaml", serialize(result, codeModelSchema), undefined, "code-model-v4");
|
||||
}
|
||||
|
||||
if (options['emit-yaml-tags'] !== true) {
|
||||
host.WriteFile('code-model-v4-no-tags.yaml', serialize(result), undefined, 'code-model-v4-no-tags');
|
||||
if (options["emit-yaml-tags"] !== true) {
|
||||
host.WriteFile("code-model-v4-no-tags.yaml", serialize(result), undefined, "code-model-v4-no-tags");
|
||||
}
|
||||
|
||||
} catch (E) {
|
||||
if (debug) {
|
||||
console.error(`${__filename} - FAILURE ${JSON.stringify(E)} ${E.stack}`);
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import { AutoRestExtension, } from '@azure-tools/autorest-extension-base';
|
||||
import { processRequest as modelerfour } from './modeler/plugin-modelerfour';
|
||||
import { processRequest as preNamer } from './prenamer/plugin-prenamer';
|
||||
import { processRequest as flattener } from './flattener/plugin-flattener';
|
||||
import { processRequest as grouper } from './grouper/plugin-grouper';
|
||||
import { processRequest as checker } from './checker/plugin-checker';
|
||||
import { processRequest as prechecker } from './quality-precheck/prechecker';
|
||||
import { AutoRestExtension } from "@azure-tools/autorest-extension-base";
|
||||
import { processRequest as modelerfour } from "./modeler/plugin-modelerfour";
|
||||
import { processRequest as preNamer } from "./prenamer/plugin-prenamer";
|
||||
import { processRequest as flattener } from "./flattener/plugin-flattener";
|
||||
import { processRequest as grouper } from "./grouper/plugin-grouper";
|
||||
import { processRequest as checker } from "./checker/plugin-checker";
|
||||
import { processRequest as prechecker } from "./quality-precheck/prechecker";
|
||||
|
||||
export async function initializePlugins(pluginHost: AutoRestExtension) {
|
||||
pluginHost.Add('prechecker', prechecker);
|
||||
pluginHost.Add('modelerfour', modelerfour);
|
||||
pluginHost.Add('grouper', grouper);
|
||||
pluginHost.Add('pre-namer', preNamer);
|
||||
pluginHost.Add('flattener', flattener);
|
||||
pluginHost.Add('checker', checker);
|
||||
pluginHost.Add("prechecker", prechecker);
|
||||
pluginHost.Add("modelerfour", modelerfour);
|
||||
pluginHost.Add("grouper", grouper);
|
||||
pluginHost.Add("pre-namer", preNamer);
|
||||
pluginHost.Add("flattener", flattener);
|
||||
pluginHost.Add("checker", checker);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
|
@ -21,4 +21,4 @@ async function main() {
|
|||
await pluginHost.Run();
|
||||
}
|
||||
|
||||
main();
|
||||
main();
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
import { Session } from '@azure-tools/autorest-extension-base';
|
||||
import * as OpenAPI from '@azure-tools/openapi';
|
||||
import { values, length, items, ToDictionary, Dictionary } from '@azure-tools/linq';
|
||||
import { ChoiceSchema, XmlSerlializationFormat, ExternalDocumentation, ApiVersion, Deprecation, ChoiceValue, SetType } from '@azure-tools/codemodel';
|
||||
import { StringFormat, JsonType, ParameterLocation } from '@azure-tools/openapi';
|
||||
import { getPascalIdentifier } from '@azure-tools/codegen';
|
||||
import { Session } from "@azure-tools/autorest-extension-base";
|
||||
import * as OpenAPI from "@azure-tools/openapi";
|
||||
import { values, length, items, ToDictionary, Dictionary } from "@azure-tools/linq";
|
||||
import {
|
||||
ChoiceSchema,
|
||||
XmlSerlializationFormat,
|
||||
ExternalDocumentation,
|
||||
ApiVersion,
|
||||
Deprecation,
|
||||
ChoiceValue,
|
||||
SetType,
|
||||
} from "@azure-tools/codemodel";
|
||||
import { StringFormat, JsonType, ParameterLocation } from "@azure-tools/openapi";
|
||||
import { getPascalIdentifier } from "@azure-tools/codegen";
|
||||
|
||||
export interface XMSEnum {
|
||||
modelAsString?: boolean;
|
||||
|
@ -11,67 +19,66 @@ export interface XMSEnum {
|
|||
name: string;
|
||||
}
|
||||
|
||||
|
||||
const removeKnownParameters = [
|
||||
'x-ms-metadata',
|
||||
'x-ms-enum',
|
||||
'x-ms-code-generation-settings',
|
||||
'x-ms-client-name',
|
||||
'x-ms-parameter-location',
|
||||
'x-ms-original',
|
||||
'x-ms-requestBody-name',
|
||||
'x-ms-requestBody-index',
|
||||
'x-ms-api-version',
|
||||
'x-ms-text'
|
||||
"x-ms-metadata",
|
||||
"x-ms-enum",
|
||||
"x-ms-code-generation-settings",
|
||||
"x-ms-client-name",
|
||||
"x-ms-parameter-location",
|
||||
"x-ms-original",
|
||||
"x-ms-requestBody-name",
|
||||
"x-ms-requestBody-index",
|
||||
"x-ms-api-version",
|
||||
"x-ms-text",
|
||||
];
|
||||
|
||||
// ref: https://www.w3schools.com/charsets/ref_html_ascii.asp
|
||||
const specialCharacterMapping: { [character: string]: string } = {
|
||||
'!': 'exclamation mark',
|
||||
'"': 'quotation mark',
|
||||
'#': 'number sign',
|
||||
'$': 'dollar sign',
|
||||
'%': 'percent sign',
|
||||
'&': 'ampersand',
|
||||
'\'': 'apostrophe',
|
||||
'(': 'left parenthesis',
|
||||
')': 'right parenthesis',
|
||||
'*': 'asterisk',
|
||||
'+': 'plus sign',
|
||||
',': 'comma',
|
||||
'-': 'hyphen',
|
||||
'.': 'period',
|
||||
'/': 'slash',
|
||||
':': 'colon',
|
||||
';': 'semicolon',
|
||||
'<': 'less-than',
|
||||
'=': 'equals-to',
|
||||
'>': 'greater-than',
|
||||
'?': 'question mark',
|
||||
'@': 'at sign',
|
||||
'[': 'left square bracket',
|
||||
'\\': 'backslash',
|
||||
']': 'right square bracket',
|
||||
'^': 'caret',
|
||||
'_': 'underscore',
|
||||
'`': 'grave accent',
|
||||
'{': 'left curly brace',
|
||||
'|': 'vertical bar',
|
||||
'}': 'right curly brace',
|
||||
'~': 'tilde'
|
||||
"!": "exclamation mark",
|
||||
'"': "quotation mark",
|
||||
"#": "number sign",
|
||||
"$": "dollar sign",
|
||||
"%": "percent sign",
|
||||
"&": "ampersand",
|
||||
"'": "apostrophe",
|
||||
"(": "left parenthesis",
|
||||
")": "right parenthesis",
|
||||
"*": "asterisk",
|
||||
"+": "plus sign",
|
||||
",": "comma",
|
||||
"-": "hyphen",
|
||||
".": "period",
|
||||
"/": "slash",
|
||||
":": "colon",
|
||||
";": "semicolon",
|
||||
"<": "less-than",
|
||||
"=": "equals-to",
|
||||
">": "greater-than",
|
||||
"?": "question mark",
|
||||
"@": "at sign",
|
||||
"[": "left square bracket",
|
||||
"\\": "backslash",
|
||||
"]": "right square bracket",
|
||||
"^": "caret",
|
||||
"_": "underscore",
|
||||
"`": "grave accent",
|
||||
"{": "left curly brace",
|
||||
"|": "vertical bar",
|
||||
"}": "right curly brace",
|
||||
"~": "tilde",
|
||||
};
|
||||
|
||||
const apiVersionParameterNames = [
|
||||
'api-version',
|
||||
'apiversion',
|
||||
'x-ms-api-version',
|
||||
'x-ms-version'
|
||||
];
|
||||
const apiVersionParameterNames = ["api-version", "apiversion", "x-ms-api-version", "x-ms-version"];
|
||||
|
||||
export function getValidEnumValueName(originalString: string): string {
|
||||
if (typeof originalString === 'string') {
|
||||
return !originalString.match(/[A-Za-z0-9]/g) ?
|
||||
getPascalIdentifier(originalString.split('').map(x => specialCharacterMapping[x]).join(' '))
|
||||
if (typeof originalString === "string") {
|
||||
return !originalString.match(/[A-Za-z0-9]/g)
|
||||
? getPascalIdentifier(
|
||||
originalString
|
||||
.split("")
|
||||
.map((x) => specialCharacterMapping[x])
|
||||
.join(" "),
|
||||
)
|
||||
: originalString;
|
||||
}
|
||||
return originalString;
|
||||
|
@ -79,11 +86,10 @@ export function getValidEnumValueName(originalString: string): string {
|
|||
|
||||
export class Interpretations {
|
||||
isTrue(value: any) {
|
||||
return (value === true || value === 'true' || value === 'True' || value === 'TRUE');
|
||||
return value === true || value === "true" || value === "True" || value === "TRUE";
|
||||
}
|
||||
|
||||
getConstantValue(schema: OpenAPI.Schema, value: any) {
|
||||
|
||||
switch (schema.type) {
|
||||
case JsonType.String:
|
||||
switch (schema.format) {
|
||||
|
@ -125,7 +131,7 @@ export class Interpretations {
|
|||
return value;
|
||||
|
||||
case StringFormat.Password:
|
||||
throw new Error('Constant values for String/Passwords should never be in input documents');
|
||||
throw new Error("Constant values for String/Passwords should never be in input documents");
|
||||
|
||||
case StringFormat.OData:
|
||||
return value;
|
||||
|
@ -137,7 +143,9 @@ export class Interpretations {
|
|||
|
||||
default:
|
||||
// console.error(`String schema '${name}' with unknown format: '${schema.format}' is treated as simple string.`);
|
||||
throw new Error(`Unknown type for constant value for String '${schema.format}'--cannot create constant value.`);
|
||||
throw new Error(
|
||||
`Unknown type for constant value for String '${schema.format}'--cannot create constant value.`,
|
||||
);
|
||||
}
|
||||
|
||||
case JsonType.Boolean:
|
||||
|
@ -153,41 +161,48 @@ export class Interpretations {
|
|||
|
||||
isApiVersionParameter(parameter: OpenAPI.Parameter): boolean {
|
||||
// Always let x-ms-api-version override the check
|
||||
if (parameter['x-ms-api-version'] !== undefined) {
|
||||
return !!parameter['x-ms-api-version'] === true;
|
||||
if (parameter["x-ms-api-version"] !== undefined) {
|
||||
return !!parameter["x-ms-api-version"] === true;
|
||||
}
|
||||
|
||||
// It's an api-version parameter if it's a query param with an expected name
|
||||
return parameter.in === ParameterLocation.Query && !!apiVersionParameterNames.find(each => each === parameter.name.toLowerCase());
|
||||
return (
|
||||
parameter.in === ParameterLocation.Query &&
|
||||
!!apiVersionParameterNames.find((each) => each === parameter.name.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
getEnumChoices(schema: OpenAPI.Schema): Array<ChoiceValue> {
|
||||
if (schema && schema.enum) {
|
||||
const xmse = <XMSEnum>schema['x-ms-enum'];
|
||||
const xmse = <XMSEnum>schema["x-ms-enum"];
|
||||
|
||||
return xmse && xmse.values ?
|
||||
xmse.values.map((each) => {
|
||||
const name = getValidEnumValueName((each.name !== undefined) ? each.name : each.value);
|
||||
const value = this.getConstantValue(schema, each.value);
|
||||
return new ChoiceValue(`${name}`, each.description || ``, value);
|
||||
}) :
|
||||
schema.enum.map(each => {
|
||||
const name = getValidEnumValueName(each);
|
||||
const value = this.getConstantValue(schema, each);
|
||||
return new ChoiceValue(`${name}`, ``, value);
|
||||
});
|
||||
return xmse && xmse.values
|
||||
? xmse.values.map((each) => {
|
||||
const name = getValidEnumValueName(each.name !== undefined ? each.name : each.value);
|
||||
const value = this.getConstantValue(schema, each.value);
|
||||
return new ChoiceValue(`${name}`, each.description || ``, value);
|
||||
})
|
||||
: schema.enum.map((each) => {
|
||||
const name = getValidEnumValueName(each);
|
||||
const value = this.getConstantValue(schema, each);
|
||||
return new ChoiceValue(`${name}`, ``, value);
|
||||
});
|
||||
}
|
||||
return [];
|
||||
}
|
||||
isEmptyObject(schema: OpenAPI.Schema): boolean {
|
||||
return (schema.type === JsonType.Object && length(schema.allOf) + length(schema.anyOf) + length(schema.oneOf) + length(schema.properties) === 0 && !schema.discriminator);
|
||||
return (
|
||||
schema.type === JsonType.Object &&
|
||||
length(schema.allOf) + length(schema.anyOf) + length(schema.oneOf) + length(schema.properties) === 0 &&
|
||||
!schema.discriminator
|
||||
);
|
||||
}
|
||||
|
||||
getSerialization(schema: OpenAPI.Schema): any | undefined {
|
||||
const xml = this.getXmlSerialization(schema);
|
||||
if (xml) {
|
||||
return {
|
||||
xml
|
||||
xml,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
|
@ -195,18 +210,18 @@ export class Interpretations {
|
|||
|
||||
getXmlSerialization(schema: OpenAPI.Schema): XmlSerlializationFormat | undefined {
|
||||
if (schema.xml) {
|
||||
if (schema.xml['x-ms-text'] && schema.xml.attribute) {
|
||||
if (schema.xml["x-ms-text"] && schema.xml.attribute) {
|
||||
throw new Error(`XML serialization for a schema cannot be in both 'text' and 'attribute'`);
|
||||
}
|
||||
|
||||
return {
|
||||
attribute: schema.xml.attribute || false,
|
||||
wrapped: schema.xml.wrapped || false,
|
||||
text: schema.xml['x-ms-text'] || false,
|
||||
text: schema.xml["x-ms-text"] || false,
|
||||
name: schema.xml.name || undefined,
|
||||
namespace: schema.xml.namespace || undefined,
|
||||
prefix: schema.xml.prefix || undefined,
|
||||
extensions: this.getExtensionProperties(schema.xml)
|
||||
extensions: this.getExtensionProperties(schema.xml),
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
|
@ -218,18 +233,24 @@ export class Interpretations {
|
|||
return undefined;
|
||||
}
|
||||
getApiVersions(schema: OpenAPI.Schema | OpenAPI.HttpOperation | OpenAPI.PathItem): Array<ApiVersion> | undefined {
|
||||
if (schema['x-ms-metadata'] && schema['x-ms-metadata']['apiVersions']) {
|
||||
const v = values(<Array<string>>schema['x-ms-metadata']['apiVersions']).select(each => SetType(ApiVersion, {
|
||||
version: each.replace(/^-/, '').replace(/\+$/, ''),
|
||||
range: each.startsWith('-') ? <any>'-' : each.endsWith('+') ? '+' : undefined
|
||||
})).toArray();
|
||||
if (schema["x-ms-metadata"] && schema["x-ms-metadata"]["apiVersions"]) {
|
||||
const v = values(<Array<string>>schema["x-ms-metadata"]["apiVersions"])
|
||||
.select((each) =>
|
||||
SetType(ApiVersion, {
|
||||
version: each.replace(/^-/, "").replace(/\+$/, ""),
|
||||
range: each.startsWith("-") ? <any>"-" : each.endsWith("+") ? "+" : undefined,
|
||||
}),
|
||||
)
|
||||
.toArray();
|
||||
return v;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
getApiVersionValues(node: OpenAPI.Schema | OpenAPI.HttpOperation | OpenAPI.PathItem): Array<string> {
|
||||
if (node['x-ms-metadata'] && node['x-ms-metadata']['apiVersions']) {
|
||||
return values(<Array<string>>node['x-ms-metadata']['apiVersions']).distinct().toArray();
|
||||
if (node["x-ms-metadata"] && node["x-ms-metadata"]["apiVersions"]) {
|
||||
return values(<Array<string>>node["x-ms-metadata"]["apiVersions"])
|
||||
.distinct()
|
||||
.toArray();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
@ -240,32 +261,36 @@ export class Interpretations {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
constructor(private session: Session<OpenAPI.Model>) {
|
||||
}
|
||||
constructor(private session: Session<OpenAPI.Model>) {}
|
||||
|
||||
xmsMeta(obj: any, key: string) {
|
||||
const m = obj['x-ms-metadata'];
|
||||
const m = obj["x-ms-metadata"];
|
||||
return m ? m[key] : undefined;
|
||||
}
|
||||
|
||||
xmsMetaFallback(obj: any, obj2: any, key: string) {
|
||||
return this.xmsMeta(obj, key) || this.xmsMeta(obj2, key)
|
||||
return this.xmsMeta(obj, key) || this.xmsMeta(obj2, key);
|
||||
}
|
||||
|
||||
splitOpId(opId: string) {
|
||||
const p = opId.indexOf('_');
|
||||
const p = opId.indexOf("_");
|
||||
|
||||
return p != -1 ? {
|
||||
group: opId.substr(0, p),
|
||||
member: opId.substr(p + 1)
|
||||
} : {
|
||||
group: '',
|
||||
member: opId
|
||||
};
|
||||
return p != -1
|
||||
? {
|
||||
group: opId.substr(0, p),
|
||||
member: opId.substr(p + 1),
|
||||
}
|
||||
: {
|
||||
group: "",
|
||||
member: opId,
|
||||
};
|
||||
}
|
||||
|
||||
isBinarySchema(schema: OpenAPI.Schema | undefined) {
|
||||
return !!(schema && (schema.format === StringFormat.Binary || schema.format === 'file' || <any>schema.type === 'file'));
|
||||
return !!(
|
||||
schema &&
|
||||
(schema.format === StringFormat.Binary || schema.format === "file" || <any>schema.type === "file")
|
||||
);
|
||||
}
|
||||
|
||||
getOperationId(httpMethod: string, path: string, original: OpenAPI.HttpOperation) {
|
||||
|
@ -275,24 +300,37 @@ export class Interpretations {
|
|||
|
||||
// synthesize from tags.
|
||||
if (original.tags && length(original.tags) > 0) {
|
||||
|
||||
switch (length(original.tags)) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
this.session.warning(`Generating 'operationId' for '${httpMethod}' operation on path '${path}' `, ['Interpretations'], original);
|
||||
this.session.warning(
|
||||
`Generating 'operationId' for '${httpMethod}' operation on path '${path}' `,
|
||||
["Interpretations"],
|
||||
original,
|
||||
);
|
||||
return this.splitOpId(`${original.tags[0]}`);
|
||||
}
|
||||
this.session.warning(`Generating 'operationId' for '${httpMethod}' operation on path '${path}' `, ['Interpretations'], original);
|
||||
this.session.warning(
|
||||
`Generating 'operationId' for '${httpMethod}' operation on path '${path}' `,
|
||||
["Interpretations"],
|
||||
original,
|
||||
);
|
||||
return this.splitOpId(`${original.tags[0]}_${original.tags[1]}`);
|
||||
}
|
||||
this.session.error(`NEED 'operationId' for '${httpMethod}' operation on path '${path}' `, ['Interpretations'], original);
|
||||
this.session.error(
|
||||
`NEED 'operationId' for '${httpMethod}' operation on path '${path}' `,
|
||||
["Interpretations"],
|
||||
original,
|
||||
);
|
||||
|
||||
return this.splitOpId('unknown-method');
|
||||
return this.splitOpId("unknown-method");
|
||||
}
|
||||
|
||||
|
||||
getDescription(defaultValue: string, original: OpenAPI.Extensions & { title?: string; summary?: string; description?: string }): string {
|
||||
getDescription(
|
||||
defaultValue: string,
|
||||
original: OpenAPI.Extensions & { title?: string; summary?: string; description?: string },
|
||||
): string {
|
||||
if (original) {
|
||||
return original.description || original.title || original.summary || defaultValue;
|
||||
}
|
||||
|
@ -300,20 +338,25 @@ export class Interpretations {
|
|||
}
|
||||
|
||||
getPreferredName(original: any, preferredName?: string, fallbackName?: string) {
|
||||
return original['x-ms-client-name'] ?? preferredName ?? original?.['x-ms-metadata']?.['name'] ?? fallbackName ?? original['name'] ?? 'MISSING_NAME';
|
||||
return (
|
||||
original["x-ms-client-name"] ??
|
||||
preferredName ??
|
||||
original?.["x-ms-metadata"]?.["name"] ??
|
||||
fallbackName ??
|
||||
original["name"] ??
|
||||
"MISSING_NAME"
|
||||
);
|
||||
}
|
||||
|
||||
getName(defaultValue: string, original: any): string {
|
||||
|
||||
return original['x-ms-client-name'] ?? original?.['x-ms-metadata']?.['name'] ?? defaultValue;
|
||||
return original["x-ms-client-name"] ?? original?.["x-ms-metadata"]?.["name"] ?? defaultValue;
|
||||
}
|
||||
|
||||
/** gets the operation path from metadata, falls back to the OAI3 path key */
|
||||
getPath(pathItem: OpenAPI.PathItem, operation: OpenAPI.HttpOperation, path: string) {
|
||||
return this.xmsMeta(pathItem, 'path') || this.xmsMeta(operation, 'path') || path;
|
||||
return this.xmsMeta(pathItem, "path") || this.xmsMeta(operation, "path") || path;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/** creates server entries that are kept in the codeModel.protocol.http, and then referenced in each operation
|
||||
*
|
||||
|
@ -358,7 +401,7 @@ export class Interpretations {
|
|||
*/
|
||||
|
||||
getEnumSchemaForVarible(name: string, somethingWithEnum: { enum?: Array<string> }): ChoiceSchema {
|
||||
return new ChoiceSchema(name, this.getDescription('MISSING-SERVER-VARIABLE-ENUM-DESCRIPTION', somethingWithEnum));
|
||||
return new ChoiceSchema(name, this.getDescription("MISSING-SERVER-VARIABLE-ENUM-DESCRIPTION", somethingWithEnum));
|
||||
}
|
||||
|
||||
getExtensionProperties(dictionary: Dictionary<any>, additional?: Dictionary<any>): Dictionary<any> | undefined {
|
||||
|
@ -372,11 +415,11 @@ export class Interpretations {
|
|||
return main;
|
||||
}
|
||||
getClientDefault(dictionary: Dictionary<any>, additional?: Dictionary<any>): string | number | boolean | undefined {
|
||||
return dictionary?.['x-ms-client-default'] || additional?.['x-ms-client-default'] || undefined;
|
||||
return dictionary?.["x-ms-client-default"] || additional?.["x-ms-client-default"] || undefined;
|
||||
}
|
||||
|
||||
static getExtensionProperties(dictionary: Dictionary<any>): Dictionary<any> | undefined {
|
||||
const result = ToDictionary(OpenAPI.includeXDash(dictionary), each => dictionary[each]);
|
||||
const result = ToDictionary(OpenAPI.includeXDash(dictionary), (each) => dictionary[each]);
|
||||
for (const each of removeKnownParameters) {
|
||||
delete result[each];
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -3,19 +3,18 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { deserialize, serialize } from '@azure-tools/codegen';
|
||||
import { Host, startSession } from '@azure-tools/autorest-extension-base';
|
||||
import * as OpenAPI from '@azure-tools/openapi';
|
||||
import { ModelerFour } from './modelerfour';
|
||||
import { codeModelSchema, CodeModel } from '@azure-tools/codemodel';
|
||||
|
||||
import { deserialize, serialize } from "@azure-tools/codegen";
|
||||
import { Host, startSession } from "@azure-tools/autorest-extension-base";
|
||||
import * as OpenAPI from "@azure-tools/openapi";
|
||||
import { ModelerFour } from "./modelerfour";
|
||||
import { codeModelSchema, CodeModel } from "@azure-tools/codemodel";
|
||||
|
||||
export async function processRequest(host: Host) {
|
||||
const debug = await host.GetValue('debug') || false;
|
||||
const debug = (await host.GetValue("debug")) || false;
|
||||
|
||||
try {
|
||||
const session = await startSession<OpenAPI.Model>(host, undefined, undefined, 'prechecked-openapi-document');
|
||||
const options = <any>await session.getValue('modelerfour', {});
|
||||
const session = await startSession<OpenAPI.Model>(host, undefined, undefined, "prechecked-openapi-document");
|
||||
const options = <any>await session.getValue("modelerfour", {});
|
||||
|
||||
// process
|
||||
const modeler = await new ModelerFour(session).init();
|
||||
|
@ -24,17 +23,16 @@ export async function processRequest(host: Host) {
|
|||
const codeModel = modeler.process();
|
||||
|
||||
// throw on errors.
|
||||
if (!await session.getValue('ignore-errors', false)) {
|
||||
if (!(await session.getValue("ignore-errors", false))) {
|
||||
session.checkpoint();
|
||||
}
|
||||
|
||||
|
||||
// output the model to the pipeline
|
||||
if (options['emit-yaml-tags'] !== false) {
|
||||
host.WriteFile('code-model-v4.yaml', serialize(codeModel, codeModelSchema), undefined, 'code-model-v4');
|
||||
if (options["emit-yaml-tags"] !== false) {
|
||||
host.WriteFile("code-model-v4.yaml", serialize(codeModel, codeModelSchema), undefined, "code-model-v4");
|
||||
}
|
||||
if (options['emit-yaml-tags'] !== true) {
|
||||
host.WriteFile('code-model-v4-no-tags.yaml', serialize(codeModel), undefined, 'code-model-v4-no-tags');
|
||||
if (options["emit-yaml-tags"] !== true) {
|
||||
host.WriteFile("code-model-v4-no-tags.yaml", serialize(codeModel), undefined, "code-model-v4-no-tags");
|
||||
}
|
||||
} catch (E) {
|
||||
if (debug) {
|
||||
|
|
|
@ -3,17 +3,17 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { serialize } from '@azure-tools/codegen';
|
||||
import { Host, startSession } from '@azure-tools/autorest-extension-base';
|
||||
import { codeModelSchema, CodeModel } from '@azure-tools/codemodel';
|
||||
import { PreNamer } from './prenamer';
|
||||
import { serialize } from "@azure-tools/codegen";
|
||||
import { Host, startSession } from "@azure-tools/autorest-extension-base";
|
||||
import { codeModelSchema, CodeModel } from "@azure-tools/codemodel";
|
||||
import { PreNamer } from "./prenamer";
|
||||
|
||||
export async function processRequest(host: Host) {
|
||||
const debug = await host.GetValue('debug') || false;
|
||||
const debug = (await host.GetValue("debug")) || false;
|
||||
|
||||
try {
|
||||
const session = await startSession<CodeModel>(host, {}, codeModelSchema);
|
||||
const options = <any>await session.getValue('modelerfour', {});
|
||||
const options = <any>await session.getValue("modelerfour", {});
|
||||
|
||||
// process
|
||||
const plugin = await new PreNamer(session).init();
|
||||
|
@ -22,11 +22,11 @@ export async function processRequest(host: Host) {
|
|||
const result = plugin.process();
|
||||
|
||||
// output the model to the pipeline
|
||||
if (options['emit-yaml-tags'] !== false) {
|
||||
host.WriteFile('code-model-v4.yaml', serialize(result, codeModelSchema), undefined, 'code-model-v4');
|
||||
if (options["emit-yaml-tags"] !== false) {
|
||||
host.WriteFile("code-model-v4.yaml", serialize(result, codeModelSchema), undefined, "code-model-v4");
|
||||
}
|
||||
if (options['emit-yaml-tags'] !== true) {
|
||||
host.WriteFile('code-model-v4-no-tags.yaml', serialize(result), undefined, 'code-model-v4-no-tags');
|
||||
if (options["emit-yaml-tags"] !== true) {
|
||||
host.WriteFile("code-model-v4-no-tags.yaml", serialize(result), undefined, "code-model-v4-no-tags");
|
||||
}
|
||||
} catch (E) {
|
||||
if (debug) {
|
||||
|
|
|
@ -1,7 +1,32 @@
|
|||
import { CodeModel, Parameter, isVirtualParameter, ObjectSchema, isObjectSchema, getAllParentProperties, Languages, SchemaType, Schema, ChoiceSchema, SealedChoiceSchema, GroupSchema, ImplementationLocation, Operation, Request, Response } from '@azure-tools/codemodel';
|
||||
import { Session } from '@azure-tools/autorest-extension-base';
|
||||
import { values, length, Dictionary, when, items } from '@azure-tools/linq';
|
||||
import { removeSequentialDuplicates, fixLeadingNumber, deconstruct, selectName, Style, Styler, pascalCase } from '@azure-tools/codegen';
|
||||
import {
|
||||
CodeModel,
|
||||
Parameter,
|
||||
isVirtualParameter,
|
||||
ObjectSchema,
|
||||
isObjectSchema,
|
||||
getAllParentProperties,
|
||||
Languages,
|
||||
SchemaType,
|
||||
Schema,
|
||||
ChoiceSchema,
|
||||
SealedChoiceSchema,
|
||||
GroupSchema,
|
||||
ImplementationLocation,
|
||||
Operation,
|
||||
Request,
|
||||
Response,
|
||||
} from "@azure-tools/codemodel";
|
||||
import { Session } from "@azure-tools/autorest-extension-base";
|
||||
import { values, length, Dictionary, when, items } from "@azure-tools/linq";
|
||||
import {
|
||||
removeSequentialDuplicates,
|
||||
fixLeadingNumber,
|
||||
deconstruct,
|
||||
selectName,
|
||||
Style,
|
||||
Styler,
|
||||
pascalCase,
|
||||
} from "@azure-tools/codegen";
|
||||
|
||||
function getNameOptions(typeName: string, components: Array<string>) {
|
||||
const result = new Set<string>();
|
||||
|
@ -13,37 +38,61 @@ function getNameOptions(typeName: string, components: Array<string>) {
|
|||
}
|
||||
|
||||
// add a second-to-last-ditch option as <typename>.<name>
|
||||
result.add(Style.pascal([...removeSequentialDuplicates([...fixLeadingNumber(deconstruct(typeName)), ...deconstruct(components.last)])]));
|
||||
result.add(
|
||||
Style.pascal([
|
||||
...removeSequentialDuplicates([...fixLeadingNumber(deconstruct(typeName)), ...deconstruct(components.last)]),
|
||||
]),
|
||||
);
|
||||
return [...result.values()];
|
||||
}
|
||||
|
||||
function isUnassigned(value: string) {
|
||||
return !value || (value.indexOf('·') > -1);
|
||||
return !value || value.indexOf("·") > -1;
|
||||
}
|
||||
|
||||
interface SetNameOptions {
|
||||
removeDuplicates: boolean
|
||||
};
|
||||
removeDuplicates: boolean;
|
||||
}
|
||||
|
||||
function setName(thing: { language: Languages }, styler: Styler, defaultValue: string, overrides: Dictionary<string>, options?: SetNameOptions) {
|
||||
function setName(
|
||||
thing: { language: Languages },
|
||||
styler: Styler,
|
||||
defaultValue: string,
|
||||
overrides: Dictionary<string>,
|
||||
options?: SetNameOptions,
|
||||
) {
|
||||
options = {
|
||||
removeDuplicates: true,
|
||||
...options
|
||||
...options,
|
||||
};
|
||||
|
||||
thing.language.default.name = styler(defaultValue && isUnassigned(thing.language.default.name) ? defaultValue : thing.language.default.name, options.removeDuplicates, overrides);
|
||||
thing.language.default.name = styler(
|
||||
defaultValue && isUnassigned(thing.language.default.name) ? defaultValue : thing.language.default.name,
|
||||
options.removeDuplicates,
|
||||
overrides,
|
||||
);
|
||||
if (!thing.language.default.name) {
|
||||
throw new Error('Name is empty!');
|
||||
throw new Error("Name is empty!");
|
||||
}
|
||||
}
|
||||
|
||||
function setNameAllowEmpty(thing: { language: Languages }, styler: Styler, defaultValue: string, overrides: Dictionary<string>, options?: SetNameOptions) {
|
||||
function setNameAllowEmpty(
|
||||
thing: { language: Languages },
|
||||
styler: Styler,
|
||||
defaultValue: string,
|
||||
overrides: Dictionary<string>,
|
||||
options?: SetNameOptions,
|
||||
) {
|
||||
options = {
|
||||
removeDuplicates: true,
|
||||
...options
|
||||
...options,
|
||||
};
|
||||
|
||||
thing.language.default.name = styler(defaultValue && isUnassigned(thing.language.default.name) ? defaultValue : thing.language.default.name, options.removeDuplicates, overrides);
|
||||
thing.language.default.name = styler(
|
||||
defaultValue && isUnassigned(thing.language.default.name) ? defaultValue : thing.language.default.name,
|
||||
options.removeDuplicates,
|
||||
overrides,
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -57,14 +106,13 @@ function deduplicateSchemaName(
|
|||
schema: Schema,
|
||||
schemaNames: Set<string>,
|
||||
session: Session<CodeModel>,
|
||||
indexer: (schema: Schema, newName: string) => string =
|
||||
(schema: Schema, proposedName: string) => proposedName
|
||||
indexer: (schema: Schema, newName: string) => string = (schema: Schema, proposedName: string) => proposedName,
|
||||
): void {
|
||||
const schemaName = schema.language.default.name;
|
||||
const maxDedupes = 1000;
|
||||
if (schemaNames.has(indexer(schema, schemaName))) {
|
||||
for (let i = 1; i <= maxDedupes; i++) {
|
||||
const newName = `${schemaName}AutoGenerated${(i === 1 ? "" : i)}`;
|
||||
const newName = `${schemaName}AutoGenerated${i === 1 ? "" : i}`;
|
||||
if (!schemaNames.has(indexer(schema, newName))) {
|
||||
schema.language.default.name = newName;
|
||||
schemaNames.add(indexer(schema, newName));
|
||||
|
@ -73,7 +121,10 @@ function deduplicateSchemaName(
|
|||
}
|
||||
}
|
||||
|
||||
session.error(`Attempted to deduplicate schema name '${schema.language.default.name}' more than ${maxDedupes} times and failed.`, ["PreNamer/DeduplicateName"])
|
||||
session.error(
|
||||
`Attempted to deduplicate schema name '${schema.language.default.name}' more than ${maxDedupes} times and failed.`,
|
||||
["PreNamer/DeduplicateName"],
|
||||
);
|
||||
}
|
||||
|
||||
// We haven't seen the name before, add it
|
||||
|
@ -81,7 +132,7 @@ function deduplicateSchemaName(
|
|||
}
|
||||
|
||||
export class PreNamer {
|
||||
codeModel: CodeModel
|
||||
codeModel: CodeModel;
|
||||
options: Dictionary<any> = {};
|
||||
format = {
|
||||
parameter: Style.camel,
|
||||
|
@ -97,20 +148,20 @@ export class PreNamer {
|
|||
client: Style.pascal,
|
||||
local: Style.camel,
|
||||
global: Style.pascal,
|
||||
override: <Dictionary<string>>{}
|
||||
}
|
||||
override: <Dictionary<string>>{},
|
||||
};
|
||||
|
||||
enum = 0;
|
||||
constant = 0;
|
||||
constructor(protected session: Session<CodeModel>) {
|
||||
this.codeModel = session.model;// shadow(session.model, filename);
|
||||
this.codeModel = session.model; // shadow(session.model, filename);
|
||||
}
|
||||
|
||||
async init() {
|
||||
// get our configuration for this run.
|
||||
this.options = await this.session.getValue('modelerfour', {});
|
||||
this.options = await this.session.getValue("modelerfour", {});
|
||||
const naming = this.options.naming || {};
|
||||
const maxPreserve = Number(naming['preserve-uppercase-max-length']) || 3;
|
||||
const maxPreserve = Number(naming["preserve-uppercase-max-length"]) || 3;
|
||||
this.format = {
|
||||
parameter: Style.select(naming.parameter, Style.camel, maxPreserve),
|
||||
property: Style.select(naming.property, Style.camel, maxPreserve),
|
||||
|
@ -125,23 +176,22 @@ export class PreNamer {
|
|||
type: Style.select(naming.type, Style.pascal, maxPreserve),
|
||||
local: Style.select(naming.local, Style.camel, maxPreserve),
|
||||
global: Style.select(naming.global, Style.pascal, maxPreserve),
|
||||
override: naming.override || {}
|
||||
}
|
||||
override: naming.override || {},
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
isUnassigned(value: string) {
|
||||
return !value || (value.indexOf('·') > -1);
|
||||
return !value || value.indexOf("·") > -1;
|
||||
}
|
||||
|
||||
process() {
|
||||
if (this.options['prenamer'] === false) {
|
||||
if (this.options["prenamer"] === false) {
|
||||
return this.codeModel;
|
||||
}
|
||||
|
||||
const deduplicateSchemaNames =
|
||||
!!this.options['lenient-model-deduplication'] ||
|
||||
!!this.options['resolve-schema-name-collisons'];
|
||||
!!this.options["lenient-model-deduplication"] || !!this.options["resolve-schema-name-collisons"];
|
||||
|
||||
// choice
|
||||
const choiceSchemaNames = new Set<string>();
|
||||
|
@ -153,7 +203,7 @@ export class PreNamer {
|
|||
}
|
||||
|
||||
for (const choice of values(schema.choices)) {
|
||||
setName(choice, this.format.choiceValue, '', this.format.override, { removeDuplicates: false });
|
||||
setName(choice, this.format.choiceValue, "", this.format.override, { removeDuplicates: false });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,7 +217,7 @@ export class PreNamer {
|
|||
}
|
||||
|
||||
for (const choice of values(schema.choices)) {
|
||||
setName(choice, this.format.choiceValue, '', this.format.override, { removeDuplicates: false });
|
||||
setName(choice, this.format.choiceValue, "", this.format.override, { removeDuplicates: false });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,7 +257,7 @@ export class PreNamer {
|
|||
setName(schema, this.format.type, schema.type, this.format.override);
|
||||
|
||||
if (isUnassigned(schema.language.default.description)) {
|
||||
schema.language.default.description = 'date in seconds since 1970-01-01T00:00:00Z.';
|
||||
schema.language.default.description = "date in seconds since 1970-01-01T00:00:00Z.";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -229,7 +279,12 @@ export class PreNamer {
|
|||
|
||||
// dictionary
|
||||
for (const schema of values(this.codeModel.schemas.dictionaries)) {
|
||||
setName(schema, this.format.type, `DictionaryOf${schema.elementType.language.default.name}`, this.format.override);
|
||||
setName(
|
||||
schema,
|
||||
this.format.type,
|
||||
`DictionaryOf${schema.elementType.language.default.name}`,
|
||||
this.format.override,
|
||||
);
|
||||
if (isUnassigned(schema.language.default.description)) {
|
||||
schema.language.default.description = `Dictionary of ${schema.elementType.language.default.name}`;
|
||||
}
|
||||
|
@ -244,52 +299,54 @@ export class PreNamer {
|
|||
|
||||
const objectSchemaNames = new Set<string>();
|
||||
for (const schema of values(this.codeModel.schemas.objects)) {
|
||||
setName(schema, this.format.type, '', this.format.override);
|
||||
setName(schema, this.format.type, "", this.format.override);
|
||||
|
||||
if (deduplicateSchemaNames) {
|
||||
deduplicateSchemaName(
|
||||
schema,
|
||||
objectSchemaNames,
|
||||
this.session,
|
||||
(schema: Schema, proposedName: string) =>
|
||||
`${(schema.language.default.namespace || "")}.${proposedName}`);
|
||||
(schema: Schema, proposedName: string) => `${schema.language.default.namespace || ""}.${proposedName}`,
|
||||
);
|
||||
}
|
||||
|
||||
for (const property of values(schema.properties)) {
|
||||
setName(property, this.format.property, '', this.format.override);
|
||||
setName(property, this.format.property, "", this.format.override);
|
||||
}
|
||||
}
|
||||
|
||||
const groupSchemaNames = new Set<string>();
|
||||
for (const schema of values(this.codeModel.schemas.groups)) {
|
||||
setName(schema, this.format.type, '', this.format.override);
|
||||
setName(schema, this.format.type, "", this.format.override);
|
||||
|
||||
if (deduplicateSchemaNames) {
|
||||
deduplicateSchemaName(
|
||||
schema,
|
||||
groupSchemaNames,
|
||||
this.session,
|
||||
(schema: Schema, proposedName: string) =>
|
||||
`${(schema.language.default.namespace || "")}.${proposedName}`);
|
||||
(schema: Schema, proposedName: string) => `${schema.language.default.namespace || ""}.${proposedName}`,
|
||||
);
|
||||
}
|
||||
|
||||
for (const property of values(schema.properties)) {
|
||||
setName(property, this.format.property, '', this.format.override);
|
||||
setName(property, this.format.property, "", this.format.override);
|
||||
}
|
||||
}
|
||||
|
||||
for (const parameter of values(this.codeModel.globalParameters)) {
|
||||
if (parameter.schema.type === SchemaType.Constant) {
|
||||
setName(parameter, this.format.constantParameter, '', this.format.override);
|
||||
setName(parameter, this.format.constantParameter, "", this.format.override);
|
||||
} else {
|
||||
setName(parameter, this.format.parameter, '', this.format.override);
|
||||
setName(parameter, this.format.parameter, "", this.format.override);
|
||||
}
|
||||
}
|
||||
|
||||
for (const operationGroup of this.codeModel.operationGroups) {
|
||||
setNameAllowEmpty(operationGroup, this.format.operationGroup, operationGroup.$key, this.format.override, { removeDuplicates: false });
|
||||
setNameAllowEmpty(operationGroup, this.format.operationGroup, operationGroup.$key, this.format.override, {
|
||||
removeDuplicates: false,
|
||||
});
|
||||
for (const operation of operationGroup.operations) {
|
||||
setName(operation, this.format.operation, '', this.format.override);
|
||||
setName(operation, this.format.operation, "", this.format.override);
|
||||
|
||||
this.setParameterNames(operation);
|
||||
for (const request of values(operation.requests)) {
|
||||
|
@ -326,23 +383,20 @@ export class PreNamer {
|
|||
private setParameterNames(parameterContainer: Operation | Request) {
|
||||
for (const parameter of values(parameterContainer.signatureParameters)) {
|
||||
if (parameter.schema.type === SchemaType.Constant) {
|
||||
setName(parameter, this.format.constantParameter, '', this.format.override);
|
||||
}
|
||||
else {
|
||||
setName(parameter, this.format.parameter, '', this.format.override);
|
||||
setName(parameter, this.format.constantParameter, "", this.format.override);
|
||||
} else {
|
||||
setName(parameter, this.format.parameter, "", this.format.override);
|
||||
}
|
||||
}
|
||||
for (const parameter of values(parameterContainer.parameters)) {
|
||||
if ((parameterContainer.signatureParameters ?? []).indexOf(parameter) === -1) {
|
||||
if (parameter.schema.type === SchemaType.Constant) {
|
||||
setName(parameter, this.format.constantParameter, '', this.format.override);
|
||||
}
|
||||
else {
|
||||
setName(parameter, this.format.constantParameter, "", this.format.override);
|
||||
} else {
|
||||
if (parameter.implementation === ImplementationLocation.Client) {
|
||||
setName(parameter, this.format.global, '', this.format.override);
|
||||
}
|
||||
else {
|
||||
setName(parameter, this.format.local, '', this.format.override);
|
||||
setName(parameter, this.format.global, "", this.format.override);
|
||||
} else {
|
||||
setName(parameter, this.format.local, "", this.format.override);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -352,13 +406,13 @@ export class PreNamer {
|
|||
private setResponseHeaderNames(response: Response) {
|
||||
if (response.protocol.http) {
|
||||
for (const { value: header } of items(response.protocol.http.headers)) {
|
||||
setName(header as {language: Languages}, this.format.responseHeader, '', this.format.override);
|
||||
setName(header as { language: Languages }, this.format.responseHeader, "", this.format.override);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fixParameterCollisions() {
|
||||
for (const operation of values(this.codeModel.operationGroups).selectMany(each => each.operations)) {
|
||||
for (const operation of values(this.codeModel.operationGroups).selectMany((each) => each.operations)) {
|
||||
for (const request of values(operation.requests)) {
|
||||
const parameters = values(operation.signatureParameters).concat(values(request.signatureParameters));
|
||||
|
||||
|
@ -380,21 +434,22 @@ export class PreNamer {
|
|||
for (const parameter of collisions) {
|
||||
let options = [parameter.language.default.name];
|
||||
if (isVirtualParameter(parameter)) {
|
||||
options = getNameOptions(parameter.schema.language.default.name, [parameter.language.default.name, ...parameter.pathToProperty.map(each => each.language.default.name)]).map(each => this.format.parameter(each));
|
||||
options = getNameOptions(parameter.schema.language.default.name, [
|
||||
parameter.language.default.name,
|
||||
...parameter.pathToProperty.map((each) => each.language.default.name),
|
||||
]).map((each) => this.format.parameter(each));
|
||||
}
|
||||
parameter.language.default.name = this.format.parameter(selectName(options, usedNames));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fixCollisions(schema: ObjectSchema) {
|
||||
for (const each of values(schema.parents?.immediate).where(each => isObjectSchema(each))) {
|
||||
for (const each of values(schema.parents?.immediate).where((each) => isObjectSchema(each))) {
|
||||
this.fixCollisions(<ObjectSchema>each);
|
||||
}
|
||||
const [owned, flattened] = values(schema.properties).bifurcate(each => length(each.flattenedNames) === 0);
|
||||
const [owned, flattened] = values(schema.properties).bifurcate((each) => length(each.flattenedNames) === 0);
|
||||
const inherited = [...getAllParentProperties(schema)];
|
||||
|
||||
const all = [...owned, ...inherited, ...flattened];
|
||||
|
@ -410,7 +465,10 @@ export class PreNamer {
|
|||
for (const each of flattened /*.sort((a, b) => length(a.nameOptions) - length(b.nameOptions)) */) {
|
||||
const ct = inlined.get(this.format.property(each.language.default.name));
|
||||
if (ct && ct > 1) {
|
||||
const options = getNameOptions(each.schema.language.default.name, [each.language.default.name, ...values(each.flattenedNames)]);
|
||||
const options = getNameOptions(each.schema.language.default.name, [
|
||||
each.language.default.name,
|
||||
...values(each.flattenedNames),
|
||||
]);
|
||||
each.language.default.name = this.format.property(selectName(options, usedNames));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,24 @@
|
|||
import { Session } from "@azure-tools/autorest-extension-base";
|
||||
import { values, items, length, Dictionary, refCount, clone, keys } from "@azure-tools/linq";
|
||||
import {
|
||||
Model as oai3,
|
||||
Refable,
|
||||
Dereferenced,
|
||||
dereference,
|
||||
Schema,
|
||||
PropertyDetails,
|
||||
JsonType,
|
||||
StringFormat,
|
||||
} from "@azure-tools/openapi";
|
||||
|
||||
import { Session } from '@azure-tools/autorest-extension-base';
|
||||
import { values, items, length, Dictionary, refCount, clone, keys } from '@azure-tools/linq';
|
||||
import { Model as oai3, Refable, Dereferenced, dereference, Schema, PropertyDetails, JsonType, StringFormat } from '@azure-tools/openapi';
|
||||
import { serialize } from "@azure-tools/codegen";
|
||||
import { Host, startSession } from "@azure-tools/autorest-extension-base";
|
||||
import { Interpretations } from "../modeler/interpretations";
|
||||
|
||||
import { serialize } from '@azure-tools/codegen';
|
||||
import { Host, startSession } from '@azure-tools/autorest-extension-base';
|
||||
import { Interpretations } from '../modeler/interpretations';
|
||||
|
||||
import { getDiff } from 'recursive-diff'
|
||||
import { getDiff } from "recursive-diff";
|
||||
|
||||
export async function processRequest(host: Host) {
|
||||
const debug = await host.GetValue('debug') || false;
|
||||
const debug = (await host.GetValue("debug")) || false;
|
||||
|
||||
try {
|
||||
const session = await startSession<oai3>(host);
|
||||
|
@ -23,12 +31,22 @@ export async function processRequest(host: Host) {
|
|||
const result = plugin.process();
|
||||
|
||||
// throw on errors.
|
||||
if (!await session.getValue('ignore-errors', false)) {
|
||||
if (!(await session.getValue("ignore-errors", false))) {
|
||||
session.checkpoint();
|
||||
}
|
||||
|
||||
host.WriteFile('prechecked-openapi-document.yaml', serialize(result, { sortKeys: false }), undefined, 'prechecked-openapi-document');
|
||||
host.WriteFile('original-openapi-document.yaml', serialize(input, { sortKeys: false }), undefined, 'openapi-document');
|
||||
host.WriteFile(
|
||||
"prechecked-openapi-document.yaml",
|
||||
serialize(result, { sortKeys: false }),
|
||||
undefined,
|
||||
"prechecked-openapi-document",
|
||||
);
|
||||
host.WriteFile(
|
||||
"original-openapi-document.yaml",
|
||||
serialize(input, { sortKeys: false }),
|
||||
undefined,
|
||||
"openapi-document",
|
||||
);
|
||||
} catch (E) {
|
||||
if (debug) {
|
||||
console.error(`${__filename} - FAILURE ${JSON.stringify(E)} ${E.stack}`);
|
||||
|
@ -43,14 +61,14 @@ export class QualityPreChecker {
|
|||
protected interpret: Interpretations;
|
||||
|
||||
constructor(protected session: Session<oai3>) {
|
||||
this.input = session.model;// shadow(session.model, filename);
|
||||
this.input = session.model; // shadow(session.model, filename);
|
||||
|
||||
this.interpret = new Interpretations(session);
|
||||
}
|
||||
|
||||
async init() {
|
||||
// get our configuration for this run.
|
||||
this.options = await this.session.getValue('modelerfour', {});
|
||||
this.options = await this.session.getValue("modelerfour", {});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -59,35 +77,37 @@ export class QualityPreChecker {
|
|||
return dereference(this.input, item);
|
||||
}
|
||||
|
||||
|
||||
getProperties(schema: Schema) {
|
||||
return items(schema.properties).select(each => ({
|
||||
return items(schema.properties).select((each) => ({
|
||||
key: each.key,
|
||||
name: <string>this.interpret.getPreferredName(each.value, each.key),
|
||||
property: this.resolve(each.value).instance
|
||||
property: this.resolve(each.value).instance,
|
||||
}));
|
||||
//return items(schema.properties).toMap(each => <string>this.interpret.getPreferredName(each.value, each.key), each => this.resolve(each.value).instance);
|
||||
}
|
||||
|
||||
getSchemasFromArray(tag: string, schemas: Array<Refable<Schema>> | undefined): Iterable<{ name: string, schema: Schema, tag: string }> {
|
||||
return values(schemas).select(a => {
|
||||
getSchemasFromArray(
|
||||
tag: string,
|
||||
schemas: Array<Refable<Schema>> | undefined,
|
||||
): Iterable<{ name: string; schema: Schema; tag: string }> {
|
||||
return values(schemas).select((a) => {
|
||||
const { instance: schema, name } = this.resolve(a);
|
||||
return {
|
||||
name: this.interpret.getName(name, schema),
|
||||
schema,
|
||||
tag
|
||||
}
|
||||
tag,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
* getAllParents(tag: string, schema: Schema): Iterable<{ name: string, schema: Schema, tag: string }> {
|
||||
*getAllParents(tag: string, schema: Schema): Iterable<{ name: string; schema: Schema; tag: string }> {
|
||||
for (const parent of this.getSchemasFromArray(tag, schema.allOf)) {
|
||||
yield parent;
|
||||
yield* this.getAllParents(parent.name, parent.schema);
|
||||
}
|
||||
}
|
||||
|
||||
* getGrandParents(tag: string, schema: Schema): Iterable<{ name: string, schema: Schema, tag: string }> {
|
||||
*getGrandParents(tag: string, schema: Schema): Iterable<{ name: string; schema: Schema; tag: string }> {
|
||||
for (const parent of this.getSchemasFromArray(tag, schema.allOf)) {
|
||||
yield* this.getAllParents(parent.name, parent.schema);
|
||||
}
|
||||
|
@ -99,40 +119,51 @@ export class QualityPreChecker {
|
|||
}
|
||||
completed.add(schema);
|
||||
|
||||
|
||||
if (schema.allOf && schema.properties) {
|
||||
|
||||
const myProperties = this.getProperties(schema).toArray();
|
||||
|
||||
for (const { name: parentName, schema: parentSchema } of this.getAllParents(schemaName, schema)) {
|
||||
this.checkForHiddenProperties(parentName, parentSchema, completed);
|
||||
|
||||
for (const { key, name: propName, property: parentProp } of this.getProperties(parentSchema)) {
|
||||
const myProp = myProperties.find(each => each.name === propName);
|
||||
const myProp = myProperties.find((each) => each.name === propName);
|
||||
if (myProp) {
|
||||
// check if the only thing different is the description.
|
||||
const diff = getDiff(parentProp, myProp.property).filter(each => each.path[0] !== 'description' && each.path[0] !== 'x-ms-metadata');
|
||||
const diff = getDiff(parentProp, myProp.property).filter(
|
||||
(each) => each.path[0] !== "description" && each.path[0] !== "x-ms-metadata",
|
||||
);
|
||||
|
||||
if (diff.length === 0) {
|
||||
// the property didn't change except for description.
|
||||
// the property didn't change except for description.
|
||||
// we can let this go with a warning.
|
||||
this.session.warning(`Schema '${schemaName}' has a property '${propName}' that is already declared the parent schema '${parentName}' but isn't significantly different. The property has been removed from ${schemaName}`, ['PreCheck', 'PropertyRedeclarationWarning']);
|
||||
this.session.warning(
|
||||
`Schema '${schemaName}' has a property '${propName}' that is already declared the parent schema '${parentName}' but isn't significantly different. The property has been removed from ${schemaName}`,
|
||||
["PreCheck", "PropertyRedeclarationWarning"],
|
||||
);
|
||||
delete schema.properties[myProp.key];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (diff.length === 1) {
|
||||
// special case to yell about readonly changes
|
||||
if (diff[0].path[0] === 'readOnly') {
|
||||
this.session.warning(`Schema '${schemaName}' has a property '${propName}' that is already declared the parent schema '${parentName}' but 'readonly' has been changed -- this is not permitted. The property has been removed from ${schemaName}`, ['PreCheck', 'PropertyRedeclarationWarning']);
|
||||
if (diff[0].path[0] === "readOnly") {
|
||||
this.session.warning(
|
||||
`Schema '${schemaName}' has a property '${propName}' that is already declared the parent schema '${parentName}' but 'readonly' has been changed -- this is not permitted. The property has been removed from ${schemaName}`,
|
||||
["PreCheck", "PropertyRedeclarationWarning"],
|
||||
);
|
||||
delete schema.properties[myProp.key];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (diff.length > 0) {
|
||||
const details = diff.map(each => `${each.path.join('.')} => '${each.op === 'delete' ? '<removed>' : each.val}'`).join(',');
|
||||
this.session.error(`Schema '${schemaName}' has a property '${propName}' that is conflicting with a property in the parent schema '${parentName}' differs more than just description : [${details}]`, ['PreCheck', 'PropertyRedeclaration']);
|
||||
const details = diff
|
||||
.map((each) => `${each.path.join(".")} => '${each.op === "delete" ? "<removed>" : each.val}'`)
|
||||
.join(",");
|
||||
this.session.error(
|
||||
`Schema '${schemaName}' has a property '${propName}' that is conflicting with a property in the parent schema '${parentName}' differs more than just description : [${details}]`,
|
||||
["PreCheck", "PropertyRedeclaration"],
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -148,13 +179,15 @@ export class QualityPreChecker {
|
|||
completed.add(schema);
|
||||
|
||||
if (schema.allOf) {
|
||||
|
||||
const grandParents = [...this.getGrandParents(schemaName, schema)];
|
||||
const direct = [...this.getSchemasFromArray(schemaName, schema.allOf)];
|
||||
|
||||
for (const myParent of direct) {
|
||||
for (const duplicate of grandParents.filter(each => each.schema === myParent.schema)) {
|
||||
this.session.error(`Schema '${schemaName}' inherits '${duplicate.tag}' via an \`allOf\` that is already coming from parent '${myParent.name}'`, ['PreCheck', 'DuplicateInheritance']);
|
||||
for (const duplicate of grandParents.filter((each) => each.schema === myParent.schema)) {
|
||||
this.session.error(
|
||||
`Schema '${schemaName}' inherits '${duplicate.tag}' via an \`allOf\` that is already coming from parent '${myParent.name}'`,
|
||||
["PreCheck", "DuplicateInheritance"],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +201,7 @@ export class QualityPreChecker {
|
|||
return false;
|
||||
|
||||
case JsonType.String:
|
||||
return schema.enum || schema['x-ms-enum'];
|
||||
return schema.enum || schema["x-ms-enum"];
|
||||
|
||||
case JsonType.Object:
|
||||
// empty objects don't worry.
|
||||
|
@ -178,30 +211,33 @@ export class QualityPreChecker {
|
|||
return true;
|
||||
|
||||
default:
|
||||
return (length(schema.properties) > 0 || length(schema.allOf) > 0) ? true : false;
|
||||
return length(schema.properties) > 0 || length(schema.allOf) > 0 ? true : false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
checkForDuplicateSchemas(): undefined {
|
||||
this.session.warning('Checking for duplicate schemas, this could take a (long) while. Run with --verbose for more detail.', ['PreCheck', 'CheckDuplicateSchemas']);
|
||||
this.session.warning(
|
||||
"Checking for duplicate schemas, this could take a (long) while. Run with --verbose for more detail.",
|
||||
["PreCheck", "CheckDuplicateSchemas"],
|
||||
);
|
||||
|
||||
// Returns true if scanning should be restarted
|
||||
const innerCheckForDuplicateSchemas = (): any => {
|
||||
const errors = new Set<string>();
|
||||
if (this.input.components && this.input.components.schemas) {
|
||||
const dupedNames = items(this.input.components?.schemas)
|
||||
.select(s => ({ key: s.key, value: this.resolve(s.value) }))
|
||||
.select((s) => ({ key: s.key, value: this.resolve(s.value) }))
|
||||
.groupBy(
|
||||
// Make sure to check x-ms-client-name first to see if the schema is already being renamed
|
||||
each => each.value.instance['x-ms-client-name'] || each.value.instance['x-ms-metadata']?.name,
|
||||
each => each);
|
||||
(each) => each.value.instance["x-ms-client-name"] || each.value.instance["x-ms-metadata"]?.name,
|
||||
(each) => each,
|
||||
);
|
||||
|
||||
for (const [name, schemas] of dupedNames.entries()) {
|
||||
if (name && schemas.length > 1) {
|
||||
|
||||
|
||||
const diff = getDiff(schemas[0].value.instance, schemas[1].value.instance).filter(each => each.path[0] !== 'description' && each.path[0] !== 'x-ms-metadata');
|
||||
const diff = getDiff(schemas[0].value.instance, schemas[1].value.instance).filter(
|
||||
(each) => each.path[0] !== "description" && each.path[0] !== "x-ms-metadata",
|
||||
);
|
||||
|
||||
if (diff.length === 0) {
|
||||
// found two schemas that are indeed the same.
|
||||
|
@ -210,42 +246,67 @@ export class QualityPreChecker {
|
|||
|
||||
delete this.input.components.schemas[schemas[1].key];
|
||||
const text = JSON.stringify(this.input);
|
||||
this.input = JSON.parse(text.replace(new RegExp(`"\\#\\/components\\/schemas\\/${schemas[1].key}"`, 'g'), `"#/components/schemas/${schemas[0].key}"`));
|
||||
this.input = JSON.parse(
|
||||
text.replace(
|
||||
new RegExp(`"\\#\\/components\\/schemas\\/${schemas[1].key}"`, "g"),
|
||||
`"#/components/schemas/${schemas[0].key}"`,
|
||||
),
|
||||
);
|
||||
|
||||
// update metadata to match
|
||||
if (this.input?.components?.schemas?.[schemas[0].key]) {
|
||||
|
||||
const primarySchema = this.resolve(this.input.components.schemas[schemas[0].key])
|
||||
const primaryMetadata = primarySchema.instance['x-ms-metadata'];
|
||||
const secondaryMetadata = schemas[1].value.instance['x-ms-metadata'];
|
||||
const primarySchema = this.resolve(this.input.components.schemas[schemas[0].key]);
|
||||
const primaryMetadata = primarySchema.instance["x-ms-metadata"];
|
||||
const secondaryMetadata = schemas[1].value.instance["x-ms-metadata"];
|
||||
|
||||
if (primaryMetadata && secondaryMetadata) {
|
||||
primaryMetadata.apiVersions = [...new Set<string>([...primaryMetadata.apiVersions || [], ...secondaryMetadata.apiVersions || []])]
|
||||
primaryMetadata.filename = [...new Set<string>([...primaryMetadata.filename || [], ...secondaryMetadata.filename || []])]
|
||||
primaryMetadata.originalLocations = [...new Set<string>([...primaryMetadata.originalLocations || [], ...secondaryMetadata.originalLocations || []])]
|
||||
primaryMetadata['x-ms-secondary-file'] = !(!primaryMetadata['x-ms-secondary-file'] || !secondaryMetadata['x-ms-secondary-file'])
|
||||
primaryMetadata.apiVersions = [
|
||||
...new Set<string>([
|
||||
...(primaryMetadata.apiVersions || []),
|
||||
...(secondaryMetadata.apiVersions || []),
|
||||
]),
|
||||
];
|
||||
primaryMetadata.filename = [
|
||||
...new Set<string>([...(primaryMetadata.filename || []), ...(secondaryMetadata.filename || [])]),
|
||||
];
|
||||
primaryMetadata.originalLocations = [
|
||||
...new Set<string>([
|
||||
...(primaryMetadata.originalLocations || []),
|
||||
...(secondaryMetadata.originalLocations || []),
|
||||
]),
|
||||
];
|
||||
primaryMetadata["x-ms-secondary-file"] = !(
|
||||
!primaryMetadata["x-ms-secondary-file"] || !secondaryMetadata["x-ms-secondary-file"]
|
||||
);
|
||||
}
|
||||
}
|
||||
this.session.verbose(`Schema ${name} has multiple identical declarations, reducing to just one - removing ${schemas[1].key} `, ['PreCheck', 'ReducingSchema']);
|
||||
this.session.verbose(
|
||||
`Schema ${name} has multiple identical declarations, reducing to just one - removing ${schemas[1].key} `,
|
||||
["PreCheck", "ReducingSchema"],
|
||||
);
|
||||
|
||||
// Restart the scan now that the duplicate has been removed
|
||||
return true;
|
||||
}
|
||||
|
||||
// it may not be identical, but if it's not an object, I'm not sure we care too much.
|
||||
if (values(schemas).any(each => this.isObjectOrEnum(each.value.instance))) {
|
||||
const rdiff = getDiff(schemas[1].value.instance, schemas[0].value.instance).filter(each => each.path[0] !== 'description' && each.path[0] !== 'x-ms-metadata');
|
||||
if (values(schemas).any((each) => this.isObjectOrEnum(each.value.instance))) {
|
||||
const rdiff = getDiff(schemas[1].value.instance, schemas[0].value.instance).filter(
|
||||
(each) => each.path[0] !== "description" && each.path[0] !== "x-ms-metadata",
|
||||
);
|
||||
if (diff.length > 0) {
|
||||
const details = diff.map(each => {
|
||||
const path = each.path.join('.');
|
||||
let iValue = each.op === 'add' ? '<none>' : JSON.stringify(each.oldVal);
|
||||
if (each.op !== 'update') {
|
||||
const v = rdiff.find(each => each.path.join('.') === path)
|
||||
iValue = JSON.stringify(v?.val);
|
||||
}
|
||||
const nValue = each.op === 'delete' ? '<none>' : JSON.stringify(each.val);
|
||||
return `${path}: ${iValue} => ${nValue}`;
|
||||
}).join(',');
|
||||
const details = diff
|
||||
.map((each) => {
|
||||
const path = each.path.join(".");
|
||||
let iValue = each.op === "add" ? "<none>" : JSON.stringify(each.oldVal);
|
||||
if (each.op !== "update") {
|
||||
const v = rdiff.find((each) => each.path.join(".") === path);
|
||||
iValue = JSON.stringify(v?.val);
|
||||
}
|
||||
const nValue = each.op === "delete" ? "<none>" : JSON.stringify(each.val);
|
||||
return `${path}: ${iValue} => ${nValue}`;
|
||||
})
|
||||
.join(",");
|
||||
errors.add(`Duplicate Schema named ${name} -- ${details} `);
|
||||
continue;
|
||||
}
|
||||
|
@ -257,12 +318,15 @@ export class QualityPreChecker {
|
|||
for (const each of errors) {
|
||||
// Allow duplicate schemas if requested
|
||||
if (!!this.options["lenient-model-deduplication"]) {
|
||||
this.session.warning(each, ['PreCheck', 'DuplicateSchema']);
|
||||
this.session.warning(each, ["PreCheck", "DuplicateSchema"]);
|
||||
} else {
|
||||
this.session.error(`${each}; This error can be *temporarily* avoided by using the 'modelerfour.lenient-model-deduplication' setting. NOTE: This setting will be removed in a future version of @autorest/modelerfour; schemas should be updated to fix this issue sooner than that.`, ['PreCheck', 'DuplicateSchema']);
|
||||
this.session.error(
|
||||
`${each}; This error can be *temporarily* avoided by using the 'modelerfour.lenient-model-deduplication' setting. NOTE: This setting will be removed in a future version of @autorest/modelerfour; schemas should be updated to fix this issue sooner than that.`,
|
||||
["PreCheck", "DuplicateSchema"],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
while (!!innerCheckForDuplicateSchemas()) {
|
||||
// Loop until the scan is complete
|
||||
|
@ -274,7 +338,10 @@ export class QualityPreChecker {
|
|||
fixUpSchemasThatUseAllOfInsteadOfJustRef() {
|
||||
const schemas = this.input.components?.schemas;
|
||||
if (schemas) {
|
||||
for (const { key, instance: schema, name, fromRef } of items(schemas).select(s => ({ key: s.key, ... this.resolve(s.value) }))) {
|
||||
for (const { key, instance: schema, name, fromRef } of items(schemas).select((s) => ({
|
||||
key: s.key,
|
||||
...this.resolve(s.value),
|
||||
}))) {
|
||||
// we're looking for schemas that offer no possible value
|
||||
// because they just use allOf instead of $ref
|
||||
if (!schema.type || schema.type === JsonType.Object) {
|
||||
|
@ -289,15 +356,26 @@ export class QualityPreChecker {
|
|||
const $ref = schema?.allOf?.[0]?.$ref;
|
||||
|
||||
const text = JSON.stringify(this.input);
|
||||
this.input = JSON.parse(text.replace(new RegExp(`"\\#\\/components\\/schemas\\/${key}"`, 'g'), `"${$ref}"`));
|
||||
const location = schema['x-ms-metadata'].originalLocations[0].replace(/^.*\//, '')
|
||||
if (schema['x-internal-autorest-anonymous-schema']) {
|
||||
this.input = JSON.parse(
|
||||
text.replace(new RegExp(`"\\#\\/components\\/schemas\\/${key}"`, "g"), `"${$ref}"`),
|
||||
);
|
||||
const location = schema["x-ms-metadata"].originalLocations[0].replace(/^.*\//, "");
|
||||
if (schema["x-internal-autorest-anonymous-schema"]) {
|
||||
delete schemas[key];
|
||||
this.session.warning(`An anonymous inline schema for property '${location.replace(/-/g, '.')}' is using an 'allOf' instead of a $ref. This creates a wasteful anonymous type when generating code. Don't do that. - removing.`, ['PreCheck', 'AllOfWhenYouMeantRef']);
|
||||
this.session.warning(
|
||||
`An anonymous inline schema for property '${location.replace(
|
||||
/-/g,
|
||||
".",
|
||||
)}' is using an 'allOf' instead of a $ref. This creates a wasteful anonymous type when generating code. Don't do that. - removing.`,
|
||||
["PreCheck", "AllOfWhenYouMeantRef"],
|
||||
);
|
||||
} else {
|
||||
// NOTE: Disabled removing of non-anonymous schema for now until
|
||||
// it has been discussed in Azure/autorest.modelerfour#278
|
||||
this.session.warning(`Schema '${location}' is using an 'allOf' instead of a $ref. This creates a wasteful anonymous type when generating code.`, ['PreCheck', 'AllOfWhenYouMeantRef']);
|
||||
this.session.warning(
|
||||
`Schema '${location}' is using an 'allOf' instead of a $ref. This creates a wasteful anonymous type when generating code.`,
|
||||
["PreCheck", "AllOfWhenYouMeantRef"],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -306,12 +384,18 @@ export class QualityPreChecker {
|
|||
}
|
||||
|
||||
fixUpObjectsWithoutType() {
|
||||
for (const { instance: schema, name, fromRef } of values(this.input.components?.schemas).select(s => this.resolve(s))) {
|
||||
if (<any>schema.type === 'file' || <any>schema.format === 'file' || <any>schema.format === 'binary') {
|
||||
for (const { instance: schema, name, fromRef } of values(this.input.components?.schemas).select((s) =>
|
||||
this.resolve(s),
|
||||
)) {
|
||||
if (<any>schema.type === "file" || <any>schema.format === "file" || <any>schema.format === "binary") {
|
||||
// handle inconsistency in file format handling.
|
||||
this.session.hint(
|
||||
`'The schema ${schema?.['x-ms-metadata']?.name || name} with 'type: ${schema.type}', format: ${schema.format}' will be treated as a binary blob for binary media types.`,
|
||||
['PreCheck', 'BinarySchema'], schema);
|
||||
`'The schema ${schema?.["x-ms-metadata"]?.name || name} with 'type: ${schema.type}', format: ${
|
||||
schema.format
|
||||
}' will be treated as a binary blob for binary media types.`,
|
||||
["PreCheck", "BinarySchema"],
|
||||
schema,
|
||||
);
|
||||
schema.type = JsonType.String;
|
||||
schema.format = StringFormat.Binary;
|
||||
}
|
||||
|
@ -323,14 +407,26 @@ export class QualityPreChecker {
|
|||
// if the model has properties, then we're going to assume they meant to say JsonType.object
|
||||
// but we're going to warn them anyway.
|
||||
|
||||
this.session.warning(`The schema '${schema?.['x-ms-metadata']?.name || name}' with an undefined type and decalared properties is a bit ambigious. This has been auto-corrected to 'type:object'`, ['PreCheck', 'SchemaMissingType'], schema);
|
||||
this.session.warning(
|
||||
`The schema '${
|
||||
schema?.["x-ms-metadata"]?.name || name
|
||||
}' with an undefined type and decalared properties is a bit ambigious. This has been auto-corrected to 'type:object'`,
|
||||
["PreCheck", "SchemaMissingType"],
|
||||
schema,
|
||||
);
|
||||
schema.type = JsonType.Object;
|
||||
break;
|
||||
}
|
||||
if (schema.additionalProperties) {
|
||||
// this looks like it's going to be a dictionary
|
||||
// we'll mark it as object and let the processObjectSchema sort it out.
|
||||
this.session.warning(`The schema '${schema?.['x-ms-metadata']?.name || name}' with an undefined type and additionalProperties is a bit ambigious. This has been auto-corrected to 'type:object'`, ['PreCheck', 'SchemaMissingType'], schema);
|
||||
this.session.warning(
|
||||
`The schema '${
|
||||
schema?.["x-ms-metadata"]?.name || name
|
||||
}' with an undefined type and additionalProperties is a bit ambigious. This has been auto-corrected to 'type:object'`,
|
||||
["PreCheck", "SchemaMissingType"],
|
||||
schema,
|
||||
);
|
||||
schema.type = JsonType.Object;
|
||||
break;
|
||||
}
|
||||
|
@ -338,7 +434,13 @@ export class QualityPreChecker {
|
|||
if (schema.allOf || schema.anyOf || schema.oneOf) {
|
||||
// if the model has properties, then we're going to assume they meant to say JsonType.object
|
||||
// but we're going to warn them anyway.
|
||||
this.session.warning(`The schema '${schema?.['x-ms-metadata']?.name || name}' with an undefined type and 'allOf'/'anyOf'/'oneOf' is a bit ambigious. This has been auto-corrected to 'type:object'`, ['PreCheck', 'SchemaMissingType'], schema);
|
||||
this.session.warning(
|
||||
`The schema '${
|
||||
schema?.["x-ms-metadata"]?.name || name
|
||||
}' with an undefined type and 'allOf'/'anyOf'/'oneOf' is a bit ambigious. This has been auto-corrected to 'type:object'`,
|
||||
["PreCheck", "SchemaMissingType"],
|
||||
schema,
|
||||
);
|
||||
schema.type = JsonType.Object;
|
||||
break;
|
||||
}
|
||||
|
@ -348,9 +450,7 @@ export class QualityPreChecker {
|
|||
}
|
||||
|
||||
isEmptyObjectSchema(schema: Schema): boolean {
|
||||
if (length(schema.properties) > 0 ||
|
||||
length(schema.allOf) > 0 ||
|
||||
schema.additionalProperties === true) {
|
||||
if (length(schema.properties) > 0 || length(schema.allOf) > 0 || schema.additionalProperties === true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -365,15 +465,21 @@ export class QualityPreChecker {
|
|||
fixUpSchemasWithEmptyObjectParent() {
|
||||
const schemas = this.input.components?.schemas;
|
||||
if (schemas) {
|
||||
for (const { key, instance: schema, name, fromRef } of items(schemas).select(s => ({ key: s.key, ... this.resolve(s.value) }))) {
|
||||
for (const { key, instance: schema, name, fromRef } of items(schemas).select((s) => ({
|
||||
key: s.key,
|
||||
...this.resolve(s.value),
|
||||
}))) {
|
||||
if (schema.type === JsonType.Object) {
|
||||
if (length(schema.allOf) > 1) {
|
||||
const schemaName = schema['x-ms-metadata']?.name || name;
|
||||
schema.allOf = schema.allOf?.filter(p => {
|
||||
const schemaName = schema["x-ms-metadata"]?.name || name;
|
||||
schema.allOf = schema.allOf?.filter((p) => {
|
||||
const parent = this.resolve(p).instance;
|
||||
|
||||
if (this.isEmptyObjectSchema(parent)) {
|
||||
this.session.warning(`Schema '${schemaName}' has an allOf list with an empty object schema as a parent, removing it.`, ['PreCheck', 'EmptyParentSchemaWarning']);
|
||||
this.session.warning(
|
||||
`Schema '${schemaName}' has an allOf list with an empty object schema as a parent, removing it.`,
|
||||
["PreCheck", "EmptyParentSchemaWarning"],
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -385,10 +491,8 @@ export class QualityPreChecker {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
process() {
|
||||
|
||||
this.fixUpSchemasThatUseAllOfInsteadOfJustRef()
|
||||
this.fixUpSchemasThatUseAllOfInsteadOfJustRef();
|
||||
|
||||
this.fixUpObjectsWithoutType();
|
||||
|
||||
|
@ -397,17 +501,19 @@ export class QualityPreChecker {
|
|||
this.checkForDuplicateSchemas();
|
||||
|
||||
let onlyOnce = new WeakSet<Schema>();
|
||||
for (const { instance: schema, name, fromRef } of values(this.input.components?.schemas).select(s => this.resolve(s))) {
|
||||
for (const { instance: schema, name, fromRef } of values(this.input.components?.schemas).select((s) =>
|
||||
this.resolve(s),
|
||||
)) {
|
||||
this.checkForHiddenProperties(this.interpret.getName(name, schema), schema, onlyOnce);
|
||||
}
|
||||
|
||||
onlyOnce = new WeakSet<Schema>();
|
||||
for (const { instance: schema, name, fromRef } of values(this.input.components?.schemas).select(s => this.resolve(s))) {
|
||||
for (const { instance: schema, name, fromRef } of values(this.input.components?.schemas).select((s) =>
|
||||
this.resolve(s),
|
||||
)) {
|
||||
this.checkForDuplicateParents(this.interpret.getName(name, schema), schema, onlyOnce);
|
||||
}
|
||||
|
||||
|
||||
return this.input;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,10 +3,8 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { suite, test } from 'mocha-typescript';
|
||||
import * as assert from 'assert';
|
||||
import { suite, test } from "mocha-typescript";
|
||||
import * as assert from "assert";
|
||||
|
||||
@suite class TestEnumNameInterpretation {
|
||||
|
||||
|
||||
}
|
||||
@suite
|
||||
class TestEnumNameInterpretation {}
|
||||
|
|
|
@ -3,24 +3,24 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { suite, test } from 'mocha-typescript';
|
||||
import * as assert from 'assert';
|
||||
import { readFile, readdir, isDirectory } from '@azure-tools/async-io';
|
||||
import { deserialize, fail, serialize } from '@azure-tools/codegen';
|
||||
import { startSession, Message, Channel } from '@azure-tools/autorest-extension-base';
|
||||
import { values, keys, length } from '@azure-tools/linq';
|
||||
import { Model } from '@azure-tools/openapi';
|
||||
import chalk from 'chalk';
|
||||
import { QualityPreChecker } from '../quality-precheck/prechecker';
|
||||
import { suite, test } from "mocha-typescript";
|
||||
import * as assert from "assert";
|
||||
import { readFile, readdir, isDirectory } from "@azure-tools/async-io";
|
||||
import { deserialize, fail, serialize } from "@azure-tools/codegen";
|
||||
import { startSession, Message, Channel } from "@azure-tools/autorest-extension-base";
|
||||
import { values, keys, length } from "@azure-tools/linq";
|
||||
import { Model } from "@azure-tools/openapi";
|
||||
import chalk from "chalk";
|
||||
import { QualityPreChecker } from "../quality-precheck/prechecker";
|
||||
|
||||
require('source-map-support').install();
|
||||
require("source-map-support").install();
|
||||
|
||||
function addStyle(style: string, text: string): string {
|
||||
return `▌PUSH:${style}▐${text}▌POP▐`;
|
||||
}
|
||||
function compileStyledText(text: string): string {
|
||||
const styleStack = ['(x => x)'];
|
||||
let result = '';
|
||||
const styleStack = ["(x => x)"];
|
||||
let result = "";
|
||||
let consumedUpTo = 0;
|
||||
const appendPart = (end: number) => {
|
||||
const CHALK = chalk;
|
||||
|
@ -31,10 +31,10 @@ function compileStyledText(text: string): string {
|
|||
const commandRegex = /▌(.+?)▐/g;
|
||||
let i: RegExpExecArray | null;
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
while (i = commandRegex.exec(text)) {
|
||||
while ((i = commandRegex.exec(text))) {
|
||||
const startIndex = i.index;
|
||||
const length = i[0].length;
|
||||
const command = i[1].split(':');
|
||||
const command = i[1].split(":");
|
||||
|
||||
// append up to here with current style
|
||||
appendPart(startIndex);
|
||||
|
@ -42,10 +42,10 @@ function compileStyledText(text: string): string {
|
|||
// process command
|
||||
consumedUpTo += length;
|
||||
switch (command[0]) {
|
||||
case 'PUSH':
|
||||
styleStack.push('CHALK.' + command[1]);
|
||||
case "PUSH":
|
||||
styleStack.push("CHALK." + command[1]);
|
||||
break;
|
||||
case 'POP':
|
||||
case "POP":
|
||||
styleStack.pop();
|
||||
break;
|
||||
}
|
||||
|
@ -55,28 +55,36 @@ function compileStyledText(text: string): string {
|
|||
}
|
||||
|
||||
export function color(text: string): string {
|
||||
return compileStyledText(text.
|
||||
replace(/\*\*(.*?)\*\*/gm, addStyle('bold', '$1')).
|
||||
replace(/(\[.*?s\])/gm, addStyle('yellow.bold', '$1')).
|
||||
replace(/^# (.*)/gm, addStyle('greenBright', '$1')).
|
||||
replace(/^## (.*)/gm, addStyle('green', '$1')).
|
||||
replace(/^### (.*)/gm, addStyle('cyanBright', '$1')).
|
||||
replace(/(https?:\/\/\S*)/gmi, addStyle('blue.bold.underline', '$1')).
|
||||
replace(/__(.*)__/gm, addStyle('italic', '$1')).
|
||||
replace(/^>(.*)/gm, addStyle('cyan', ' $1')).
|
||||
replace(/^!(.*)/gm, addStyle('red.bold', ' $1')).
|
||||
replace(/^(ERROR) (.*?):?(.*)/gmi, `${addStyle('red.bold', '$1')} ${addStyle('green', '$2')}:$3`).
|
||||
replace(/^(WARNING) (.*?):?(.*)/gmi, `${addStyle('yellow.bold', '$1')} ${addStyle('green', '$2')}:$3`).
|
||||
replace(/^(\s* - \w*:\/\/\S*):(\d*):(\d*) (.*)/gm, `${addStyle('cyan', '$1')}:${addStyle('cyan.bold', '$2')}:${addStyle('cyan.bold', '$3')} $4`).
|
||||
replace(/`(.+?)`/gm, addStyle('gray', '$1')).
|
||||
replace(/"(.*?)"/gm, addStyle('gray', '"$1"')).
|
||||
replace(/'(.*?)'/gm, addStyle('gray', '\'$1\'')));
|
||||
return compileStyledText(
|
||||
text
|
||||
.replace(/\*\*(.*?)\*\*/gm, addStyle("bold", "$1"))
|
||||
.replace(/(\[.*?s\])/gm, addStyle("yellow.bold", "$1"))
|
||||
.replace(/^# (.*)/gm, addStyle("greenBright", "$1"))
|
||||
.replace(/^## (.*)/gm, addStyle("green", "$1"))
|
||||
.replace(/^### (.*)/gm, addStyle("cyanBright", "$1"))
|
||||
.replace(/(https?:\/\/\S*)/gim, addStyle("blue.bold.underline", "$1"))
|
||||
.replace(/__(.*)__/gm, addStyle("italic", "$1"))
|
||||
.replace(/^>(.*)/gm, addStyle("cyan", " $1"))
|
||||
.replace(/^!(.*)/gm, addStyle("red.bold", " $1"))
|
||||
.replace(/^(ERROR) (.*?):?(.*)/gim, `${addStyle("red.bold", "$1")} ${addStyle("green", "$2")}:$3`)
|
||||
.replace(/^(WARNING) (.*?):?(.*)/gim, `${addStyle("yellow.bold", "$1")} ${addStyle("green", "$2")}:$3`)
|
||||
.replace(
|
||||
/^(\s* - \w*:\/\/\S*):(\d*):(\d*) (.*)/gm,
|
||||
`${addStyle("cyan", "$1")}:${addStyle("cyan.bold", "$2")}:${addStyle("cyan.bold", "$3")} $4`,
|
||||
)
|
||||
.replace(/`(.+?)`/gm, addStyle("gray", "$1"))
|
||||
.replace(/"(.*?)"/gm, addStyle("gray", '"$1"'))
|
||||
.replace(/'(.*?)'/gm, addStyle("gray", "'$1'")),
|
||||
);
|
||||
}
|
||||
(<any>global).color = color;
|
||||
|
||||
let unexpectedErrorCount = 0;
|
||||
|
||||
async function readData(folder: string, ...files: Array<string>): Promise<Array<{ model: any; filename: string; content: string }>> {
|
||||
async function readData(
|
||||
folder: string,
|
||||
...files: Array<string>
|
||||
): Promise<Array<{ model: any; filename: string; content: string }>> {
|
||||
const results = [];
|
||||
for (const filename of files) {
|
||||
const content = await readFile(`${folder}/${filename}`);
|
||||
|
@ -84,42 +92,56 @@ async function readData(folder: string, ...files: Array<string>): Promise<Array<
|
|||
results.push({
|
||||
model,
|
||||
filename,
|
||||
content
|
||||
content,
|
||||
});
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
async function createTestSession<TInputModel>(config: any, folder: string, inputs: Array<string>, outputs: Array<string>) {
|
||||
async function createTestSession<TInputModel>(
|
||||
config: any,
|
||||
folder: string,
|
||||
inputs: Array<string>,
|
||||
outputs: Array<string>,
|
||||
) {
|
||||
const filesInFolder = await readData(folder, ...inputs);
|
||||
|
||||
const expected = <any>deserialize((values(filesInFolder).first(each => each.filename === 'expected.yaml') || fail(`missing test file 'expected.yaml' in ${folder}`)).content, 'expected.yaml')
|
||||
const expected = <any>(
|
||||
deserialize(
|
||||
(
|
||||
values(filesInFolder).first((each) => each.filename === "expected.yaml") ||
|
||||
fail(`missing test file 'expected.yaml' in ${folder}`)
|
||||
).content,
|
||||
"expected.yaml",
|
||||
)
|
||||
);
|
||||
const unexpected = <any>{};
|
||||
|
||||
return {
|
||||
session: await startSession<TInputModel>({
|
||||
ReadFile: async (filename: string): Promise<string> => (values(filesInFolder).first(each => each.filename === filename) || fail(`missing input '${filename}'`)).content,
|
||||
ReadFile: async (filename: string): Promise<string> =>
|
||||
(values(filesInFolder).first((each) => each.filename === filename) || fail(`missing input '${filename}'`))
|
||||
.content,
|
||||
GetValue: async (key: string): Promise<any> => {
|
||||
if (!key) {
|
||||
return config;
|
||||
}
|
||||
return config[key];
|
||||
},
|
||||
ListInputs: async (artifactType?: string): Promise<Array<string>> => filesInFolder.map(each => each.filename),
|
||||
ListInputs: async (artifactType?: string): Promise<Array<string>> => filesInFolder.map((each) => each.filename),
|
||||
|
||||
ProtectFiles: async (path: string): Promise<void> => {
|
||||
// test
|
||||
// test
|
||||
},
|
||||
WriteFile: (filename: string, content: string, sourceMap?: any, artifactType?: string): void => {
|
||||
// test
|
||||
// test
|
||||
},
|
||||
Message: (message: Message): void => {
|
||||
if (expected[message.Channel]) {
|
||||
const i = expected[message.Channel].indexOf(message.Text.trim());
|
||||
|
||||
if (i > -1) {
|
||||
// expected message found. remove it
|
||||
// expected message found. remove it
|
||||
expected[message.Channel].splice(i, 1);
|
||||
if (expected[message.Channel].length === 0) {
|
||||
delete expected[message.Channel];
|
||||
|
@ -133,35 +155,40 @@ async function createTestSession<TInputModel>(config: any, folder: string, input
|
|||
unexpected[message.Channel].push(message.Text);
|
||||
},
|
||||
UpdateConfigurationFile: (filename: string, content: string): void => {
|
||||
// test
|
||||
// test
|
||||
},
|
||||
GetConfigurationFile: async (filename: string): Promise<string> => '',
|
||||
GetConfigurationFile: async (filename: string): Promise<string> => "",
|
||||
}),
|
||||
expected,
|
||||
unexpected
|
||||
unexpected,
|
||||
};
|
||||
}
|
||||
|
||||
@suite class Process {
|
||||
@suite
|
||||
class Process {
|
||||
// to generate the oai3 doc from an oai2 input:
|
||||
// autorest --pipeline-model:v3 --input-file:./oai2.loaded.json --output-folder:. --verbose --debug --no-network-check ../test-configuration.md
|
||||
|
||||
@test async 'error-checks'() {
|
||||
@test async "error-checks"() {
|
||||
const folders = await readdir(`${__dirname}/../../test/errors/`);
|
||||
for (const each of folders) {
|
||||
const folder = `${__dirname}/../../test/errors/${each}`;
|
||||
|
||||
if (! await isDirectory(folder)) {
|
||||
if (!(await isDirectory(folder))) {
|
||||
continue;
|
||||
}
|
||||
console.log(`Expecting Errors From: ${folder}`);
|
||||
|
||||
const cfg = {
|
||||
modelerfour: {
|
||||
}
|
||||
}
|
||||
modelerfour: {},
|
||||
};
|
||||
|
||||
const { session, expected, unexpected } = await createTestSession<Model>(cfg, folder, ['openapi-document.json', 'expected.yaml'], []);
|
||||
const { session, expected, unexpected } = await createTestSession<Model>(
|
||||
cfg,
|
||||
folder,
|
||||
["openapi-document.json", "expected.yaml"],
|
||||
[],
|
||||
);
|
||||
|
||||
// process OAI model
|
||||
const prechecker = await new QualityPreChecker(session).init();
|
||||
|
@ -169,9 +196,19 @@ async function createTestSession<TInputModel>(config: any, folder: string, input
|
|||
// go!
|
||||
await prechecker.process();
|
||||
|
||||
assert(unexpectedErrorCount === 0, `Unexpected messages encountered -- these should be in the 'unexpected.yaml' file:\n\n=========\n\n${serialize(unexpected)}\n\n=========`);
|
||||
assert(
|
||||
unexpectedErrorCount === 0,
|
||||
`Unexpected messages encountered -- these should be in the 'unexpected.yaml' file:\n\n=========\n\n${serialize(
|
||||
unexpected,
|
||||
)}\n\n=========`,
|
||||
);
|
||||
|
||||
assert(length(expected) === 0, `Did not hit expected messages -- the following are present in the 'expected.yaml' file, but not hit: \n\n=========\n\n${serialize(expected)}\n\n=========`);
|
||||
assert(
|
||||
length(expected) === 0,
|
||||
`Did not hit expected messages -- the following are present in the 'expected.yaml' file, but not hit: \n\n=========\n\n${serialize(
|
||||
expected,
|
||||
)}\n\n=========`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,34 +3,31 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { suite, test } from 'mocha-typescript';
|
||||
import * as assert from 'assert';
|
||||
import { ModelerFour } from '../modeler/modelerfour';
|
||||
import { readFile, writeFile, readdir, mkdir } from '@azure-tools/async-io';
|
||||
import { deserialize, serialize, fail } from '@azure-tools/codegen';
|
||||
import { startSession } from '@azure-tools/autorest-extension-base';
|
||||
import { values } from '@azure-tools/linq';
|
||||
import { CodeModel } from '@azure-tools/codemodel';
|
||||
import { Model } from '@azure-tools/openapi';
|
||||
import { codeModelSchema } from '@azure-tools/codemodel';
|
||||
import { ReadUri } from '@azure-tools/uri';
|
||||
import { PreNamer } from '../prenamer/prenamer';
|
||||
import { Flattener } from '../flattener/flattener';
|
||||
import { Grouper } from '../grouper/grouper';
|
||||
import { Checker } from '../checker/checker';
|
||||
import chalk from 'chalk';
|
||||
|
||||
require('source-map-support').install();
|
||||
|
||||
|
||||
import { suite, test } from "mocha-typescript";
|
||||
import * as assert from "assert";
|
||||
import { ModelerFour } from "../modeler/modelerfour";
|
||||
import { readFile, writeFile, readdir, mkdir } from "@azure-tools/async-io";
|
||||
import { deserialize, serialize, fail } from "@azure-tools/codegen";
|
||||
import { startSession } from "@azure-tools/autorest-extension-base";
|
||||
import { values } from "@azure-tools/linq";
|
||||
import { CodeModel } from "@azure-tools/codemodel";
|
||||
import { Model } from "@azure-tools/openapi";
|
||||
import { codeModelSchema } from "@azure-tools/codemodel";
|
||||
import { ReadUri } from "@azure-tools/uri";
|
||||
import { PreNamer } from "../prenamer/prenamer";
|
||||
import { Flattener } from "../flattener/flattener";
|
||||
import { Grouper } from "../grouper/grouper";
|
||||
import { Checker } from "../checker/checker";
|
||||
import chalk from "chalk";
|
||||
|
||||
require("source-map-support").install();
|
||||
|
||||
function addStyle(style: string, text: string): string {
|
||||
return `▌PUSH:${style}▐${text}▌POP▐`;
|
||||
}
|
||||
function compileStyledText(text: string): string {
|
||||
const styleStack = ['(x => x)'];
|
||||
let result = '';
|
||||
const styleStack = ["(x => x)"];
|
||||
let result = "";
|
||||
let consumedUpTo = 0;
|
||||
const appendPart = (end: number) => {
|
||||
const CHALK = chalk;
|
||||
|
@ -41,10 +38,10 @@ function compileStyledText(text: string): string {
|
|||
const commandRegex = /▌(.+?)▐/g;
|
||||
let i: RegExpExecArray | null;
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
while (i = commandRegex.exec(text)) {
|
||||
while ((i = commandRegex.exec(text))) {
|
||||
const startIndex = i.index;
|
||||
const length = i[0].length;
|
||||
const command = i[1].split(':');
|
||||
const command = i[1].split(":");
|
||||
|
||||
// append up to here with current style
|
||||
appendPart(startIndex);
|
||||
|
@ -52,10 +49,10 @@ function compileStyledText(text: string): string {
|
|||
// process command
|
||||
consumedUpTo += length;
|
||||
switch (command[0]) {
|
||||
case 'PUSH':
|
||||
styleStack.push('CHALK.' + command[1]);
|
||||
case "PUSH":
|
||||
styleStack.push("CHALK." + command[1]);
|
||||
break;
|
||||
case 'POP':
|
||||
case "POP":
|
||||
styleStack.pop();
|
||||
break;
|
||||
}
|
||||
|
@ -65,22 +62,27 @@ function compileStyledText(text: string): string {
|
|||
}
|
||||
|
||||
export function color(text: string): string {
|
||||
return compileStyledText(text.
|
||||
replace(/\*\*(.*?)\*\*/gm, addStyle('bold', '$1')).
|
||||
replace(/(\[.*?s\])/gm, addStyle('yellow.bold', '$1')).
|
||||
replace(/^# (.*)/gm, addStyle('greenBright', '$1')).
|
||||
replace(/^## (.*)/gm, addStyle('green', '$1')).
|
||||
replace(/^### (.*)/gm, addStyle('cyanBright', '$1')).
|
||||
replace(/(https?:\/\/\S*)/gmi, addStyle('blue.bold.underline', '$1')).
|
||||
replace(/__(.*)__/gm, addStyle('italic', '$1')).
|
||||
replace(/^>(.*)/gm, addStyle('cyan', ' $1')).
|
||||
replace(/^!(.*)/gm, addStyle('red.bold', ' $1')).
|
||||
replace(/^(ERROR) (.*?):?(.*)/gmi, `${addStyle('red.bold', '$1')} ${addStyle('green', '$2')}:$3`).
|
||||
replace(/^(WARNING) (.*?):?(.*)/gmi, `${addStyle('yellow.bold', '$1')} ${addStyle('green', '$2')}:$3`).
|
||||
replace(/^(\s* - \w*:\/\/\S*):(\d*):(\d*) (.*)/gm, `${addStyle('cyan', '$1')}:${addStyle('cyan.bold', '$2')}:${addStyle('cyan.bold', '$3')} $4`).
|
||||
replace(/`(.+?)`/gm, addStyle('gray', '$1')).
|
||||
replace(/"(.*?)"/gm, addStyle('gray', '"$1"')).
|
||||
replace(/'(.*?)'/gm, addStyle('gray', '\'$1\'')));
|
||||
return compileStyledText(
|
||||
text
|
||||
.replace(/\*\*(.*?)\*\*/gm, addStyle("bold", "$1"))
|
||||
.replace(/(\[.*?s\])/gm, addStyle("yellow.bold", "$1"))
|
||||
.replace(/^# (.*)/gm, addStyle("greenBright", "$1"))
|
||||
.replace(/^## (.*)/gm, addStyle("green", "$1"))
|
||||
.replace(/^### (.*)/gm, addStyle("cyanBright", "$1"))
|
||||
.replace(/(https?:\/\/\S*)/gim, addStyle("blue.bold.underline", "$1"))
|
||||
.replace(/__(.*)__/gm, addStyle("italic", "$1"))
|
||||
.replace(/^>(.*)/gm, addStyle("cyan", " $1"))
|
||||
.replace(/^!(.*)/gm, addStyle("red.bold", " $1"))
|
||||
.replace(/^(ERROR) (.*?):?(.*)/gim, `${addStyle("red.bold", "$1")} ${addStyle("green", "$2")}:$3`)
|
||||
.replace(/^(WARNING) (.*?):?(.*)/gim, `${addStyle("yellow.bold", "$1")} ${addStyle("green", "$2")}:$3`)
|
||||
.replace(
|
||||
/^(\s* - \w*:\/\/\S*):(\d*):(\d*) (.*)/gm,
|
||||
`${addStyle("cyan", "$1")}:${addStyle("cyan.bold", "$2")}:${addStyle("cyan.bold", "$3")} $4`,
|
||||
)
|
||||
.replace(/`(.+?)`/gm, addStyle("gray", "$1"))
|
||||
.replace(/"(.*?)"/gm, addStyle("gray", '"$1"'))
|
||||
.replace(/'(.*?)'/gm, addStyle("gray", "'$1'")),
|
||||
);
|
||||
}
|
||||
(<any>global).color = color;
|
||||
|
||||
|
@ -88,7 +90,10 @@ let errorCount = 0;
|
|||
|
||||
const resources = `${__dirname}/../../test/resources/process`;
|
||||
|
||||
async function readData(folder: string, ...files: Array<string>): Promise<Array<{ model: any; filename: string; content: string }>> {
|
||||
async function readData(
|
||||
folder: string,
|
||||
...files: Array<string>
|
||||
): Promise<Array<{ model: any; filename: string; content: string }>> {
|
||||
const results = [];
|
||||
for (const filename of files) {
|
||||
const content = await readFile(`${folder}/${filename}`);
|
||||
|
@ -96,128 +101,138 @@ async function readData(folder: string, ...files: Array<string>): Promise<Array<
|
|||
results.push({
|
||||
model,
|
||||
filename,
|
||||
content
|
||||
content,
|
||||
});
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
async function cts<TInputModel>(config: any, filename: string, content: string) {
|
||||
const ii = [{
|
||||
model: deserialize<any>(content, filename),
|
||||
filename,
|
||||
content
|
||||
}];
|
||||
const ii = [
|
||||
{
|
||||
model: deserialize<any>(content, filename),
|
||||
filename,
|
||||
content,
|
||||
},
|
||||
];
|
||||
|
||||
return await startSession<TInputModel>({
|
||||
ReadFile: async (filename: string): Promise<string> => (values(ii).first(each => each.filename === filename) || fail(`missing input '${filename}'`)).content,
|
||||
ReadFile: async (filename: string): Promise<string> =>
|
||||
(values(ii).first((each) => each.filename === filename) || fail(`missing input '${filename}'`)).content,
|
||||
GetValue: async (key: string): Promise<any> => {
|
||||
if (!key) {
|
||||
return config;
|
||||
}
|
||||
return config[key];
|
||||
},
|
||||
ListInputs: async (artifactType?: string): Promise<Array<string>> => ii.map(each => each.filename),
|
||||
ListInputs: async (artifactType?: string): Promise<Array<string>> => ii.map((each) => each.filename),
|
||||
|
||||
ProtectFiles: async (path: string): Promise<void> => {
|
||||
// test
|
||||
// test
|
||||
},
|
||||
WriteFile: (filename: string, content: string, sourceMap?: any, artifactType?: string): void => {
|
||||
// test
|
||||
// test
|
||||
},
|
||||
Message: (message: any): void => {
|
||||
// test
|
||||
if (message.Channel === 'warning' || message.Channel === 'error' || message.Channel === 'verbose') {
|
||||
if (message.Channel === 'error') {
|
||||
// test
|
||||
if (message.Channel === "warning" || message.Channel === "error" || message.Channel === "verbose") {
|
||||
if (message.Channel === "error") {
|
||||
errorCount++;
|
||||
}
|
||||
console.error(color(`${message.Channel} ${message.Text}`));
|
||||
}
|
||||
|
||||
},
|
||||
UpdateConfigurationFile: (filename: string, content: string): void => {
|
||||
// test
|
||||
// test
|
||||
},
|
||||
GetConfigurationFile: async (filename: string): Promise<string> => '',
|
||||
GetConfigurationFile: async (filename: string): Promise<string> => "",
|
||||
});
|
||||
}
|
||||
|
||||
async function createTestSession<TInputModel>(config: any, folder: string, inputs: Array<string>, outputs: Array<string>) {
|
||||
async function createTestSession<TInputModel>(
|
||||
config: any,
|
||||
folder: string,
|
||||
inputs: Array<string>,
|
||||
outputs: Array<string>,
|
||||
) {
|
||||
const ii = await readData(folder, ...inputs);
|
||||
const oo = await readData(folder, ...outputs);
|
||||
|
||||
return await startSession<TInputModel>({
|
||||
ReadFile: async (filename: string): Promise<string> => (values(ii).first(each => each.filename === filename) || fail(`missing input '${filename}'`)).content,
|
||||
ReadFile: async (filename: string): Promise<string> =>
|
||||
(values(ii).first((each) => each.filename === filename) || fail(`missing input '${filename}'`)).content,
|
||||
GetValue: async (key: string): Promise<any> => {
|
||||
if (!key) {
|
||||
return config;
|
||||
}
|
||||
return config[key];
|
||||
},
|
||||
ListInputs: async (artifactType?: string): Promise<Array<string>> => ii.map(each => each.filename),
|
||||
ListInputs: async (artifactType?: string): Promise<Array<string>> => ii.map((each) => each.filename),
|
||||
|
||||
ProtectFiles: async (path: string): Promise<void> => {
|
||||
// test
|
||||
// test
|
||||
},
|
||||
WriteFile: (filename: string, content: string, sourceMap?: any, artifactType?: string): void => {
|
||||
// test
|
||||
// test
|
||||
},
|
||||
Message: (message: any): void => {
|
||||
// test
|
||||
if (message.Channel === 'warning' || message.Channel === 'error' || message.Channel === 'verbose') {
|
||||
if (message.Channel === 'error') {
|
||||
|
||||
// test
|
||||
if (message.Channel === "warning" || message.Channel === "error" || message.Channel === "verbose") {
|
||||
if (message.Channel === "error") {
|
||||
errorCount++;
|
||||
}
|
||||
console.error(color(`${message.Channel} ${message.Text}`));
|
||||
}
|
||||
},
|
||||
UpdateConfigurationFile: (filename: string, content: string): void => {
|
||||
// test
|
||||
// test
|
||||
},
|
||||
GetConfigurationFile: async (filename: string): Promise<string> => '',
|
||||
GetConfigurationFile: async (filename: string): Promise<string> => "",
|
||||
});
|
||||
}
|
||||
|
||||
async function createPassThruSession(config: any, input: string, inputArtifactType: string) {
|
||||
return await startSession<CodeModel>({
|
||||
ReadFile: async (filename: string): Promise<string> => input,
|
||||
GetValue: async (key: string): Promise<any> => {
|
||||
if (!key) {
|
||||
return config;
|
||||
}
|
||||
return config[key];
|
||||
},
|
||||
ListInputs: async (artifactType?: string): Promise<Array<string>> => [inputArtifactType],
|
||||
|
||||
ProtectFiles: async (path: string): Promise<void> => {
|
||||
// test
|
||||
},
|
||||
WriteFile: (filename: string, content: string, sourceMap?: any, artifactType?: string): void => {
|
||||
// test
|
||||
},
|
||||
Message: (message: any): void => {
|
||||
// test
|
||||
if (message.Channel === 'warning' || message.Channel === 'error' || message.Channel === 'verbose') {
|
||||
if (message.Channel === 'error') {
|
||||
errorCount++;
|
||||
return await startSession<CodeModel>(
|
||||
{
|
||||
ReadFile: async (filename: string): Promise<string> => input,
|
||||
GetValue: async (key: string): Promise<any> => {
|
||||
if (!key) {
|
||||
return config;
|
||||
}
|
||||
console.error(color(`${message.Channel} ${message.Text}`));
|
||||
}
|
||||
return config[key];
|
||||
},
|
||||
ListInputs: async (artifactType?: string): Promise<Array<string>> => [inputArtifactType],
|
||||
|
||||
ProtectFiles: async (path: string): Promise<void> => {
|
||||
// test
|
||||
},
|
||||
WriteFile: (filename: string, content: string, sourceMap?: any, artifactType?: string): void => {
|
||||
// test
|
||||
},
|
||||
Message: (message: any): void => {
|
||||
// test
|
||||
if (message.Channel === "warning" || message.Channel === "error" || message.Channel === "verbose") {
|
||||
if (message.Channel === "error") {
|
||||
errorCount++;
|
||||
}
|
||||
console.error(color(`${message.Channel} ${message.Text}`));
|
||||
}
|
||||
},
|
||||
UpdateConfigurationFile: (filename: string, content: string): void => {
|
||||
// test
|
||||
},
|
||||
GetConfigurationFile: async (filename: string): Promise<string> => "",
|
||||
},
|
||||
UpdateConfigurationFile: (filename: string, content: string): void => {
|
||||
// test
|
||||
},
|
||||
GetConfigurationFile: async (filename: string): Promise<string> => '',
|
||||
}, {}, codeModelSchema);
|
||||
{},
|
||||
codeModelSchema,
|
||||
);
|
||||
}
|
||||
|
||||
@suite
|
||||
class Process {
|
||||
@test
|
||||
async 'simple model test'() {
|
||||
const session = await createTestSession<Model>({}, resources, ['input2.yaml'], ['output1.yaml']);
|
||||
async "simple model test"() {
|
||||
const session = await createTestSession<Model>({}, resources, ["input2.yaml"], ["output1.yaml"]);
|
||||
|
||||
// process OAI model
|
||||
const modeler = await new ModelerFour(session).init();
|
||||
|
@ -230,32 +245,32 @@ class Process {
|
|||
|
||||
//await (writeFile(`${__dirname}/../../output.yaml`, yaml));
|
||||
|
||||
const cms = deserialize<CodeModel>(yaml, 'foo.yaml', codeModelSchema);
|
||||
const cms = deserialize<CodeModel>(yaml, "foo.yaml", codeModelSchema);
|
||||
|
||||
assert.strictEqual(true, cms instanceof CodeModel, 'Type Info is maintained in deserialization.');
|
||||
assert.strictEqual(true, cms instanceof CodeModel, "Type Info is maintained in deserialization.");
|
||||
}
|
||||
|
||||
@test
|
||||
async 'acceptance-suite'() {
|
||||
async "acceptance-suite"() {
|
||||
const folders = await readdir(`${__dirname}/../../test/scenarios/`);
|
||||
for (const each of folders) {
|
||||
console.log(`Processing: ${each}`);
|
||||
|
||||
const cfg = {
|
||||
modelerfour: {
|
||||
'flatten-models': true,
|
||||
'flatten-payloads': true,
|
||||
'group-parameters': true,
|
||||
'resolve-schema-name-collisons': true,
|
||||
'additional-checks': true,
|
||||
"modelerfour": {
|
||||
"flatten-models": true,
|
||||
"flatten-payloads": true,
|
||||
"group-parameters": true,
|
||||
"resolve-schema-name-collisons": true,
|
||||
"additional-checks": true,
|
||||
//'always-create-content-type-parameter': true,
|
||||
naming: {
|
||||
"naming": {
|
||||
override: {
|
||||
'$host': '$host',
|
||||
'cmyk': 'CMYK'
|
||||
$host: "$host",
|
||||
cmyk: "CMYK",
|
||||
},
|
||||
local: "_ + camel",
|
||||
constantParameter: 'pascal',
|
||||
constantParameter: "pascal",
|
||||
/*
|
||||
for when playing with python style settings :
|
||||
|
||||
|
@ -268,12 +283,17 @@ class Process {
|
|||
constant: 'uppercase',
|
||||
type: 'pascalcase',
|
||||
// */
|
||||
}
|
||||
},
|
||||
},
|
||||
'payload-flattening-threshold': 2
|
||||
}
|
||||
"payload-flattening-threshold": 2,
|
||||
};
|
||||
|
||||
const session = await createTestSession<Model>(cfg, `${__dirname}/../../test/scenarios/${each}`, ['openapi-document.json'], []);
|
||||
const session = await createTestSession<Model>(
|
||||
cfg,
|
||||
`${__dirname}/../../test/scenarios/${each}`,
|
||||
["openapi-document.json"],
|
||||
[],
|
||||
);
|
||||
|
||||
// process OAI model
|
||||
const modeler = await new ModelerFour(session).init();
|
||||
|
@ -283,24 +303,24 @@ class Process {
|
|||
|
||||
const yaml = serialize(codeModel, codeModelSchema);
|
||||
await mkdir(`${__dirname}/../../test/scenarios/${each}`);
|
||||
await (writeFile(`${__dirname}/../../test/scenarios/${each}/modeler.yaml`, yaml));
|
||||
await writeFile(`${__dirname}/../../test/scenarios/${each}/modeler.yaml`, yaml);
|
||||
|
||||
const flattener = await new Flattener(await createPassThruSession(cfg, yaml, 'code-model-v4')).init();
|
||||
const flattener = await new Flattener(await createPassThruSession(cfg, yaml, "code-model-v4")).init();
|
||||
const flattened = await flattener.process();
|
||||
const flatteneyaml = serialize(flattened, codeModelSchema);
|
||||
await (writeFile(`${__dirname}/../../test/scenarios/${each}/flattened.yaml`, flatteneyaml));
|
||||
await writeFile(`${__dirname}/../../test/scenarios/${each}/flattened.yaml`, flatteneyaml);
|
||||
|
||||
const grouper = await new Grouper(await createPassThruSession(cfg, flatteneyaml, 'code-model-v4')).init();
|
||||
const grouper = await new Grouper(await createPassThruSession(cfg, flatteneyaml, "code-model-v4")).init();
|
||||
const grouped = await grouper.process();
|
||||
const groupedYaml = serialize(grouped, codeModelSchema);
|
||||
await (writeFile(`${__dirname}/../../test/scenarios/${each}/grouped.yaml`, groupedYaml));
|
||||
await writeFile(`${__dirname}/../../test/scenarios/${each}/grouped.yaml`, groupedYaml);
|
||||
|
||||
const namer = await new PreNamer(await createPassThruSession(cfg, groupedYaml, 'code-model-v4')).init();
|
||||
const namer = await new PreNamer(await createPassThruSession(cfg, groupedYaml, "code-model-v4")).init();
|
||||
const named = await namer.process();
|
||||
const namedyaml = serialize(named, codeModelSchema);
|
||||
await (writeFile(`${__dirname}/../../test/scenarios/${each}/namer.yaml`, namedyaml));
|
||||
await writeFile(`${__dirname}/../../test/scenarios/${each}/namer.yaml`, namedyaml);
|
||||
|
||||
const checker = await new Checker(await createPassThruSession(cfg, namedyaml, 'code-model-v4')).init();
|
||||
const checker = await new Checker(await createPassThruSession(cfg, namedyaml, "code-model-v4")).init();
|
||||
await checker.process();
|
||||
|
||||
assert(errorCount === 0, "Errors Encountered");
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -9,7 +9,7 @@ import {
|
|||
Schema,
|
||||
PropertyDetails,
|
||||
JsonType,
|
||||
StringFormat
|
||||
StringFormat,
|
||||
} from "@azure-tools/openapi";
|
||||
import {
|
||||
createTestSession,
|
||||
|
@ -18,7 +18,7 @@ import {
|
|||
addOperation,
|
||||
response,
|
||||
InitialTestSpec,
|
||||
responses
|
||||
responses,
|
||||
} from "./unitTestUtil";
|
||||
|
||||
class PreCheckerClient {
|
||||
|
@ -52,35 +52,29 @@ class PreChecker {
|
|||
nullable: true,
|
||||
properties: {
|
||||
hack: {
|
||||
type: "boolean"
|
||||
}
|
||||
}
|
||||
type: "boolean",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
addSchema(spec, "ChildSchema", {
|
||||
type: "object",
|
||||
allOf: [
|
||||
{ type: "object" },
|
||||
{ $ref: "#/components/schemas/ParentSchema" }
|
||||
],
|
||||
allOf: [{ type: "object" }, { $ref: "#/components/schemas/ParentSchema" }],
|
||||
properties: {
|
||||
childOfHack: {
|
||||
type: "integer"
|
||||
}
|
||||
}
|
||||
type: "integer",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const client = await PreCheckerClient.create(spec);
|
||||
const model = client.result;
|
||||
|
||||
const childSchemaRef =
|
||||
model.components?.schemas && model.components?.schemas["ChildSchema"];
|
||||
const childSchemaRef = model.components?.schemas && model.components?.schemas["ChildSchema"];
|
||||
if (childSchemaRef) {
|
||||
const childSchema = client.resolve<Schema>(childSchemaRef);
|
||||
assert.strictEqual(childSchema.instance.allOf?.length, 1);
|
||||
const parent = client.resolve(
|
||||
childSchema.instance.allOf && childSchema.instance.allOf[0]
|
||||
);
|
||||
const parent = client.resolve(childSchema.instance.allOf && childSchema.instance.allOf[0]);
|
||||
assert.strictEqual(parent.name, "ParentSchema");
|
||||
} else {
|
||||
assert.fail("No 'ChildSchema' found!");
|
||||
|
|
|
@ -4,54 +4,37 @@ import { values, clone } from "@azure-tools/linq";
|
|||
import { Model } from "@azure-tools/openapi";
|
||||
import { codeModelSchema } from "@azure-tools/codemodel";
|
||||
|
||||
export async function createTestSession(
|
||||
config: any,
|
||||
openApiModel: any,
|
||||
messageList: any[]
|
||||
) {
|
||||
export async function createTestSession(config: any, openApiModel: any, messageList: any[]) {
|
||||
const openApiText = JSON.stringify(openApiModel);
|
||||
const ii = [
|
||||
{
|
||||
model: openApiModel as Model,
|
||||
filename: "openapi-3.json",
|
||||
content: openApiText
|
||||
}
|
||||
content: openApiText,
|
||||
},
|
||||
];
|
||||
|
||||
return await startSession<Model>(
|
||||
{
|
||||
ReadFile: async (filename: string): Promise<string> =>
|
||||
(
|
||||
values(ii).first(each => each.filename === filename) ||
|
||||
fail(`missing input '${filename}'`)
|
||||
).content,
|
||||
(values(ii).first((each) => each.filename === filename) || fail(`missing input '${filename}'`)).content,
|
||||
GetValue: async (key: string): Promise<any> => {
|
||||
if (!key) {
|
||||
return config;
|
||||
}
|
||||
return config[key];
|
||||
},
|
||||
ListInputs: async (artifactType?: string): Promise<Array<string>> =>
|
||||
ii.map(each => each.filename),
|
||||
ListInputs: async (artifactType?: string): Promise<Array<string>> => ii.map((each) => each.filename),
|
||||
|
||||
ProtectFiles: async (path: string): Promise<void> => {
|
||||
// test
|
||||
},
|
||||
WriteFile: (
|
||||
filename: string,
|
||||
content: string,
|
||||
sourceMap?: any,
|
||||
artifactType?: string
|
||||
): void => {
|
||||
WriteFile: (filename: string, content: string, sourceMap?: any, artifactType?: string): void => {
|
||||
// test
|
||||
},
|
||||
Message: (message: any): void => {
|
||||
// test
|
||||
if (
|
||||
message.Channel === "warning" ||
|
||||
message.Channel === "error" ||
|
||||
message.Channel === "verbose"
|
||||
) {
|
||||
if (message.Channel === "warning" || message.Channel === "error" || message.Channel === "verbose") {
|
||||
if (message.Channel === "error") {
|
||||
messageList.push(message);
|
||||
}
|
||||
|
@ -60,10 +43,10 @@ export async function createTestSession(
|
|||
UpdateConfigurationFile: (filename: string, content: string): void => {
|
||||
// test
|
||||
},
|
||||
GetConfigurationFile: async (filename: string): Promise<string> => ""
|
||||
GetConfigurationFile: async (filename: string): Promise<string> => "",
|
||||
},
|
||||
{},
|
||||
codeModelSchema
|
||||
codeModelSchema,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -72,26 +55,23 @@ export function response(
|
|||
contentType: string,
|
||||
schema: any,
|
||||
description: string = "The response.",
|
||||
extraProperties?: any
|
||||
extraProperties?: any,
|
||||
) {
|
||||
return {
|
||||
[code]: {
|
||||
description,
|
||||
content: {
|
||||
[contentType]: {
|
||||
schema
|
||||
}
|
||||
schema,
|
||||
},
|
||||
},
|
||||
...extraProperties
|
||||
}
|
||||
...extraProperties,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function responses(...responses: any[]) {
|
||||
return responses.reduce(
|
||||
(responsesDict, response) => Object.assign(responsesDict, response),
|
||||
{}
|
||||
);
|
||||
return responses.reduce((responsesDict, response) => Object.assign(responsesDict, response), {});
|
||||
}
|
||||
|
||||
export function properties(...properties: any[]) {
|
||||
|
@ -105,44 +85,36 @@ export const InitialTestSpec = {
|
|||
contact: {
|
||||
name: "Microsoft Corporation",
|
||||
url: "https://microsoft.com",
|
||||
email: "devnull@microsoft.com"
|
||||
email: "devnull@microsoft.com",
|
||||
},
|
||||
license: "MIT",
|
||||
version: "1.0"
|
||||
version: "1.0",
|
||||
},
|
||||
paths: {},
|
||||
components: {
|
||||
schemas: {}
|
||||
}
|
||||
schemas: {},
|
||||
},
|
||||
};
|
||||
|
||||
export type TestSpecCustomizer = (spec: any) => any;
|
||||
|
||||
export function createTestSpec(...customizers: TestSpecCustomizer[]): any {
|
||||
return customizers.reduce<any>(
|
||||
(spec: any, customizer: TestSpecCustomizer) => {
|
||||
return customizer(spec);
|
||||
},
|
||||
clone(InitialTestSpec)
|
||||
);
|
||||
return customizers.reduce<any>((spec: any, customizer: TestSpecCustomizer) => {
|
||||
return customizer(spec);
|
||||
}, clone(InitialTestSpec));
|
||||
}
|
||||
|
||||
export function addOperation(
|
||||
spec: any,
|
||||
path: string,
|
||||
operationDict: any,
|
||||
metadata: any = { apiVersions: ["1.0.0"] }
|
||||
metadata: any = { apiVersions: ["1.0.0"] },
|
||||
): void {
|
||||
operationDict = { ...operationDict, ...{ "x-ms-metadata": metadata } };
|
||||
spec.paths[path] = operationDict;
|
||||
}
|
||||
|
||||
export function addSchema(
|
||||
spec: any,
|
||||
name: string,
|
||||
schemaDict: any,
|
||||
metadata: any = { apiVersions: ["1.0.0"] }
|
||||
): void {
|
||||
export function addSchema(spec: any, name: string, schemaDict: any, metadata: any = { apiVersions: ["1.0.0"] }): void {
|
||||
schemaDict = { ...schemaDict, ...{ "x-ms-metadata": metadata } };
|
||||
spec.components.schemas[name] = schemaDict;
|
||||
}
|
||||
|
|
|
@ -3,17 +3,8 @@
|
|||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": ".",
|
||||
"types": [
|
||||
"mocha"
|
||||
]
|
||||
"types": ["mocha"]
|
||||
},
|
||||
"include": [
|
||||
"."
|
||||
],
|
||||
"exclude": [
|
||||
"dist",
|
||||
"resources",
|
||||
"node_modules",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
"include": ["."],
|
||||
"exclude": ["dist", "resources", "node_modules", "**/*.d.ts"]
|
||||
}
|
||||
|
|
|
@ -101,4 +101,4 @@
|
|||
"shouldPublish": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,13 +14,9 @@
|
|||
"stripInternal": true,
|
||||
"noEmitHelpers": false,
|
||||
"target": "es2018",
|
||||
"types": [
|
||||
"node"
|
||||
],
|
||||
"lib": [
|
||||
"es2018"
|
||||
],
|
||||
"types": ["node"],
|
||||
"lib": ["es2018"],
|
||||
"experimentalDecorators": true,
|
||||
"newLine": "LF"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче