Merge pull request #364 from Azure/internal/prettier
Internal: Configure prettier and eslint + fix
This commit is contained in:
Коммит
8b75335858
|
@ -1,61 +1,50 @@
|
||||||
---
|
---
|
||||||
parser: "@typescript-eslint/parser"
|
parser: "@typescript-eslint/parser"
|
||||||
plugins:
|
plugins:
|
||||||
- "@typescript-eslint"
|
- "@typescript-eslint"
|
||||||
|
- prettier
|
||||||
env:
|
env:
|
||||||
es6: true
|
es6: true
|
||||||
node: true
|
node: true
|
||||||
extends:
|
extends:
|
||||||
- eslint:recommended
|
- eslint:recommended
|
||||||
- plugin:@typescript-eslint/recommended
|
- plugin:@typescript-eslint/recommended
|
||||||
globals:
|
globals:
|
||||||
Atomics: readonly
|
Atomics: readonly
|
||||||
SharedArrayBuffer: readonly
|
SharedArrayBuffer: readonly
|
||||||
parserOptions:
|
parserOptions:
|
||||||
ecmaVersion: 2018
|
ecmaVersion: 2018
|
||||||
sourceType: module
|
sourceType: module
|
||||||
warnOnUnsupportedTypeScriptVersion : false
|
warnOnUnsupportedTypeScriptVersion: false
|
||||||
rules:
|
rules:
|
||||||
"@typescript-eslint/no-this-alias" : 'off'
|
"@typescript-eslint/no-this-alias": "off"
|
||||||
"@typescript-eslint/interface-name-prefix": 'off'
|
"@typescript-eslint/interface-name-prefix": "off"
|
||||||
"@typescript-eslint/explicit-function-return-type": 'off'
|
"@typescript-eslint/explicit-function-return-type": "off"
|
||||||
"@typescript-eslint/no-explicit-any": 'off'
|
"@typescript-eslint/no-explicit-any": "off"
|
||||||
"@typescript-eslint/no-empty-interface": 'off'
|
"@typescript-eslint/no-empty-interface": "off"
|
||||||
"@typescript-eslint/no-namespace": 'off'
|
"@typescript-eslint/no-namespace": "off"
|
||||||
"@typescript-eslint/explicit-member-accessibility": 'off'
|
"@typescript-eslint/explicit-member-accessibility": "off"
|
||||||
"@typescript-eslint/no-unused-vars": 'off'
|
"@typescript-eslint/no-unused-vars": "off"
|
||||||
"@typescript-eslint/no-parameter-properties": 'off'
|
"@typescript-eslint/no-parameter-properties": "off"
|
||||||
"@typescript-eslint/no-angle-bracket-type-assertion" : 'off'
|
"@typescript-eslint/no-angle-bracket-type-assertion": "off"
|
||||||
"require-atomic-updates" : 'off'
|
"@typescript-eslint/no-non-null-assertion": "off"
|
||||||
'@typescript-eslint/consistent-type-assertions' :
|
"@typescript-eslint/no-use-before-define": "off"
|
||||||
- error
|
"require-atomic-updates": "off"
|
||||||
- assertionStyle: 'angle-bracket'
|
"@typescript-eslint/consistent-type-assertions": "off"
|
||||||
|
|
||||||
"@typescript-eslint/array-type":
|
"@typescript-eslint/array-type":
|
||||||
- error
|
- warn
|
||||||
- default: generic
|
- default: generic
|
||||||
indent:
|
no-case-declarations: "off"
|
||||||
- warn
|
no-undef: "off"
|
||||||
- 2
|
no-unused-vars: "off"
|
||||||
- SwitchCase : 1
|
|
||||||
"@typescript-eslint/indent":
|
|
||||||
- 0
|
|
||||||
- 2
|
|
||||||
no-undef: 'off'
|
|
||||||
no-unused-vars: 'off'
|
|
||||||
linebreak-style:
|
linebreak-style:
|
||||||
- 'error'
|
- "warn"
|
||||||
- unix
|
- unix
|
||||||
quotes:
|
|
||||||
- error
|
|
||||||
- single
|
|
||||||
semi:
|
semi:
|
||||||
- error
|
- warn
|
||||||
- always
|
- always
|
||||||
no-multiple-empty-lines:
|
no-multiple-empty-lines:
|
||||||
- error
|
- warn
|
||||||
- max: 2
|
- max: 2
|
||||||
maxBOF: 0
|
maxBOF: 0
|
||||||
maxEOF: 1
|
maxEOF: 1
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,331 @@
|
||||||
|
#-------------------------------------------------------------------------------------------------------------------
|
||||||
|
# Keep this section in sync with .gitignore
|
||||||
|
#-------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## Ignore generated code
|
||||||
|
PackageTest/NugetPackageTest/Generated
|
||||||
|
src/generator/AutoRest.NodeJS.Tests/AcceptanceTests/*.js
|
||||||
|
|
||||||
|
## Ignore user-specific files, temporary files, build results, etc.
|
||||||
|
compare-results/*
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.sln.docstates
|
||||||
|
.vs
|
||||||
|
launchSettings.json
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
binaries/
|
||||||
|
[Dd]ebug*/
|
||||||
|
[Rr]elease/
|
||||||
|
|
||||||
|
[Tt]est[Rr]esult*
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
[Bb]uild.out
|
||||||
|
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
|
||||||
|
*.pidb
|
||||||
|
|
||||||
|
*.log*
|
||||||
|
*.scc
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
|
||||||
|
# VS Code settings
|
||||||
|
*.vscode
|
||||||
|
|
||||||
|
# Code analysis
|
||||||
|
*.CodeAnalysisLog.xml
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a Visual Studio add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
*.ncrunch*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
|
||||||
|
# Others
|
||||||
|
[Bb]in
|
||||||
|
[Oo]bj
|
||||||
|
sql
|
||||||
|
*.Cache
|
||||||
|
ClientBin
|
||||||
|
[Ss]tyle[Cc]op.*
|
||||||
|
~$*
|
||||||
|
*.dbmdl
|
||||||
|
|
||||||
|
# Build tasks
|
||||||
|
[Tt]ools/*.dll
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Mobile Tools for Java (J2ME)
|
||||||
|
.mtj.tmp/
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.ear
|
||||||
|
|
||||||
|
# Azure Tooling #
|
||||||
|
node_modules
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
|
||||||
|
# Eclipse #
|
||||||
|
*.pydevproject
|
||||||
|
.project
|
||||||
|
.metadata
|
||||||
|
bin/**
|
||||||
|
tmp/**
|
||||||
|
tmp/**/*
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
*~.nib
|
||||||
|
local.properties
|
||||||
|
.classpath
|
||||||
|
.settings/
|
||||||
|
.loadpath
|
||||||
|
|
||||||
|
# Xamarin #
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Other Tooling #
|
||||||
|
.classpath
|
||||||
|
.project
|
||||||
|
target
|
||||||
|
build
|
||||||
|
reports
|
||||||
|
.gradle
|
||||||
|
.idea
|
||||||
|
*.iml
|
||||||
|
Tools/7-Zip
|
||||||
|
.gitrevision
|
||||||
|
|
||||||
|
# Sensitive files
|
||||||
|
*.keys
|
||||||
|
*.pfx
|
||||||
|
*.cer
|
||||||
|
*.pem
|
||||||
|
*.jks
|
||||||
|
|
||||||
|
# Backup & report files from converting a project to a new version of VS.
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
|
||||||
|
# NuGet
|
||||||
|
packages
|
||||||
|
|
||||||
|
# Mac OS #
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
|
||||||
|
# Windows #
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Mono
|
||||||
|
*dll.mdb
|
||||||
|
*exe.mdb
|
||||||
|
|
||||||
|
#old nuget restore folder
|
||||||
|
.nuget/
|
||||||
|
src/generator/AutoRest.Ruby*Tests/Gemfile.lock
|
||||||
|
src/generator/AutoRest.Ruby*/*/RspecTests/Generated/*
|
||||||
|
|
||||||
|
#netcore
|
||||||
|
/NetCore
|
||||||
|
*.lock.json
|
||||||
|
|
||||||
|
#dnx installation
|
||||||
|
dnx-clr-win-x86*/
|
||||||
|
dnx-coreclr-win-x86*/
|
||||||
|
/dnx
|
||||||
|
|
||||||
|
# Gemfile.lock
|
||||||
|
Gemfile.lock
|
||||||
|
|
||||||
|
# go ignore
|
||||||
|
src/generator/AutoRest.Go.Tests/pkg/*
|
||||||
|
src/generator/AutoRest.Go.Tests/bin/*
|
||||||
|
src/generator/AutoRest.Go.Tests/src/github.com/*
|
||||||
|
src/generator/AutoRest.Go.Tests/src/tests/generated/*
|
||||||
|
src/generator/AutoRest.Go.Tests/src/tests/vendor/*
|
||||||
|
src/generator/AutoRest.Go.Tests/src/tests/glide.lock
|
||||||
|
|
||||||
|
autorest/**/*.js
|
||||||
|
core/**/*.js
|
||||||
|
|
||||||
|
*.js.map
|
||||||
|
|
||||||
|
# backup files
|
||||||
|
*~
|
||||||
|
|
||||||
|
#client runtime
|
||||||
|
src/client/**/*
|
||||||
|
|
||||||
|
src/extension/old/**/*
|
||||||
|
*.d.ts
|
||||||
|
|
||||||
|
src/bootstrapper
|
||||||
|
src/extension/out
|
||||||
|
src/next-gen
|
||||||
|
|
||||||
|
package/nuget/tools
|
||||||
|
package/chocolatey/*.nupkg
|
||||||
|
|
||||||
|
Samples/**/*.map
|
||||||
|
|
||||||
|
# npm (we do want to test for most recent versions)
|
||||||
|
**/package-lock.json
|
||||||
|
**/dist/
|
||||||
|
src/*/nm
|
||||||
|
/nm/
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
|
||||||
|
# next.js build output
|
||||||
|
.next
|
||||||
|
|
||||||
|
# Common toolchain intermediate files
|
||||||
|
temp
|
||||||
|
|
||||||
|
# Rush files
|
||||||
|
common/temp/**
|
||||||
|
package-deps.json
|
||||||
|
common/config/rush/shrinkwrap.yaml
|
||||||
|
|
||||||
|
# Code generation output for regression tests
|
||||||
|
core/test/regression
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------------------------------------------
|
||||||
|
# Prettier-specific overrides
|
||||||
|
#-------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Rush files
|
||||||
|
common/changes/
|
||||||
|
common/scripts/
|
||||||
|
common/config/
|
||||||
|
CHANGELOG.*
|
||||||
|
|
||||||
|
# Package manager files
|
||||||
|
pnpm-lock.yaml
|
||||||
|
yarn.lock
|
||||||
|
package-lock.json
|
||||||
|
shrinkwrap.json
|
||||||
|
|
||||||
|
# Build outputs
|
||||||
|
dist
|
||||||
|
lib
|
||||||
|
|
||||||
|
# Prettier reformats code blocks inside Markdown, which affects rendered output
|
||||||
|
*.md
|
||||||
|
|
||||||
|
schema/
|
||||||
|
Samples/
|
||||||
|
modelerfour/test/resources/
|
||||||
|
modelerfour/test/scenarios/
|
||||||
|
modelerfour/test/errors/
|
|
@ -0,0 +1,5 @@
|
||||||
|
trailingComma: "all"
|
||||||
|
printWidth: 120
|
||||||
|
quoteProps: "consistent"
|
||||||
|
endOfLine: "auto"
|
||||||
|
arrowParens: always
|
|
@ -3,33 +3,33 @@ trigger: none
|
||||||
pr: none
|
pr: none
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'ubuntu-latest'
|
vmImage: "ubuntu-latest"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: NodeTool@0
|
- task: NodeTool@0
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: '12.x'
|
versionSpec: "12.x"
|
||||||
displayName: 'Install Node.js'
|
displayName: "Install Node.js"
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
npm install -g npm
|
npm install -g npm
|
||||||
npm cache clear --force
|
npm cache clear --force
|
||||||
|
|
||||||
npx @microsoft/rush update
|
npx @microsoft/rush update
|
||||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||||
npx @microsoft/rush rebuild
|
npx @microsoft/rush rebuild
|
||||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||||
|
|
||||||
displayName: 'Build Modelerfour'
|
displayName: "Build Modelerfour"
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
npm install -g autorest
|
npm install -g autorest
|
||||||
git clone --no-tags --depth=1 https://github.com/Azure/azure-rest-api-specs
|
git clone --no-tags --depth=1 https://github.com/Azure/azure-rest-api-specs
|
||||||
node .scripts/run-benchmark.js ./azure-rest-api-specs ./output
|
node .scripts/run-benchmark.js ./azure-rest-api-specs ./output
|
||||||
displayName: Benchmark Azure REST API Specs
|
displayName: Benchmark Azure REST API Specs
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@1
|
- task: PublishPipelineArtifact@1
|
||||||
inputs:
|
inputs:
|
||||||
targetPath: '$(System.DefaultWorkingDirectory)/output'
|
targetPath: "$(System.DefaultWorkingDirectory)/output"
|
||||||
artifactName: benchmark-output
|
artifactName: benchmark-output
|
||||||
displayName: 'Upload Results'
|
displayName: "Upload Results"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const { spawn } = require('child_process');
|
const { spawn } = require("child_process");
|
||||||
const { readFileSync } = require('fs');
|
const { readFileSync } = require("fs");
|
||||||
const { resolve } = require('path');
|
const { resolve } = require("path");
|
||||||
|
|
||||||
function read(filename) {
|
function read(filename) {
|
||||||
const txt = readFileSync(filename, "utf8")
|
const txt = readFileSync(filename, "utf8")
|
||||||
|
@ -17,8 +17,6 @@ const repo = `${__dirname}/..`;
|
||||||
const rush = read(`${repo}/rush.json`);
|
const rush = read(`${repo}/rush.json`);
|
||||||
const pjs = {};
|
const pjs = {};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function forEachProject(onEach) {
|
function forEachProject(onEach) {
|
||||||
// load all the projects
|
// load all the projects
|
||||||
for (const each of rush.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" });
|
const proc = spawn("npm", ["--silent", "run", cmd], { cwd: location, shell: true, stdio: "inherit" });
|
||||||
procs.push(proc);
|
procs.push(proc);
|
||||||
result[name] = {
|
result[name] = {
|
||||||
name, location, project, proc,
|
name,
|
||||||
|
location,
|
||||||
|
project,
|
||||||
|
proc,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
procs.forEach(proc => proc.on("close", (code, signal) => {
|
procs.forEach((proc) =>
|
||||||
count--;
|
proc.on("close", (code, signal) => {
|
||||||
exitCode += code;
|
count--;
|
||||||
|
exitCode += code;
|
||||||
|
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
const t2 = process.uptime() * 100;
|
const t2 = process.uptime() * 100;
|
||||||
|
|
||||||
console.log('---------------------------------------------------------');
|
console.log("---------------------------------------------------------");
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
console.log(` Done : command '${cmd}' - ${Math.floor(t2 - t1) / 100} s -- Errors ${exitCode} `)
|
console.log(` Done : command '${cmd}' - ${Math.floor(t2 - t1) / 100} s -- Errors ${exitCode} `);
|
||||||
} else {
|
} else {
|
||||||
console.log(` Done : command '${cmd}' - ${Math.floor(t2 - t1) / 100} s -- No Errors `)
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
// Runs the npm run command on each project that has it.
|
// 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
|
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'ubuntu-latest'
|
vmImage: "ubuntu-latest"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: NodeTool@0
|
- task: NodeTool@0
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: '10.x'
|
versionSpec: "10.x"
|
||||||
displayName: 'Install Node.js'
|
displayName: "Install Node.js"
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
# ensure latest npm is installed
|
# ensure latest npm is installed
|
||||||
npm install -g npm
|
npm install -g npm
|
||||||
npm install -g publish-release
|
npm install -g publish-release
|
||||||
|
|
||||||
# make sure the versions are all synchronized and pull in dependencies
|
# make sure the versions are all synchronized and pull in dependencies
|
||||||
npx @microsoft/rush sync-versions
|
npx @microsoft/rush sync-versions
|
||||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||||
|
|
||||||
npx @microsoft/rush update
|
npx @microsoft/rush update
|
||||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||||
|
|
||||||
# set the actual package versions and update again
|
# set the actual package versions and update again
|
||||||
npx @microsoft/rush set-versions
|
npx @microsoft/rush set-versions
|
||||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||||
|
|
||||||
npx @microsoft/rush sync-versions
|
npx @microsoft/rush sync-versions
|
||||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||||
|
|
||||||
npx @microsoft/rush update
|
npx @microsoft/rush update
|
||||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||||
|
|
||||||
|
|
||||||
# compile the code
|
# compile the code
|
||||||
npx @microsoft/rush rebuild
|
npx @microsoft/rush rebuild
|
||||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||||
|
|
||||||
# build the packages
|
# build the packages
|
||||||
npx @microsoft/rush publish --publish --pack --include-all --tag latest
|
npx @microsoft/rush publish --publish --pack --include-all --tag latest
|
||||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||||
|
|
||||||
# publish autorest cli and beta (@autorest/autorest)
|
# publish autorest cli and beta (@autorest/autorest)
|
||||||
cd modelerfour
|
cd modelerfour
|
||||||
v=`node -e "console.log(require('./package.json').version)"`
|
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)
|
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
|
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||||
cd ..
|
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 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:
|
specs:
|
||||||
- specRootPath: ../modelerfour/node_modules/@microsoft.azure/autorest.testserver/swagger
|
- specRootPath: ../modelerfour/node_modules/@microsoft.azure/autorest.testserver/swagger
|
||||||
specPaths:
|
specPaths:
|
||||||
- additionalProperties.json
|
- additionalProperties.json
|
||||||
- azure-parameter-grouping.json
|
- azure-parameter-grouping.json
|
||||||
- azure-report.json
|
- azure-report.json
|
||||||
- azure-resource-x.json
|
- azure-resource-x.json
|
||||||
- azure-resource.json
|
- azure-resource.json
|
||||||
- azure-special-properties.json
|
- azure-special-properties.json
|
||||||
- body-array.json
|
- body-array.json
|
||||||
- body-boolean.json
|
- body-boolean.json
|
||||||
- body-boolean.quirks.json
|
- body-boolean.quirks.json
|
||||||
- body-byte.json
|
- body-byte.json
|
||||||
- body-complex.json
|
- body-complex.json
|
||||||
- body-date.json
|
- body-date.json
|
||||||
- body-datetime-rfc1123.json
|
- body-datetime-rfc1123.json
|
||||||
- body-datetime.json
|
- body-datetime.json
|
||||||
- body-duration.json
|
- body-duration.json
|
||||||
- body-dictionary.json
|
- body-dictionary.json
|
||||||
- body-file.json
|
- body-file.json
|
||||||
# Not yet supported by AutoRest v3 and Modelerfour
|
# Not yet supported by AutoRest v3 and Modelerfour
|
||||||
# - body-formdata-urlencoded.json
|
# - body-formdata-urlencoded.json
|
||||||
# - body-formdata.json
|
# - body-formdata.json
|
||||||
- body-integer.json
|
- body-integer.json
|
||||||
- body-number.json
|
- body-number.json
|
||||||
- body-number.quirks.json
|
- body-number.quirks.json
|
||||||
- body-string.json
|
- body-string.json
|
||||||
- body-string.quirks.json
|
- body-string.quirks.json
|
||||||
- complex-model.json
|
- complex-model.json
|
||||||
- custom-baseUrl.json
|
- custom-baseUrl.json
|
||||||
- custom-baseUrl-more-options.json
|
- custom-baseUrl-more-options.json
|
||||||
- custom-baseUrl-paging.json
|
- custom-baseUrl-paging.json
|
||||||
- extensible-enums-swagger.json
|
- extensible-enums-swagger.json
|
||||||
- head-exceptions.json
|
- head-exceptions.json
|
||||||
- head.json
|
- head.json
|
||||||
- header.json
|
- header.json
|
||||||
- httpInfrastructure.json
|
- httpInfrastructure.json
|
||||||
- httpInfrastructure.quirks.json
|
- httpInfrastructure.quirks.json
|
||||||
- lro.json
|
- lro.json
|
||||||
- media_types.json
|
- media_types.json
|
||||||
- model-flattening.json
|
- model-flattening.json
|
||||||
- multiapi-v1.json
|
- multiapi-v1.json
|
||||||
- multiapi-v2.json
|
- multiapi-v2.json
|
||||||
- multiapi-v3.json
|
- multiapi-v3.json
|
||||||
- object-type.json
|
- object-type.json
|
||||||
- paging.json
|
- paging.json
|
||||||
- parameter-flattening.json
|
- parameter-flattening.json
|
||||||
- report.json
|
- report.json
|
||||||
- required-optional.json
|
- required-optional.json
|
||||||
- storage.json
|
- storage.json
|
||||||
- subscriptionId-apiVersion.json
|
- subscriptionId-apiVersion.json
|
||||||
- url-multi-collectionFormat.json
|
- url-multi-collectionFormat.json
|
||||||
- url.json
|
- url.json
|
||||||
- validation.json
|
- validation.json
|
||||||
- xml-service.json
|
- xml-service.json
|
||||||
- xms-error-responses.json
|
- xms-error-responses.json
|
||||||
languages:
|
languages:
|
||||||
- language: python
|
- language: python
|
||||||
outputPath: ../modelerfour/test/regression/python
|
outputPath: ../modelerfour/test/regression/python
|
||||||
|
@ -73,7 +73,7 @@ languages:
|
||||||
outputPath: ../modelerfour/test/regression/typescript
|
outputPath: ../modelerfour/test/regression/typescript
|
||||||
excludeSpecs:
|
excludeSpecs:
|
||||||
# The paging spec currently fails, will re-enable once it's fixed
|
# The paging spec currently fails, will re-enable once it's fixed
|
||||||
- paging.json
|
- paging.json
|
||||||
oldArgs:
|
oldArgs:
|
||||||
- --v3
|
- --v3
|
||||||
- --package-name:test-package
|
- --package-name:test-package
|
||||||
|
|
|
@ -32,10 +32,7 @@ function executeBenchmark(readmePath) {
|
||||||
|
|
||||||
fs.mkdirSync(outputPath, { recursive: true });
|
fs.mkdirSync(outputPath, { recursive: true });
|
||||||
|
|
||||||
const fileStream = fs.openSync(
|
const fileStream = fs.openSync(path.join(outputPath, "autorest-output.txt"), "w");
|
||||||
path.join(outputPath, "autorest-output.txt"),
|
|
||||||
"w"
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(relativePath);
|
console.log(relativePath);
|
||||||
|
|
||||||
|
@ -53,7 +50,7 @@ function executeBenchmark(readmePath) {
|
||||||
--inspector.output-folder="./${outputPath}" \
|
--inspector.output-folder="./${outputPath}" \
|
||||||
--output-folder="./${outputPath}" \
|
--output-folder="./${outputPath}" \
|
||||||
"${readmePath}"`,
|
"${readmePath}"`,
|
||||||
{ stdio: ["inherit", fileStream, fileStream] }
|
{ stdio: ["inherit", fileStream, fileStream] },
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
result = "failed";
|
result = "failed";
|
||||||
|
@ -61,16 +58,14 @@ function executeBenchmark(readmePath) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const elapsedTime = process.hrtime(startTime);
|
const elapsedTime = process.hrtime(startTime);
|
||||||
const elapsedSeconds = (elapsedTime[0] + elapsedTime[1] / 1000000000).toFixed(
|
const elapsedSeconds = (elapsedTime[0] + elapsedTime[1] / 1000000000).toFixed(3);
|
||||||
3
|
|
||||||
);
|
|
||||||
console.log(` └ ${resultColor}${result} in ${elapsedSeconds}`, "\033[0m\n");
|
console.log(` └ ${resultColor}${result} in ${elapsedSeconds}`, "\033[0m\n");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
specPath: readmePath,
|
specPath: readmePath,
|
||||||
outputPath,
|
outputPath,
|
||||||
succeeded: result === "succeeded",
|
succeeded: result === "succeeded",
|
||||||
time: parseFloat(elapsedSeconds)
|
time: parseFloat(elapsedSeconds),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,9 +74,7 @@ console.log("");
|
||||||
|
|
||||||
const readmePaths = getReadmesRecursively(specsPath);
|
const readmePaths = getReadmesRecursively(specsPath);
|
||||||
|
|
||||||
const results = readmePaths
|
const results = readmePaths.map(executeBenchmark).sort((a, b) => b.time - a.time);
|
||||||
.map(executeBenchmark)
|
|
||||||
.sort((a, b) => b.time - a.time);
|
|
||||||
|
|
||||||
let aggregate = results.reduce(
|
let aggregate = results.reduce(
|
||||||
(totals, result) => {
|
(totals, result) => {
|
||||||
|
@ -89,31 +82,23 @@ let aggregate = results.reduce(
|
||||||
totals.time += result.time;
|
totals.time += result.time;
|
||||||
return totals;
|
return totals;
|
||||||
},
|
},
|
||||||
{ time: 0.0, success: 0 }
|
{ time: 0.0, success: 0 },
|
||||||
);
|
);
|
||||||
|
|
||||||
const successPercentage = ((aggregate.success / results.length) * 100).toFixed(
|
const successPercentage = ((aggregate.success / results.length) * 100).toFixed(2);
|
||||||
2
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`${aggregate.success} out of ${
|
`${aggregate.success} out of ${results.length} succeeded (${successPercentage}%), ${aggregate.time.toFixed(
|
||||||
results.length
|
3,
|
||||||
} succeeded (${successPercentage}%), ${aggregate.time.toFixed(
|
)}s total time\n`,
|
||||||
3
|
|
||||||
)}s total time\n`
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const topCount = Math.min(5, results.length);
|
const topCount = Math.min(5, results.length);
|
||||||
console.log(`Top ${topCount} longest runs:\n`);
|
console.log(`Top ${topCount} longest runs:\n`);
|
||||||
results
|
results.slice(0, topCount).forEach((r) => console.log(`${r.time}s ${r.specPath}`));
|
||||||
.slice(0, topCount)
|
|
||||||
.forEach(r => console.log(`${r.time}s ${r.specPath}`));
|
|
||||||
|
|
||||||
// Write out aggregate results file
|
// Write out aggregate results file
|
||||||
const resultsFile = fs.writeFileSync(
|
const resultsFile = fs.writeFileSync(
|
||||||
path.join(outputBasePath, "autorest-benchmark-results.csv"),
|
path.join(outputBasePath, "autorest-benchmark-results.csv"),
|
||||||
results
|
results.map((r) => `${r.specPath},${r.succeeded ? "succeeded" : "failed"},${r.time}`).join("\n"),
|
||||||
.map(r => `${r.specPath},${r.succeeded ? "succeeded" : "failed"},${r.time}`)
|
|
||||||
.join("\n")
|
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,44 +1,42 @@
|
||||||
const { exec } = require('child_process');
|
const { exec } = require("child_process");
|
||||||
const { writeFileSync } = require('fs');
|
const { writeFileSync } = require("fs");
|
||||||
const { forEachProject, projectCount } = require('./for-each');
|
const { forEachProject, projectCount } = require("./for-each");
|
||||||
|
|
||||||
let count = projectCount;
|
let count = projectCount;
|
||||||
|
|
||||||
|
|
||||||
function updateVersion(name, project, location, patch) {
|
function updateVersion(name, project, location, patch) {
|
||||||
const origJson = JSON.stringify(project, null, 2);
|
const origJson = JSON.stringify(project, null, 2);
|
||||||
|
|
||||||
// update the third digit
|
// update the third digit
|
||||||
const verInfo = project.version.split('.');
|
const verInfo = project.version.split(".");
|
||||||
verInfo[2] = patch;
|
verInfo[2] = patch;
|
||||||
project.version = verInfo.join('.');
|
project.version = verInfo.join(".");
|
||||||
|
|
||||||
// write the file if it's changed
|
// write the file if it's changed
|
||||||
const newJson = JSON.stringify(project, null, 2);
|
const newJson = JSON.stringify(project, null, 2);
|
||||||
if (origJson !== newJson) {
|
if (origJson !== newJson) {
|
||||||
console.log(`Writing project '${name}' version to '${project.version}' in '${location}'`);
|
console.log(`Writing project '${name}' version to '${project.version}' in '${location}'`);
|
||||||
writeFileSync(`${location}/package.json`, newJson)
|
writeFileSync(`${location}/package.json`, newJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
count--;
|
count--;
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
// last one!
|
// last one!
|
||||||
// call sync-versions
|
// call sync-versions
|
||||||
require('./sync-versions');
|
require("./sync-versions");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.argv[2] === '--reset') {
|
if (process.argv[2] === "--reset") {
|
||||||
forEachProject((name, location, project) => {
|
forEachProject((name, location, project) => {
|
||||||
updateVersion(name, project, location, 0);
|
updateVersion(name, project, location, 0);
|
||||||
})
|
});
|
||||||
} else {
|
} else {
|
||||||
// Sets the patch version on each package.json in the project.
|
// Sets the patch version on each package.json in the project.
|
||||||
forEachProject((name, location, project) => {
|
forEachProject((name, location, project) => {
|
||||||
exec(`git rev-list --parents HEAD --count --full-history .`, { cwd: location }, (o, stdout) => {
|
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);
|
updateVersion(name, project, location, patch);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
const { readFileSync, writeFileSync } = require('fs');
|
const { readFileSync, writeFileSync } = require("fs");
|
||||||
|
|
||||||
function read(filename) {
|
function read(filename) {
|
||||||
const txt = readFileSync(filename, 'utf8')
|
const txt = readFileSync(filename, "utf8")
|
||||||
.replace(/\r/gm, '')
|
.replace(/\r/gm, "")
|
||||||
.replace(/\n/gm, '«')
|
.replace(/\n/gm, "«")
|
||||||
.replace(/\/\*.*?\*\//gm, '')
|
.replace(/\/\*.*?\*\//gm, "")
|
||||||
.replace(/«/gm, '\n')
|
.replace(/«/gm, "\n")
|
||||||
.replace(/\s+\/\/.*/g, '');
|
.replace(/\s+\/\/.*/g, "");
|
||||||
return JSON.parse(txt);
|
return JSON.parse(txt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,26 +15,29 @@ const rush = read(`${__dirname}/../rush.json`);
|
||||||
const pjs = {};
|
const pjs = {};
|
||||||
|
|
||||||
function writeIfChanged(filename, content) {
|
function writeIfChanged(filename, content) {
|
||||||
const orig = JSON.parse(readFileSync(filename))
|
const orig = JSON.parse(readFileSync(filename));
|
||||||
const origJson = JSON.stringify(orig, null, 2);
|
const origJson = JSON.stringify(orig, null, 2);
|
||||||
const json = JSON.stringify(content, null, 2);
|
const json = JSON.stringify(content, null, 2);
|
||||||
|
|
||||||
if (origJson !== json) {
|
if (origJson !== json) {
|
||||||
console.log(`Writing updated file '${filename}'`)
|
console.log(`Writing updated file '${filename}'`);
|
||||||
writeFileSync(filename, json)
|
writeFileSync(filename, json);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function versionToInt(ver) {
|
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) {
|
while (v.length < 3) {
|
||||||
v.unshift(0);
|
v.unshift(0);
|
||||||
}
|
}
|
||||||
let n = 0;
|
let n = 0;
|
||||||
for (let i = 0; i < v.length; i++) {
|
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;
|
return n;
|
||||||
}
|
}
|
||||||
|
@ -51,7 +54,6 @@ function setPeerDependencies(dependencies) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function recordDeps(dependencies) {
|
function recordDeps(dependencies) {
|
||||||
for (const packageName in dependencies) {
|
for (const packageName in dependencies) {
|
||||||
const packageVersion = dependencies[packageName];
|
const packageVersion = dependencies[packageName];
|
||||||
|
@ -71,7 +73,6 @@ function recordDeps(dependencies) {
|
||||||
}
|
}
|
||||||
const v2 = versionToInt(packageList[packageName]);
|
const v2 = versionToInt(packageList[packageName]);
|
||||||
if (v > v2) {
|
if (v > v2) {
|
||||||
|
|
||||||
packageList[packageName] = packageVersion;
|
packageList[packageName] = packageVersion;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -82,7 +83,7 @@ function recordDeps(dependencies) {
|
||||||
function fixDeps(pj, dependencies) {
|
function fixDeps(pj, dependencies) {
|
||||||
for (const packageName in dependencies) {
|
for (const packageName in dependencies) {
|
||||||
if (dependencies[packageName] !== packageList[packageName]) {
|
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];
|
dependencies[packageName] = packageList[packageName];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,8 +101,8 @@ for (const pj of Object.getOwnPropertyNames(pjs)) {
|
||||||
const each = pjs[pj];
|
const each = pjs[pj];
|
||||||
setPeerDependencies(each.dependencies);
|
setPeerDependencies(each.dependencies);
|
||||||
setPeerDependencies(each.devDependencies);
|
setPeerDependencies(each.devDependencies);
|
||||||
if (each['static-link']) {
|
if (each["static-link"]) {
|
||||||
setPeerDependencies(each['static-link'].dependencies);
|
setPeerDependencies(each["static-link"].dependencies);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,8 +112,8 @@ for (const pj of Object.getOwnPropertyNames(pjs)) {
|
||||||
const each = pjs[pj];
|
const each = pjs[pj];
|
||||||
recordDeps(each.dependencies);
|
recordDeps(each.dependencies);
|
||||||
recordDeps(each.devDependencies);
|
recordDeps(each.devDependencies);
|
||||||
if (each['static-link']) {
|
if (each["static-link"]) {
|
||||||
recordDeps(each['static-link'].dependencies);
|
recordDeps(each["static-link"].dependencies);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,8 +121,8 @@ for (const pj of Object.getOwnPropertyNames(pjs)) {
|
||||||
const each = pjs[pj];
|
const each = pjs[pj];
|
||||||
fixDeps(pj, each.dependencies);
|
fixDeps(pj, each.dependencies);
|
||||||
fixDeps(pj, each.devDependencies);
|
fixDeps(pj, each.devDependencies);
|
||||||
if (each['static-link']) {
|
if (each["static-link"]) {
|
||||||
fixDeps(pj, each['static-link'].dependencies);
|
fixDeps(pj, each["static-link"].dependencies);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var changed = 0;
|
var changed = 0;
|
||||||
|
@ -138,5 +139,5 @@ for (const each of rush.projects) {
|
||||||
if (changed) {
|
if (changed) {
|
||||||
console.log(`Updated ${changed} files.`);
|
console.log(`Updated ${changed} files.`);
|
||||||
} else {
|
} else {
|
||||||
console.log('No changes made')
|
console.log("No changes made");
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,92 +4,92 @@
|
||||||
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
|
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
|
||||||
|
|
||||||
trigger:
|
trigger:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'ubuntu-latest'
|
vmImage: "ubuntu-latest"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: NodeTool@0
|
- task: NodeTool@0
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: '10.x'
|
versionSpec: "10.x"
|
||||||
displayName: 'Install Node.js'
|
displayName: "Install Node.js"
|
||||||
|
|
||||||
- script: |
|
|
||||||
npm install -g npm
|
|
||||||
npm cache clear --force
|
|
||||||
|
|
||||||
npx @microsoft/rush update
|
- script: |
|
||||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
npm install -g npm
|
||||||
npx @microsoft/rush rebuild
|
npm cache clear --force
|
||||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
|
||||||
npx @microsoft/rush test
|
|
||||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
|
||||||
|
|
||||||
# check to see if any files changed after the test run.
|
npx @microsoft/rush update
|
||||||
git status
|
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
|
# check to see if any files changed after the test run.
|
||||||
npx @microsoft/rush set-versions
|
git status
|
||||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
|
||||||
|
|
||||||
npx @microsoft/rush publish --publish --pack --include-all --tag latest
|
# set the actual package versions
|
||||||
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
npx @microsoft/rush set-versions
|
||||||
|
rc=$?; if [ $rc -ne 0 ]; then exit $rc ; fi
|
||||||
|
|
||||||
v=`node -e "console.log(require('./modelerfour/package.json').version)"`
|
npx @microsoft/rush publish --publish --pack --include-all --tag latest
|
||||||
echo "##vso[task.setvariable variable=artver]$v"
|
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
|
displayName: "Rush install, build and test"
|
||||||
inputs:
|
|
||||||
targetPath: '$(Build.SourcesDirectory)/common/temp/artifacts/packages/autorest-modelerfour-$(artver).tgz'
|
|
||||||
artifact: 'packages'
|
|
||||||
publishLocation: 'pipeline'
|
|
||||||
|
|
||||||
- pwsh: |
|
- task: PublishPipelineArtifact@1
|
||||||
$url = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$env:SYSTEM_TEAMPROJECTID/_apis/build/builds/$(Build.BuildId)/artifacts?artifactName=packages&api-version=5.1"
|
inputs:
|
||||||
$buildPipeline= Invoke-RestMethod -Uri $url -Headers @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" } -Method Get
|
targetPath: "$(Build.SourcesDirectory)/common/temp/artifacts/packages/autorest-modelerfour-$(artver).tgz"
|
||||||
$ArtifactDownloadURL= $buildPipeline.resource.downloadUrl
|
artifact: "packages"
|
||||||
$ArtifactDownloadURL = $ArtifactDownloadURL -replace "format=zip","format=file&subPath=%2Fautorest-modelerfour-$(artver).tgz"
|
publishLocation: "pipeline"
|
||||||
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: GitHubComment@0
|
- pwsh: |
|
||||||
inputs:
|
$url = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$env:SYSTEM_TEAMPROJECTID/_apis/build/builds/$(Build.BuildId)/artifacts?artifactName=packages&api-version=5.1"
|
||||||
gitHubConnection: 'Azure'
|
$buildPipeline= Invoke-RestMethod -Uri $url -Headers @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" } -Method Get
|
||||||
repositoryName: '$(Build.Repository.Name)'
|
$ArtifactDownloadURL= $buildPipeline.resource.downloadUrl
|
||||||
comment: |
|
$ArtifactDownloadURL = $ArtifactDownloadURL -replace "format=zip","format=file&subPath=%2Fautorest-modelerfour-$(artver).tgz"
|
||||||
You may test this build by running `autorest --reset` and then either:
|
echo "Raw Artifact Url: '$ArtifactDownloadURL'"
|
||||||
<hr>
|
$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:
|
- task: GitHubComment@0
|
||||||
|
inputs:
|
||||||
`autorest --use:$(DOWNURL) ` ...
|
gitHubConnection: "Azure"
|
||||||
<hr>
|
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:
|
add `--use:` to the command line:
|
||||||
|
|
||||||
``` yaml
|
|
||||||
use-extension:
|
|
||||||
"@autorest/modelerfour": "$(DOWNURL)"
|
|
||||||
```
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
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
|
``` yaml
|
||||||
inputs:
|
use-extension:
|
||||||
versionSpec: '3.x'
|
"@autorest/modelerfour": "$(DOWNURL)"
|
||||||
|
```
|
||||||
|
<hr>
|
||||||
|
|
||||||
- script: npm install -g @autorest/compare@~0.3.0
|
If this build is good for you, give this comment a thumbs up. (👍)
|
||||||
displayName: Install autorest-compare
|
|
||||||
|
|
||||||
- script: autorest-compare --compare:.scripts/regression-compare.yaml --language:python
|
And you should run `autorest --reset` again once you're finished testing to remove it.
|
||||||
displayName: Regression Test - @autorest/python
|
|
||||||
|
|
||||||
- script: autorest-compare --compare:.scripts/regression-compare.yaml --language:typescript
|
- task: UsePythonVersion@0
|
||||||
displayName: Regression Test - @autorest/typescript
|
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) {
|
if (project.scripts.watch) {
|
||||||
console.log(`npm run watch {cwd: ${__dirname}/../${projectFolder}}`);
|
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) => {
|
proc.on("error", (c, s) => {
|
||||||
console.log(packageName);
|
console.log(packageName);
|
||||||
console.error(c);
|
console.error(c);
|
||||||
console.error(s);
|
console.error(s);
|
||||||
});
|
});
|
||||||
proc.on('exit', (c, s) => {
|
proc.on("exit", (c, s) => {
|
||||||
console.log(packageName);
|
console.log(packageName);
|
||||||
console.error(c);
|
console.error(c);
|
||||||
console.error(s);
|
console.error(s);
|
||||||
});
|
});
|
||||||
proc.on('message', (c, s) => {
|
proc.on("message", (c, s) => {
|
||||||
console.log(packageName);
|
console.log(packageName);
|
||||||
console.error(c);
|
console.error(c);
|
||||||
console.error(s);
|
console.error(s);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -23,11 +23,11 @@
|
||||||
"**/obj/**": true,
|
"**/obj/**": true,
|
||||||
"**/bin/**": true
|
"**/bin/**": true
|
||||||
},
|
},
|
||||||
|
"[javascript]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
|
},
|
||||||
"[typescript]": {
|
"[typescript]": {
|
||||||
"editor.formatOnSave": true,
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
"editor.formatOnPaste": true,
|
|
||||||
"editor.tabSize": 2,
|
|
||||||
"editor.detectIndentation": false,
|
|
||||||
},
|
},
|
||||||
"[json]": {
|
"[json]": {
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
|
@ -47,18 +47,10 @@
|
||||||
"command-line.json": "jsonc",
|
"command-line.json": "jsonc",
|
||||||
"common-versions.json": "jsonc"
|
"common-versions.json": "jsonc"
|
||||||
},
|
},
|
||||||
"eslint.autoFixOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
"eslint.validate": [
|
"editor.codeActionsOnSave": {
|
||||||
"javascript",
|
"source.fixAll.eslint": true
|
||||||
{
|
},
|
||||||
"language": "typescript",
|
|
||||||
"autoFix": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"language": "typescriptreact",
|
|
||||||
"autoFix": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"eslint.workingDirectories": [
|
"eslint.workingDirectories": [
|
||||||
{
|
{
|
||||||
"changeProcessCWD": true,
|
"changeProcessCWD": true,
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
# configure plugins first
|
# configure plugins first
|
||||||
parser: "@typescript-eslint/parser"
|
parser: "@typescript-eslint/parser"
|
||||||
plugins:
|
plugins:
|
||||||
- "@typescript-eslint"
|
- "@typescript-eslint"
|
||||||
|
|
||||||
# then inherit the common settings
|
# then inherit the common settings
|
||||||
extends:
|
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 {
|
||||||
import { Session } from '@azure-tools/autorest-extension-base';
|
CodeModel,
|
||||||
import { values, items, length, Dictionary, refCount, clone } from '@azure-tools/linq';
|
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 {
|
export class Checker {
|
||||||
codeModel: CodeModel
|
codeModel: CodeModel;
|
||||||
options: Dictionary<any> = {};
|
options: Dictionary<any> = {};
|
||||||
|
|
||||||
constructor(protected session: Session<CodeModel>) {
|
constructor(protected session: Session<CodeModel>) {
|
||||||
this.codeModel = session.model;// shadow(session.model, filename);
|
this.codeModel = session.model; // shadow(session.model, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
// get our configuration for this run.
|
// get our configuration for this run.
|
||||||
this.options = await this.session.getValue('modelerfour', {});
|
this.options = await this.session.getValue("modelerfour", {});
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkOperationGroups() {
|
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 .`, []);
|
this.session.error(`Duplicate Operation group '${dupe}' detected .`, []);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkOperations() {
|
checkOperations() {
|
||||||
for (const group of this.codeModel.operationGroups) {
|
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.`, []);
|
this.session.error(`Duplicate Operation '${dupe}' detected.`, []);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkSchemas() {
|
checkSchemas() {
|
||||||
const allSchemas = values(<Dictionary<Schema[]>><any>this.codeModel.schemas).selectMany(schemas => Array.isArray(schemas) ? values(schemas) : []).toArray();
|
const allSchemas = values(<Dictionary<Array<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)}'.`, []);
|
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()
|
const types = values(<Array<Schema>>this.codeModel.schemas.objects)
|
||||||
for (const dupe of values(types).duplicates(each => each.language.default.name)) {
|
.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.`, []);
|
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()) {
|
/* for (const dupe of values(this.codeModel.schemas.numbers).select(each => each.type).duplicates()) {
|
||||||
this.session.error(`Duplicate '${dupe}' detected.`, []);
|
this.session.error(`Duplicate '${dupe}' detected.`, []);
|
||||||
|
@ -48,7 +72,7 @@ export class Checker {
|
||||||
}
|
}
|
||||||
|
|
||||||
process() {
|
process() {
|
||||||
if (this.options['additional-checks'] !== false) {
|
if (this.options["additional-checks"] !== false) {
|
||||||
this.checkOperationGroups();
|
this.checkOperationGroups();
|
||||||
|
|
||||||
this.checkOperations();
|
this.checkOperations();
|
||||||
|
@ -57,4 +81,4 @@ export class Checker {
|
||||||
}
|
}
|
||||||
return this.codeModel;
|
return this.codeModel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,17 +3,17 @@
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { serialize } from '@azure-tools/codegen';
|
import { serialize } from "@azure-tools/codegen";
|
||||||
import { Host, startSession } from '@azure-tools/autorest-extension-base';
|
import { Host, startSession } from "@azure-tools/autorest-extension-base";
|
||||||
import { codeModelSchema, CodeModel } from '@azure-tools/codemodel';
|
import { codeModelSchema, CodeModel } from "@azure-tools/codemodel";
|
||||||
import { Checker } from './checker';
|
import { Checker } from "./checker";
|
||||||
|
|
||||||
export async function processRequest(host: Host) {
|
export async function processRequest(host: Host) {
|
||||||
const debug = await host.GetValue('debug') || false;
|
const debug = (await host.GetValue("debug")) || false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const session = await startSession<CodeModel>(host, {}, codeModelSchema);
|
const session = await startSession<CodeModel>(host, {}, codeModelSchema);
|
||||||
const options = <any>await session.getValue('modelerfour', {});
|
const options = <any>await session.getValue("modelerfour", {});
|
||||||
|
|
||||||
// process
|
// process
|
||||||
const plugin = await new Checker(session).init();
|
const plugin = await new Checker(session).init();
|
||||||
|
@ -22,19 +22,18 @@ export async function processRequest(host: Host) {
|
||||||
const result = plugin.process();
|
const result = plugin.process();
|
||||||
|
|
||||||
// throw on errors.
|
// throw on errors.
|
||||||
if (!await session.getValue('no-errors', false)) {
|
if (!(await session.getValue("no-errors", false))) {
|
||||||
session.checkpoint();
|
session.checkpoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
// output the model to the pipeline
|
// output the model to the pipeline
|
||||||
if (options['emit-yaml-tags'] !== false) {
|
if (options["emit-yaml-tags"] !== false) {
|
||||||
host.WriteFile('code-model-v4.yaml', serialize(result, codeModelSchema), undefined, 'code-model-v4');
|
host.WriteFile("code-model-v4.yaml", serialize(result, codeModelSchema), undefined, "code-model-v4");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options['emit-yaml-tags'] !== true) {
|
if (options["emit-yaml-tags"] !== true) {
|
||||||
host.WriteFile('code-model-v4-no-tags.yaml', serialize(result), undefined, 'code-model-v4-no-tags');
|
host.WriteFile("code-model-v4-no-tags.yaml", serialize(result), undefined, "code-model-v4-no-tags");
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (E) {
|
} catch (E) {
|
||||||
if (debug) {
|
if (debug) {
|
||||||
console.error(`${__filename} - FAILURE ${JSON.stringify(E)} ${E.stack}`);
|
console.error(`${__filename} - FAILURE ${JSON.stringify(E)} ${E.stack}`);
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
// load modules from static linker filesystem.
|
// load modules from static linker filesystem.
|
||||||
try {
|
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/static-loader.js`).load(`${__dirname}/../dist/static_modules.fs`);
|
||||||
}
|
}
|
||||||
require(`${__dirname}/../dist/main.js`);
|
require(`${__dirname}/../dist/main.js`);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
import { CodeModel } from '@azure-tools/codemodel';
|
import { CodeModel } from "@azure-tools/codemodel";
|
||||||
import { Session } from '@azure-tools/autorest-extension-base';
|
import { Session } from "@azure-tools/autorest-extension-base";
|
||||||
|
|
||||||
export class Example {
|
export class Example {
|
||||||
codeModel: CodeModel
|
codeModel: CodeModel;
|
||||||
|
|
||||||
constructor(protected session: Session<CodeModel>) {
|
constructor(protected session: Session<CodeModel>) {
|
||||||
this.codeModel = session.model;// shadow(session.model, filename);
|
this.codeModel = session.model; // shadow(session.model, filename);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
process() {
|
process() {
|
||||||
return this.codeModel;
|
return this.codeModel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,13 @@
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { serialize } from '@azure-tools/codegen';
|
import { serialize } from "@azure-tools/codegen";
|
||||||
import { Host, startSession } from '@azure-tools/autorest-extension-base';
|
import { Host, startSession } from "@azure-tools/autorest-extension-base";
|
||||||
import { codeModelSchema, CodeModel } from '@azure-tools/codemodel';
|
import { codeModelSchema, CodeModel } from "@azure-tools/codemodel";
|
||||||
import { Example } from './example';
|
import { Example } from "./example";
|
||||||
|
|
||||||
|
|
||||||
export async function processRequest(host: Host) {
|
export async function processRequest(host: Host) {
|
||||||
const debug = await host.GetValue('debug') || false;
|
const debug = (await host.GetValue("debug")) || false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const session = await startSession<CodeModel>(host, {}, codeModelSchema);
|
const session = await startSession<CodeModel>(host, {}, codeModelSchema);
|
||||||
|
@ -22,9 +21,8 @@ export async function processRequest(host: Host) {
|
||||||
const result = plugin.process();
|
const result = plugin.process();
|
||||||
|
|
||||||
// output the model to the pipeline
|
// output the model to the pipeline
|
||||||
host.WriteFile('code-model-v4.yaml', serialize(result, codeModelSchema), undefined, 'code-model-v4');
|
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-no-tags.yaml", serialize(result), undefined, "code-model-v4-no-tags");
|
||||||
|
|
||||||
} catch (E) {
|
} catch (E) {
|
||||||
if (debug) {
|
if (debug) {
|
||||||
console.error(`${__filename} - FAILURE ${JSON.stringify(E)} ${E.stack}`);
|
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 {
|
||||||
import { Session } from '@azure-tools/autorest-extension-base';
|
CodeModel,
|
||||||
import { values, items, length, Dictionary, refCount, clone } from '@azure-tools/linq';
|
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 xmsThreshold = "x-ms-payload-flattening-threshold";
|
||||||
const xmsFlatten = 'x-ms-client-flatten';
|
const xmsFlatten = "x-ms-client-flatten";
|
||||||
const isCurrentlyFlattening = 'x-ms-flattening';
|
const isCurrentlyFlattening = "x-ms-flattening";
|
||||||
const hasBeenFlattened = 'x-ms-flattened';
|
const hasBeenFlattened = "x-ms-flattened";
|
||||||
|
|
||||||
export class Flattener {
|
export class Flattener {
|
||||||
codeModel: CodeModel
|
codeModel: CodeModel;
|
||||||
options: Dictionary<any> = {};
|
options: Dictionary<any> = {};
|
||||||
threshold: number = 0;
|
threshold = 0;
|
||||||
recursePayload: boolean = false;
|
recursePayload = false;
|
||||||
|
|
||||||
constructor(protected session: Session<CodeModel>) {
|
constructor(protected session: Session<CodeModel>) {
|
||||||
this.codeModel = session.model;// shadow(session.model, filename);
|
this.codeModel = session.model; // shadow(session.model, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
// get our configuration for this run.
|
// get our configuration for this run.
|
||||||
this.options = await this.session.getValue('modelerfour', {});
|
this.options = await this.session.getValue("modelerfour", {});
|
||||||
this.threshold = await this.session.getValue('payload-flattening-threshold', 0);
|
this.threshold = await this.session.getValue("payload-flattening-threshold", 0);
|
||||||
this.recursePayload = await this.session.getValue('recursive-payload-flattening', false);
|
this.recursePayload = await this.session.getValue("recursive-payload-flattening", false);
|
||||||
return this;
|
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) {
|
if (property.readOnly) {
|
||||||
// skip read-only properties
|
// skip read-only properties
|
||||||
return;
|
return;
|
||||||
|
@ -35,29 +53,34 @@ export class Flattener {
|
||||||
yield* this.getFlattenedParameters(parameter, child, [...path, property]);
|
yield* this.getFlattenedParameters(parameter, child, [...path, property]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const vp = new VirtualParameter(property.language.default.name, property.language.default.description, property.schema, {
|
const vp = new VirtualParameter(
|
||||||
...property,
|
property.language.default.name,
|
||||||
implementation: ImplementationLocation.Method,
|
property.language.default.description,
|
||||||
originalParameter: parameter,
|
property.schema,
|
||||||
targetProperty: property,
|
{
|
||||||
pathToProperty: path
|
...property,
|
||||||
});
|
implementation: ImplementationLocation.Method,
|
||||||
|
originalParameter: parameter,
|
||||||
|
targetProperty: property,
|
||||||
|
pathToProperty: path,
|
||||||
|
},
|
||||||
|
);
|
||||||
delete (<any>vp).serializedName;
|
delete (<any>vp).serializedName;
|
||||||
delete (<any>vp).readOnly;
|
delete (<any>vp).readOnly;
|
||||||
delete (<any>vp).isDiscriminator;
|
delete (<any>vp).isDiscriminator;
|
||||||
delete (<any>vp).flattenedNames;
|
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 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']) {
|
if (path.length === 0 && parameter.extensions?.["x-ms-parameter-grouping"]) {
|
||||||
(vp.extensions = vp.extensions || {})['x-ms-parameter-grouping'] = parameter.extensions?.['x-ms-parameter-grouping'];
|
(vp.extensions = vp.extensions || {})["x-ms-parameter-grouping"] =
|
||||||
|
parameter.extensions?.["x-ms-parameter-grouping"];
|
||||||
}
|
}
|
||||||
|
|
||||||
yield vp;
|
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)
|
* 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) {
|
flattenPayload(request: Request, parameter: Parameter, schema: ObjectSchema) {
|
||||||
|
@ -73,7 +96,6 @@ export class Flattener {
|
||||||
request.parameters?.push(vp);
|
request.parameters?.push(vp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,14 +112,16 @@ export class Flattener {
|
||||||
|
|
||||||
if (state === true) {
|
if (state === true) {
|
||||||
// in progress.
|
// 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.
|
// hasn't started yet.
|
||||||
schema.extensions = schema.extensions || {};
|
schema.extensions = schema.extensions || {};
|
||||||
schema.extensions[isCurrentlyFlattening] = true;
|
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.
|
// the problem when the order isn't just right.
|
||||||
for (const parent of values(schema.parents?.immediate)) {
|
for (const parent of values(schema.parents?.immediate)) {
|
||||||
if (isObjectSchema(parent)) {
|
if (isObjectSchema(parent)) {
|
||||||
|
@ -107,7 +131,6 @@ export class Flattener {
|
||||||
|
|
||||||
if (schema.properties) {
|
if (schema.properties) {
|
||||||
for (const { key: index, value: property } of items(schema.properties).toArray().reverse()) {
|
for (const { key: index, value: property } of items(schema.properties).toArray().reverse()) {
|
||||||
|
|
||||||
if (isObjectSchema(property.schema) && property.extensions?.[xmsFlatten]) {
|
if (isObjectSchema(property.schema) && property.extensions?.[xmsFlatten]) {
|
||||||
// first, ensure tha the child is pre-flattened
|
// first, ensure tha the child is pre-flattened
|
||||||
this.flattenSchema(property.schema);
|
this.flattenSchema(property.schema);
|
||||||
|
@ -115,20 +138,30 @@ export class Flattener {
|
||||||
// remove that property from the scheama
|
// remove that property from the scheama
|
||||||
schema.properties.splice(index, 1);
|
schema.properties.splice(index, 1);
|
||||||
|
|
||||||
// copy all of the properties from the child into this
|
// copy all of the properties from the child into this
|
||||||
// schema
|
// schema
|
||||||
for (const childProperty of values(getAllProperties(property.schema))) {
|
for (const childProperty of values(getAllProperties(property.schema))) {
|
||||||
schema.addProperty(new Property(childProperty.language.default.name, childProperty.language.default.description, childProperty.schema, {
|
schema.addProperty(
|
||||||
...(<any>childProperty),
|
new Property(
|
||||||
flattenedNames: [property.serializedName, ...childProperty.flattenedNames ? childProperty.flattenedNames : [childProperty.serializedName]],
|
childProperty.language.default.name,
|
||||||
required: property.required && childProperty.required
|
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
|
// remove the extension
|
||||||
delete property.extensions[xmsFlatten];
|
delete property.extensions[xmsFlatten];
|
||||||
if (length(property.extensions) === 0) {
|
if (length(property.extensions) === 0) {
|
||||||
delete property['extensions'];
|
delete property["extensions"];
|
||||||
}
|
}
|
||||||
// and mark the child class as 'do-not-generate' ?
|
// and mark the child class as 'do-not-generate' ?
|
||||||
(property.schema.extensions = property.schema.extensions || {})[hasBeenFlattened] = true;
|
(property.schema.extensions = property.schema.extensions || {})[hasBeenFlattened] = true;
|
||||||
|
@ -143,18 +176,17 @@ export class Flattener {
|
||||||
// support 'x-ms-payload-flattening-threshold' per-operation
|
// support 'x-ms-payload-flattening-threshold' per-operation
|
||||||
// support '--payload-flattening-threshold:X' global setting
|
// 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)) {
|
for (const schema of values(this.codeModel.schemas.objects)) {
|
||||||
this.flattenSchema(schema);
|
this.flattenSchema(schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.options['keep-unused-flattened-models']) {
|
if (!this.options["keep-unused-flattened-models"]) {
|
||||||
let dirty = false;
|
let dirty = false;
|
||||||
do {
|
do {
|
||||||
// reset on every pass
|
// reset on every pass
|
||||||
dirty = false;
|
dirty = false;
|
||||||
// remove unreferenced models
|
// remove unreferenced models
|
||||||
for (const { key, value: schema } of items(this.codeModel.schemas.objects).toArray()) {
|
for (const { key, value: schema } of items(this.codeModel.schemas.objects).toArray()) {
|
||||||
// only remove unreferenced models that have been flattened.
|
// only remove unreferenced models that have been flattened.
|
||||||
if (!schema.extensions?.[hasBeenFlattened]) {
|
if (!schema.extensions?.[hasBeenFlattened]) {
|
||||||
|
@ -162,12 +194,12 @@ export class Flattener {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (schema.discriminatorValue || schema.discriminator) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (schema.children?.all || schema.parents?.all) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,34 +212,33 @@ export class Flattener {
|
||||||
} while (dirty);
|
} while (dirty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (const schema of values(this.codeModel.schemas.objects)) {
|
for (const schema of values(this.codeModel.schemas.objects)) {
|
||||||
if (schema.extensions) {
|
if (schema.extensions) {
|
||||||
delete schema.extensions[isCurrentlyFlattening];
|
delete schema.extensions[isCurrentlyFlattening];
|
||||||
// don't want this until I have removed the unreferenced models.
|
// don't want this until I have removed the unreferenced models.
|
||||||
// delete schema.extensions[hasBeenFlattened];
|
// delete schema.extensions[hasBeenFlattened];
|
||||||
if (length(schema.extensions) === 0) {
|
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:
|
* A body parameter is flattened (one level) when:
|
||||||
*
|
*
|
||||||
* - the body parameter schema is an object
|
* - the body parameter schema is an object
|
||||||
* - the body parameter schema is not polymorphic (is this true?)
|
* - the body parameter schema is not polymorphic (is this true?)
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* and one of:
|
* and one of:
|
||||||
* - the body parameter has x-ms-client-flatten: true
|
* - 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 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
|
* - 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
|
// flatten payloads
|
||||||
|
@ -216,12 +247,15 @@ export class Flattener {
|
||||||
for (const operation of group.operations) {
|
for (const operation of group.operations) {
|
||||||
// when there are multiple requests in an operation
|
// when there are multiple requests in an operation
|
||||||
// and the generator asks not to flatten them
|
// 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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const request of values(operation.requests)) {
|
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)) {
|
if (body && isObjectSchema(body.schema)) {
|
||||||
const schema = <ObjectSchema>body.schema;
|
const schema = <ObjectSchema>body.schema;
|
||||||
|
@ -236,12 +270,16 @@ export class Flattener {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!flattenOperationPayload) {
|
if (!flattenOperationPayload) {
|
||||||
const threshold = <number>operation.extensions?.[xmsThreshold] ?? this.threshold;
|
const threshold = <number>operation.extensions?.[xmsThreshold] ?? this.threshold;
|
||||||
if (threshold > 0) {
|
if (threshold > 0) {
|
||||||
// get the count of the (non-readonly) properties in the schema
|
// 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.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { serialize } from '@azure-tools/codegen';
|
import { serialize } from "@azure-tools/codegen";
|
||||||
import { Host, startSession } from '@azure-tools/autorest-extension-base';
|
import { Host, startSession } from "@azure-tools/autorest-extension-base";
|
||||||
import { codeModelSchema, CodeModel } from '@azure-tools/codemodel';
|
import { codeModelSchema, CodeModel } from "@azure-tools/codemodel";
|
||||||
import { Flattener } from './flattener';
|
import { Flattener } from "./flattener";
|
||||||
|
|
||||||
|
|
||||||
export async function processRequest(host: Host) {
|
export async function processRequest(host: Host) {
|
||||||
const debug = await host.GetValue('debug') || false;
|
const debug = (await host.GetValue("debug")) || false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const session = await startSession<CodeModel>(host, {}, codeModelSchema);
|
const session = await startSession<CodeModel>(host, {}, codeModelSchema);
|
||||||
const options = <any>await session.getValue('modelerfour', {});
|
const options = <any>await session.getValue("modelerfour", {});
|
||||||
|
|
||||||
// process
|
// process
|
||||||
const plugin = await new Flattener(session).init();
|
const plugin = await new Flattener(session).init();
|
||||||
|
@ -23,14 +22,13 @@ export async function processRequest(host: Host) {
|
||||||
const result = plugin.process();
|
const result = plugin.process();
|
||||||
|
|
||||||
// output the model to the pipeline
|
// output the model to the pipeline
|
||||||
if (options['emit-yaml-tags'] !== false) {
|
if (options["emit-yaml-tags"] !== false) {
|
||||||
host.WriteFile('code-model-v4.yaml', serialize(result, codeModelSchema), undefined, 'code-model-v4');
|
host.WriteFile("code-model-v4.yaml", serialize(result, codeModelSchema), undefined, "code-model-v4");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options['emit-yaml-tags'] !== true) {
|
if (options["emit-yaml-tags"] !== true) {
|
||||||
host.WriteFile('code-model-v4-no-tags.yaml', serialize(result), undefined, 'code-model-v4-no-tags');
|
host.WriteFile("code-model-v4-no-tags.yaml", serialize(result), undefined, "code-model-v4-no-tags");
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (E) {
|
} catch (E) {
|
||||||
if (debug) {
|
if (debug) {
|
||||||
console.error(`${__filename} - FAILURE ${JSON.stringify(E)} ${E.stack}`);
|
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 {
|
||||||
import { Session } from '@azure-tools/autorest-extension-base';
|
CodeModel,
|
||||||
import { values, items, length, Dictionary, refCount, clone } from '@azure-tools/linq';
|
Schema,
|
||||||
import { pascalCase, camelCase } from '@azure-tools/codegen';
|
GroupSchema,
|
||||||
|
isObjectSchema,
|
||||||
const mergeReponseHeaders = 'merge-response-headers';
|
SchemaType,
|
||||||
const enableParameterGrouping = 'group-parameters';
|
GroupProperty,
|
||||||
const xmsParameterGrouping = 'x-ms-parameter-grouping'
|
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 {
|
export class Grouper {
|
||||||
codeModel: CodeModel
|
codeModel: CodeModel;
|
||||||
options: Dictionary<any> = {};
|
options: Dictionary<any> = {};
|
||||||
groups: Dictionary<GroupSchema> = {};
|
groups: Dictionary<GroupSchema> = {};
|
||||||
|
|
||||||
constructor(protected session: Session<CodeModel>) {
|
constructor(protected session: Session<CodeModel>) {
|
||||||
this.codeModel = session.model;// shadow(session.model, filename);
|
this.codeModel = session.model; // shadow(session.model, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
// get our configuration for this run.
|
// get our configuration for this run.
|
||||||
this.options = await this.session.getValue('modelerfour', {});
|
this.options = await this.session.getValue("modelerfour", {});
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
process() {
|
process() {
|
||||||
|
|
||||||
if (this.options[enableParameterGrouping] === true) {
|
if (this.options[enableParameterGrouping] === true) {
|
||||||
|
|
||||||
for (const group of this.codeModel.operationGroups) {
|
for (const group of this.codeModel.operationGroups) {
|
||||||
for (const operation of group.operations) {
|
for (const operation of group.operations) {
|
||||||
for (const request of values(operation.requests)) {
|
for (const request of values(operation.requests)) {
|
||||||
|
@ -53,17 +66,26 @@ export class Grouper {
|
||||||
|
|
||||||
proposedName(group: OperationGroup, operation: Operation, parameter: Parameter) {
|
proposedName(group: OperationGroup, operation: Operation, parameter: Parameter) {
|
||||||
const xmsp = parameter.extensions?.[xmsParameterGrouping];
|
const xmsp = parameter.extensions?.[xmsParameterGrouping];
|
||||||
if (xmsp.name && typeof xmsp.name === 'string') {
|
if (xmsp.name && typeof xmsp.name === "string") {
|
||||||
return xmsp.name;
|
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}`);
|
return pascalCase(`${group.$key} ${operation.language.default.name} ${postfix}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
processParameterGroup(group: OperationGroup, operation: Operation, request: Request) {
|
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) {
|
if (grouped.length > 0) {
|
||||||
// create a parameter group object schema for the selected parameters.
|
// 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.
|
// see if we've started the schema for this yet.
|
||||||
if (!this.groups[groupName]) {
|
if (!this.groups[groupName]) {
|
||||||
// create a new object schema for this group
|
// 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];
|
schema.usage = [SchemaContext.Input];
|
||||||
this.groups[groupName] = schema;
|
this.groups[groupName] = schema;
|
||||||
this.codeModel.schemas.add(schema);
|
this.codeModel.schemas.add(schema);
|
||||||
}
|
}
|
||||||
const schema = this.groups[groupName];
|
const schema = this.groups[groupName];
|
||||||
|
|
||||||
// see if the group has this parameter.
|
// see if the group has this parameter.
|
||||||
const existingProperty = values(schema.properties).first(each => each.language.default.name === parameter.language.default.name);
|
const existingProperty = values(schema.properties).first(
|
||||||
|
(each) => each.language.default.name === parameter.language.default.name,
|
||||||
|
);
|
||||||
if (existingProperty) {
|
if (existingProperty) {
|
||||||
// we have a property by this name one already
|
// we have a property by this name one already
|
||||||
// mark the groupproperty with this parameter (so we can find it if needed)
|
// mark the groupproperty with this parameter (so we can find it if needed)
|
||||||
existingProperty.originalParameter.push(parameter);
|
existingProperty.originalParameter.push(parameter);
|
||||||
} else {
|
} else {
|
||||||
// create a property for this parameter.
|
// create a property for this parameter.
|
||||||
const gp = new GroupProperty(parameter.language.default.name, parameter.language.default.description, parameter.schema, {
|
const gp = new GroupProperty(
|
||||||
required: parameter.required,
|
parameter.language.default.name,
|
||||||
});
|
parameter.language.default.description,
|
||||||
|
parameter.schema,
|
||||||
|
{
|
||||||
|
required: parameter.required,
|
||||||
|
},
|
||||||
|
);
|
||||||
gp.originalParameter.push(parameter);
|
gp.originalParameter.push(parameter);
|
||||||
schema.add(gp);
|
schema.add(gp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if this groupSchema has been added as a parameter for this operation yet.
|
// check if this groupSchema has been added as a parameter for this operation yet.
|
||||||
if (!addedGroupedParameters.has(schema)) {
|
if (!addedGroupedParameters.has(schema)) {
|
||||||
addedGroupedParameters.set(schema, request.addParameter(new Parameter(camelCase(schema.language.default.name), schema.language.default.description, schema, {
|
addedGroupedParameters.set(
|
||||||
implementation: ImplementationLocation.Method,
|
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.
|
// make sure that it's not optional if any parameter are not optional.
|
||||||
|
@ -115,7 +149,7 @@ export class Grouper {
|
||||||
if (parameter.extensions) {
|
if (parameter.extensions) {
|
||||||
delete parameter.extensions[xmsParameterGrouping];
|
delete parameter.extensions[xmsParameterGrouping];
|
||||||
if (length(parameter.extensions) === 0) {
|
if (length(parameter.extensions) === 0) {
|
||||||
delete parameter['extensions'];
|
delete parameter["extensions"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,6 +157,6 @@ export class Grouper {
|
||||||
}
|
}
|
||||||
|
|
||||||
processResponseHeaders(operation: Operation) {
|
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.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { serialize } from '@azure-tools/codegen';
|
import { serialize } from "@azure-tools/codegen";
|
||||||
import { Host, startSession } from '@azure-tools/autorest-extension-base';
|
import { Host, startSession } from "@azure-tools/autorest-extension-base";
|
||||||
import { codeModelSchema, CodeModel } from '@azure-tools/codemodel';
|
import { codeModelSchema, CodeModel } from "@azure-tools/codemodel";
|
||||||
import { Grouper } from './grouper';
|
import { Grouper } from "./grouper";
|
||||||
|
|
||||||
|
|
||||||
export async function processRequest(host: Host) {
|
export async function processRequest(host: Host) {
|
||||||
const debug = await host.GetValue('debug') || false;
|
const debug = (await host.GetValue("debug")) || false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const session = await startSession<CodeModel>(host, {}, codeModelSchema);
|
const session = await startSession<CodeModel>(host, {}, codeModelSchema);
|
||||||
const options = <any>await session.getValue('modelerfour', {});
|
const options = <any>await session.getValue("modelerfour", {});
|
||||||
|
|
||||||
// process
|
// process
|
||||||
const plugin = await new Grouper(session).init();
|
const plugin = await new Grouper(session).init();
|
||||||
|
@ -23,14 +22,13 @@ export async function processRequest(host: Host) {
|
||||||
const result = plugin.process();
|
const result = plugin.process();
|
||||||
|
|
||||||
// output the model to the pipeline
|
// output the model to the pipeline
|
||||||
if (options['emit-yaml-tags'] !== false) {
|
if (options["emit-yaml-tags"] !== false) {
|
||||||
host.WriteFile('code-model-v4.yaml', serialize(result, codeModelSchema), undefined, 'code-model-v4');
|
host.WriteFile("code-model-v4.yaml", serialize(result, codeModelSchema), undefined, "code-model-v4");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options['emit-yaml-tags'] !== true) {
|
if (options["emit-yaml-tags"] !== true) {
|
||||||
host.WriteFile('code-model-v4-no-tags.yaml', serialize(result), undefined, 'code-model-v4-no-tags');
|
host.WriteFile("code-model-v4-no-tags.yaml", serialize(result), undefined, "code-model-v4-no-tags");
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (E) {
|
} catch (E) {
|
||||||
if (debug) {
|
if (debug) {
|
||||||
console.error(`${__filename} - FAILURE ${JSON.stringify(E)} ${E.stack}`);
|
console.error(`${__filename} - FAILURE ${JSON.stringify(E)} ${E.stack}`);
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
import { AutoRestExtension, } from '@azure-tools/autorest-extension-base';
|
import { AutoRestExtension } from "@azure-tools/autorest-extension-base";
|
||||||
import { processRequest as modelerfour } from './modeler/plugin-modelerfour';
|
import { processRequest as modelerfour } from "./modeler/plugin-modelerfour";
|
||||||
import { processRequest as preNamer } from './prenamer/plugin-prenamer';
|
import { processRequest as preNamer } from "./prenamer/plugin-prenamer";
|
||||||
import { processRequest as flattener } from './flattener/plugin-flattener';
|
import { processRequest as flattener } from "./flattener/plugin-flattener";
|
||||||
import { processRequest as grouper } from './grouper/plugin-grouper';
|
import { processRequest as grouper } from "./grouper/plugin-grouper";
|
||||||
import { processRequest as checker } from './checker/plugin-checker';
|
import { processRequest as checker } from "./checker/plugin-checker";
|
||||||
import { processRequest as prechecker } from './quality-precheck/prechecker';
|
import { processRequest as prechecker } from "./quality-precheck/prechecker";
|
||||||
|
|
||||||
export async function initializePlugins(pluginHost: AutoRestExtension) {
|
export async function initializePlugins(pluginHost: AutoRestExtension) {
|
||||||
pluginHost.Add('prechecker', prechecker);
|
pluginHost.Add("prechecker", prechecker);
|
||||||
pluginHost.Add('modelerfour', modelerfour);
|
pluginHost.Add("modelerfour", modelerfour);
|
||||||
pluginHost.Add('grouper', grouper);
|
pluginHost.Add("grouper", grouper);
|
||||||
pluginHost.Add('pre-namer', preNamer);
|
pluginHost.Add("pre-namer", preNamer);
|
||||||
pluginHost.Add('flattener', flattener);
|
pluginHost.Add("flattener", flattener);
|
||||||
pluginHost.Add('checker', checker);
|
pluginHost.Add("checker", checker);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
|
@ -21,4 +21,4 @@ async function main() {
|
||||||
await pluginHost.Run();
|
await pluginHost.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
main();
|
||||||
|
|
|
@ -1,9 +1,17 @@
|
||||||
import { Session } from '@azure-tools/autorest-extension-base';
|
import { Session } from "@azure-tools/autorest-extension-base";
|
||||||
import * as OpenAPI from '@azure-tools/openapi';
|
import * as OpenAPI from "@azure-tools/openapi";
|
||||||
import { values, length, items, ToDictionary, Dictionary } from '@azure-tools/linq';
|
import { values, length, items, ToDictionary, Dictionary } from "@azure-tools/linq";
|
||||||
import { ChoiceSchema, XmlSerlializationFormat, ExternalDocumentation, ApiVersion, Deprecation, ChoiceValue, SetType } from '@azure-tools/codemodel';
|
import {
|
||||||
import { StringFormat, JsonType, ParameterLocation } from '@azure-tools/openapi';
|
ChoiceSchema,
|
||||||
import { getPascalIdentifier } from '@azure-tools/codegen';
|
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 {
|
export interface XMSEnum {
|
||||||
modelAsString?: boolean;
|
modelAsString?: boolean;
|
||||||
|
@ -11,67 +19,66 @@ export interface XMSEnum {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const removeKnownParameters = [
|
const removeKnownParameters = [
|
||||||
'x-ms-metadata',
|
"x-ms-metadata",
|
||||||
'x-ms-enum',
|
"x-ms-enum",
|
||||||
'x-ms-code-generation-settings',
|
"x-ms-code-generation-settings",
|
||||||
'x-ms-client-name',
|
"x-ms-client-name",
|
||||||
'x-ms-parameter-location',
|
"x-ms-parameter-location",
|
||||||
'x-ms-original',
|
"x-ms-original",
|
||||||
'x-ms-requestBody-name',
|
"x-ms-requestBody-name",
|
||||||
'x-ms-requestBody-index',
|
"x-ms-requestBody-index",
|
||||||
'x-ms-api-version',
|
"x-ms-api-version",
|
||||||
'x-ms-text'
|
"x-ms-text",
|
||||||
];
|
];
|
||||||
|
|
||||||
// ref: https://www.w3schools.com/charsets/ref_html_ascii.asp
|
// ref: https://www.w3schools.com/charsets/ref_html_ascii.asp
|
||||||
const specialCharacterMapping: { [character: string]: string } = {
|
const specialCharacterMapping: { [character: string]: string } = {
|
||||||
'!': 'exclamation mark',
|
"!": "exclamation mark",
|
||||||
'"': 'quotation mark',
|
'"': "quotation mark",
|
||||||
'#': 'number sign',
|
"#": "number sign",
|
||||||
'$': 'dollar sign',
|
"$": "dollar sign",
|
||||||
'%': 'percent sign',
|
"%": "percent sign",
|
||||||
'&': 'ampersand',
|
"&": "ampersand",
|
||||||
'\'': 'apostrophe',
|
"'": "apostrophe",
|
||||||
'(': 'left parenthesis',
|
"(": "left parenthesis",
|
||||||
')': 'right parenthesis',
|
")": "right parenthesis",
|
||||||
'*': 'asterisk',
|
"*": "asterisk",
|
||||||
'+': 'plus sign',
|
"+": "plus sign",
|
||||||
',': 'comma',
|
",": "comma",
|
||||||
'-': 'hyphen',
|
"-": "hyphen",
|
||||||
'.': 'period',
|
".": "period",
|
||||||
'/': 'slash',
|
"/": "slash",
|
||||||
':': 'colon',
|
":": "colon",
|
||||||
';': 'semicolon',
|
";": "semicolon",
|
||||||
'<': 'less-than',
|
"<": "less-than",
|
||||||
'=': 'equals-to',
|
"=": "equals-to",
|
||||||
'>': 'greater-than',
|
">": "greater-than",
|
||||||
'?': 'question mark',
|
"?": "question mark",
|
||||||
'@': 'at sign',
|
"@": "at sign",
|
||||||
'[': 'left square bracket',
|
"[": "left square bracket",
|
||||||
'\\': 'backslash',
|
"\\": "backslash",
|
||||||
']': 'right square bracket',
|
"]": "right square bracket",
|
||||||
'^': 'caret',
|
"^": "caret",
|
||||||
'_': 'underscore',
|
"_": "underscore",
|
||||||
'`': 'grave accent',
|
"`": "grave accent",
|
||||||
'{': 'left curly brace',
|
"{": "left curly brace",
|
||||||
'|': 'vertical bar',
|
"|": "vertical bar",
|
||||||
'}': 'right curly brace',
|
"}": "right curly brace",
|
||||||
'~': 'tilde'
|
"~": "tilde",
|
||||||
};
|
};
|
||||||
|
|
||||||
const apiVersionParameterNames = [
|
const apiVersionParameterNames = ["api-version", "apiversion", "x-ms-api-version", "x-ms-version"];
|
||||||
'api-version',
|
|
||||||
'apiversion',
|
|
||||||
'x-ms-api-version',
|
|
||||||
'x-ms-version'
|
|
||||||
];
|
|
||||||
|
|
||||||
export function getValidEnumValueName(originalString: string): string {
|
export function getValidEnumValueName(originalString: string): string {
|
||||||
if (typeof originalString === 'string') {
|
if (typeof originalString === "string") {
|
||||||
return !originalString.match(/[A-Za-z0-9]/g) ?
|
return !originalString.match(/[A-Za-z0-9]/g)
|
||||||
getPascalIdentifier(originalString.split('').map(x => specialCharacterMapping[x]).join(' '))
|
? getPascalIdentifier(
|
||||||
|
originalString
|
||||||
|
.split("")
|
||||||
|
.map((x) => specialCharacterMapping[x])
|
||||||
|
.join(" "),
|
||||||
|
)
|
||||||
: originalString;
|
: originalString;
|
||||||
}
|
}
|
||||||
return originalString;
|
return originalString;
|
||||||
|
@ -79,11 +86,10 @@ export function getValidEnumValueName(originalString: string): string {
|
||||||
|
|
||||||
export class Interpretations {
|
export class Interpretations {
|
||||||
isTrue(value: any) {
|
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) {
|
getConstantValue(schema: OpenAPI.Schema, value: any) {
|
||||||
|
|
||||||
switch (schema.type) {
|
switch (schema.type) {
|
||||||
case JsonType.String:
|
case JsonType.String:
|
||||||
switch (schema.format) {
|
switch (schema.format) {
|
||||||
|
@ -125,7 +131,7 @@ export class Interpretations {
|
||||||
return value;
|
return value;
|
||||||
|
|
||||||
case StringFormat.Password:
|
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:
|
case StringFormat.OData:
|
||||||
return value;
|
return value;
|
||||||
|
@ -137,7 +143,9 @@ export class Interpretations {
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// console.error(`String schema '${name}' with unknown format: '${schema.format}' is treated as simple string.`);
|
// 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:
|
case JsonType.Boolean:
|
||||||
|
@ -153,41 +161,48 @@ export class Interpretations {
|
||||||
|
|
||||||
isApiVersionParameter(parameter: OpenAPI.Parameter): boolean {
|
isApiVersionParameter(parameter: OpenAPI.Parameter): boolean {
|
||||||
// Always let x-ms-api-version override the check
|
// Always let x-ms-api-version override the check
|
||||||
if (parameter['x-ms-api-version'] !== undefined) {
|
if (parameter["x-ms-api-version"] !== undefined) {
|
||||||
return !!parameter['x-ms-api-version'] === true;
|
return !!parameter["x-ms-api-version"] === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// It's an api-version parameter if it's a query param with an expected name
|
// 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> {
|
getEnumChoices(schema: OpenAPI.Schema): Array<ChoiceValue> {
|
||||||
if (schema && schema.enum) {
|
if (schema && schema.enum) {
|
||||||
const xmse = <XMSEnum>schema['x-ms-enum'];
|
const xmse = <XMSEnum>schema["x-ms-enum"];
|
||||||
|
|
||||||
return xmse && xmse.values ?
|
return xmse && xmse.values
|
||||||
xmse.values.map((each) => {
|
? xmse.values.map((each) => {
|
||||||
const name = getValidEnumValueName((each.name !== undefined) ? each.name : each.value);
|
const name = getValidEnumValueName(each.name !== undefined ? each.name : each.value);
|
||||||
const value = this.getConstantValue(schema, each.value);
|
const value = this.getConstantValue(schema, each.value);
|
||||||
return new ChoiceValue(`${name}`, each.description || ``, value);
|
return new ChoiceValue(`${name}`, each.description || ``, value);
|
||||||
}) :
|
})
|
||||||
schema.enum.map(each => {
|
: schema.enum.map((each) => {
|
||||||
const name = getValidEnumValueName(each);
|
const name = getValidEnumValueName(each);
|
||||||
const value = this.getConstantValue(schema, each);
|
const value = this.getConstantValue(schema, each);
|
||||||
return new ChoiceValue(`${name}`, ``, value);
|
return new ChoiceValue(`${name}`, ``, value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
isEmptyObject(schema: OpenAPI.Schema): boolean {
|
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 {
|
getSerialization(schema: OpenAPI.Schema): any | undefined {
|
||||||
const xml = this.getXmlSerialization(schema);
|
const xml = this.getXmlSerialization(schema);
|
||||||
if (xml) {
|
if (xml) {
|
||||||
return {
|
return {
|
||||||
xml
|
xml,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -195,18 +210,18 @@ export class Interpretations {
|
||||||
|
|
||||||
getXmlSerialization(schema: OpenAPI.Schema): XmlSerlializationFormat | undefined {
|
getXmlSerialization(schema: OpenAPI.Schema): XmlSerlializationFormat | undefined {
|
||||||
if (schema.xml) {
|
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'`);
|
throw new Error(`XML serialization for a schema cannot be in both 'text' and 'attribute'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attribute: schema.xml.attribute || false,
|
attribute: schema.xml.attribute || false,
|
||||||
wrapped: schema.xml.wrapped || 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,
|
name: schema.xml.name || undefined,
|
||||||
namespace: schema.xml.namespace || undefined,
|
namespace: schema.xml.namespace || undefined,
|
||||||
prefix: schema.xml.prefix || undefined,
|
prefix: schema.xml.prefix || undefined,
|
||||||
extensions: this.getExtensionProperties(schema.xml)
|
extensions: this.getExtensionProperties(schema.xml),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -218,18 +233,24 @@ export class Interpretations {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
getApiVersions(schema: OpenAPI.Schema | OpenAPI.HttpOperation | OpenAPI.PathItem): Array<ApiVersion> | undefined {
|
getApiVersions(schema: OpenAPI.Schema | OpenAPI.HttpOperation | OpenAPI.PathItem): Array<ApiVersion> | undefined {
|
||||||
if (schema['x-ms-metadata'] && schema['x-ms-metadata']['apiVersions']) {
|
if (schema["x-ms-metadata"] && schema["x-ms-metadata"]["apiVersions"]) {
|
||||||
const v = values(<Array<string>>schema['x-ms-metadata']['apiVersions']).select(each => SetType(ApiVersion, {
|
const v = values(<Array<string>>schema["x-ms-metadata"]["apiVersions"])
|
||||||
version: each.replace(/^-/, '').replace(/\+$/, ''),
|
.select((each) =>
|
||||||
range: each.startsWith('-') ? <any>'-' : each.endsWith('+') ? '+' : undefined
|
SetType(ApiVersion, {
|
||||||
})).toArray();
|
version: each.replace(/^-/, "").replace(/\+$/, ""),
|
||||||
|
range: each.startsWith("-") ? <any>"-" : each.endsWith("+") ? "+" : undefined,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.toArray();
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
getApiVersionValues(node: OpenAPI.Schema | OpenAPI.HttpOperation | OpenAPI.PathItem): Array<string> {
|
getApiVersionValues(node: OpenAPI.Schema | OpenAPI.HttpOperation | OpenAPI.PathItem): Array<string> {
|
||||||
if (node['x-ms-metadata'] && node['x-ms-metadata']['apiVersions']) {
|
if (node["x-ms-metadata"] && node["x-ms-metadata"]["apiVersions"]) {
|
||||||
return values(<Array<string>>node['x-ms-metadata']['apiVersions']).distinct().toArray();
|
return values(<Array<string>>node["x-ms-metadata"]["apiVersions"])
|
||||||
|
.distinct()
|
||||||
|
.toArray();
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
@ -240,32 +261,36 @@ export class Interpretations {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private session: Session<OpenAPI.Model>) {
|
constructor(private session: Session<OpenAPI.Model>) {}
|
||||||
}
|
|
||||||
|
|
||||||
xmsMeta(obj: any, key: string) {
|
xmsMeta(obj: any, key: string) {
|
||||||
const m = obj['x-ms-metadata'];
|
const m = obj["x-ms-metadata"];
|
||||||
return m ? m[key] : undefined;
|
return m ? m[key] : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
xmsMetaFallback(obj: any, obj2: any, key: string) {
|
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) {
|
splitOpId(opId: string) {
|
||||||
const p = opId.indexOf('_');
|
const p = opId.indexOf("_");
|
||||||
|
|
||||||
return p != -1 ? {
|
return p != -1
|
||||||
group: opId.substr(0, p),
|
? {
|
||||||
member: opId.substr(p + 1)
|
group: opId.substr(0, p),
|
||||||
} : {
|
member: opId.substr(p + 1),
|
||||||
group: '',
|
}
|
||||||
member: opId
|
: {
|
||||||
};
|
group: "",
|
||||||
|
member: opId,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
isBinarySchema(schema: OpenAPI.Schema | undefined) {
|
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) {
|
getOperationId(httpMethod: string, path: string, original: OpenAPI.HttpOperation) {
|
||||||
|
@ -275,24 +300,37 @@ export class Interpretations {
|
||||||
|
|
||||||
// synthesize from tags.
|
// synthesize from tags.
|
||||||
if (original.tags && length(original.tags) > 0) {
|
if (original.tags && length(original.tags) > 0) {
|
||||||
|
|
||||||
switch (length(original.tags)) {
|
switch (length(original.tags)) {
|
||||||
case 0:
|
case 0:
|
||||||
break;
|
break;
|
||||||
case 1:
|
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]}`);
|
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]}`);
|
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(
|
||||||
getDescription(defaultValue: string, original: OpenAPI.Extensions & { title?: string; summary?: string; description?: string }): string {
|
defaultValue: string,
|
||||||
|
original: OpenAPI.Extensions & { title?: string; summary?: string; description?: string },
|
||||||
|
): string {
|
||||||
if (original) {
|
if (original) {
|
||||||
return original.description || original.title || original.summary || defaultValue;
|
return original.description || original.title || original.summary || defaultValue;
|
||||||
}
|
}
|
||||||
|
@ -300,20 +338,25 @@ export class Interpretations {
|
||||||
}
|
}
|
||||||
|
|
||||||
getPreferredName(original: any, preferredName?: string, fallbackName?: string) {
|
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 {
|
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 */
|
/** gets the operation path from metadata, falls back to the OAI3 path key */
|
||||||
getPath(pathItem: OpenAPI.PathItem, operation: OpenAPI.HttpOperation, path: string) {
|
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
|
/** 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 {
|
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 {
|
getExtensionProperties(dictionary: Dictionary<any>, additional?: Dictionary<any>): Dictionary<any> | undefined {
|
||||||
|
@ -372,11 +415,11 @@ export class Interpretations {
|
||||||
return main;
|
return main;
|
||||||
}
|
}
|
||||||
getClientDefault(dictionary: Dictionary<any>, additional?: Dictionary<any>): string | number | boolean | undefined {
|
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 {
|
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) {
|
for (const each of removeKnownParameters) {
|
||||||
delete result[each];
|
delete result[each];
|
||||||
}
|
}
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -3,19 +3,18 @@
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { deserialize, serialize } from '@azure-tools/codegen';
|
import { deserialize, serialize } from "@azure-tools/codegen";
|
||||||
import { Host, startSession } from '@azure-tools/autorest-extension-base';
|
import { Host, startSession } from "@azure-tools/autorest-extension-base";
|
||||||
import * as OpenAPI from '@azure-tools/openapi';
|
import * as OpenAPI from "@azure-tools/openapi";
|
||||||
import { ModelerFour } from './modelerfour';
|
import { ModelerFour } from "./modelerfour";
|
||||||
import { codeModelSchema, CodeModel } from '@azure-tools/codemodel';
|
import { codeModelSchema, CodeModel } from "@azure-tools/codemodel";
|
||||||
|
|
||||||
|
|
||||||
export async function processRequest(host: Host) {
|
export async function processRequest(host: Host) {
|
||||||
const debug = await host.GetValue('debug') || false;
|
const debug = (await host.GetValue("debug")) || false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const session = await startSession<OpenAPI.Model>(host, undefined, undefined, 'prechecked-openapi-document');
|
const session = await startSession<OpenAPI.Model>(host, undefined, undefined, "prechecked-openapi-document");
|
||||||
const options = <any>await session.getValue('modelerfour', {});
|
const options = <any>await session.getValue("modelerfour", {});
|
||||||
|
|
||||||
// process
|
// process
|
||||||
const modeler = await new ModelerFour(session).init();
|
const modeler = await new ModelerFour(session).init();
|
||||||
|
@ -24,17 +23,16 @@ export async function processRequest(host: Host) {
|
||||||
const codeModel = modeler.process();
|
const codeModel = modeler.process();
|
||||||
|
|
||||||
// throw on errors.
|
// throw on errors.
|
||||||
if (!await session.getValue('ignore-errors', false)) {
|
if (!(await session.getValue("ignore-errors", false))) {
|
||||||
session.checkpoint();
|
session.checkpoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// output the model to the pipeline
|
// output the model to the pipeline
|
||||||
if (options['emit-yaml-tags'] !== false) {
|
if (options["emit-yaml-tags"] !== false) {
|
||||||
host.WriteFile('code-model-v4.yaml', serialize(codeModel, codeModelSchema), undefined, 'code-model-v4');
|
host.WriteFile("code-model-v4.yaml", serialize(codeModel, codeModelSchema), undefined, "code-model-v4");
|
||||||
}
|
}
|
||||||
if (options['emit-yaml-tags'] !== true) {
|
if (options["emit-yaml-tags"] !== true) {
|
||||||
host.WriteFile('code-model-v4-no-tags.yaml', serialize(codeModel), undefined, 'code-model-v4-no-tags');
|
host.WriteFile("code-model-v4-no-tags.yaml", serialize(codeModel), undefined, "code-model-v4-no-tags");
|
||||||
}
|
}
|
||||||
} catch (E) {
|
} catch (E) {
|
||||||
if (debug) {
|
if (debug) {
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node --max_old_space_size=4096 ./entrypoints/main.js",
|
"start": "node --max_old_space_size=4096 ./entrypoints/main.js",
|
||||||
"debug": "node --max_old_space_size=4096 --inspect-brk=localhost:9229 ./entrypoints/main.js",
|
"debug": "node --max_old_space_size=4096 --inspect-brk=localhost:9229 ./entrypoints/main.js",
|
||||||
"eslint-fix": "eslint . --fix --ext .ts",
|
"fix": "eslint . --fix --ext .ts",
|
||||||
"eslint": "eslint . --ext .ts",
|
"lint": "eslint . --ext .ts --max-warnings=0",
|
||||||
"static-link": "static-link --no-node-modules --debug",
|
"static-link": "static-link --no-node-modules --debug",
|
||||||
"watch": "tsc -p . --watch",
|
"watch": "tsc -p . --watch",
|
||||||
"build": "tsc -p .",
|
"build": "tsc -p .",
|
||||||
|
@ -62,7 +62,9 @@
|
||||||
"@azure-tools/linq": "~3.1.0",
|
"@azure-tools/linq": "~3.1.0",
|
||||||
"static-link": "^0.3.0",
|
"static-link": "^0.3.0",
|
||||||
"chalk": "2.3.0",
|
"chalk": "2.3.0",
|
||||||
"recursive-diff": "~1.0.6"
|
"recursive-diff": "~1.0.6",
|
||||||
|
"prettier": "~2.2.1",
|
||||||
|
"eslint-plugin-prettier": "~3.2.0"
|
||||||
},
|
},
|
||||||
"static-link": {
|
"static-link": {
|
||||||
"entrypoints": [],
|
"entrypoints": [],
|
||||||
|
|
|
@ -3,17 +3,17 @@
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { serialize } from '@azure-tools/codegen';
|
import { serialize } from "@azure-tools/codegen";
|
||||||
import { Host, startSession } from '@azure-tools/autorest-extension-base';
|
import { Host, startSession } from "@azure-tools/autorest-extension-base";
|
||||||
import { codeModelSchema, CodeModel } from '@azure-tools/codemodel';
|
import { codeModelSchema, CodeModel } from "@azure-tools/codemodel";
|
||||||
import { PreNamer } from './prenamer';
|
import { PreNamer } from "./prenamer";
|
||||||
|
|
||||||
export async function processRequest(host: Host) {
|
export async function processRequest(host: Host) {
|
||||||
const debug = await host.GetValue('debug') || false;
|
const debug = (await host.GetValue("debug")) || false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const session = await startSession<CodeModel>(host, {}, codeModelSchema);
|
const session = await startSession<CodeModel>(host, {}, codeModelSchema);
|
||||||
const options = <any>await session.getValue('modelerfour', {});
|
const options = <any>await session.getValue("modelerfour", {});
|
||||||
|
|
||||||
// process
|
// process
|
||||||
const plugin = await new PreNamer(session).init();
|
const plugin = await new PreNamer(session).init();
|
||||||
|
@ -22,11 +22,11 @@ export async function processRequest(host: Host) {
|
||||||
const result = plugin.process();
|
const result = plugin.process();
|
||||||
|
|
||||||
// output the model to the pipeline
|
// output the model to the pipeline
|
||||||
if (options['emit-yaml-tags'] !== false) {
|
if (options["emit-yaml-tags"] !== false) {
|
||||||
host.WriteFile('code-model-v4.yaml', serialize(result, codeModelSchema), undefined, 'code-model-v4');
|
host.WriteFile("code-model-v4.yaml", serialize(result, codeModelSchema), undefined, "code-model-v4");
|
||||||
}
|
}
|
||||||
if (options['emit-yaml-tags'] !== true) {
|
if (options["emit-yaml-tags"] !== true) {
|
||||||
host.WriteFile('code-model-v4-no-tags.yaml', serialize(result), undefined, 'code-model-v4-no-tags');
|
host.WriteFile("code-model-v4-no-tags.yaml", serialize(result), undefined, "code-model-v4-no-tags");
|
||||||
}
|
}
|
||||||
} catch (E) {
|
} catch (E) {
|
||||||
if (debug) {
|
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 {
|
||||||
import { Session } from '@azure-tools/autorest-extension-base';
|
CodeModel,
|
||||||
import { values, length, Dictionary, when, items } from '@azure-tools/linq';
|
Parameter,
|
||||||
import { removeSequentialDuplicates, fixLeadingNumber, deconstruct, selectName, Style, Styler, pascalCase } from '@azure-tools/codegen';
|
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>) {
|
function getNameOptions(typeName: string, components: Array<string>) {
|
||||||
const result = new Set<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>
|
// 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()];
|
return [...result.values()];
|
||||||
}
|
}
|
||||||
|
|
||||||
function isUnassigned(value: string) {
|
function isUnassigned(value: string) {
|
||||||
return !value || (value.indexOf('·') > -1);
|
return !value || value.indexOf("·") > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SetNameOptions {
|
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 = {
|
options = {
|
||||||
removeDuplicates: true,
|
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) {
|
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 = {
|
options = {
|
||||||
removeDuplicates: true,
|
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,
|
schema: Schema,
|
||||||
schemaNames: Set<string>,
|
schemaNames: Set<string>,
|
||||||
session: Session<CodeModel>,
|
session: Session<CodeModel>,
|
||||||
indexer: (schema: Schema, newName: string) => string =
|
indexer: (schema: Schema, newName: string) => string = (schema: Schema, proposedName: string) => proposedName,
|
||||||
(schema: Schema, proposedName: string) => proposedName
|
|
||||||
): void {
|
): void {
|
||||||
const schemaName = schema.language.default.name;
|
const schemaName = schema.language.default.name;
|
||||||
const maxDedupes = 1000;
|
const maxDedupes = 1000;
|
||||||
if (schemaNames.has(indexer(schema, schemaName))) {
|
if (schemaNames.has(indexer(schema, schemaName))) {
|
||||||
for (let i = 1; i <= maxDedupes; i++) {
|
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))) {
|
if (!schemaNames.has(indexer(schema, newName))) {
|
||||||
schema.language.default.name = newName;
|
schema.language.default.name = newName;
|
||||||
schemaNames.add(indexer(schema, 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
|
// We haven't seen the name before, add it
|
||||||
|
@ -81,7 +132,7 @@ function deduplicateSchemaName(
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PreNamer {
|
export class PreNamer {
|
||||||
codeModel: CodeModel
|
codeModel: CodeModel;
|
||||||
options: Dictionary<any> = {};
|
options: Dictionary<any> = {};
|
||||||
format = {
|
format = {
|
||||||
parameter: Style.camel,
|
parameter: Style.camel,
|
||||||
|
@ -97,20 +148,20 @@ export class PreNamer {
|
||||||
client: Style.pascal,
|
client: Style.pascal,
|
||||||
local: Style.camel,
|
local: Style.camel,
|
||||||
global: Style.pascal,
|
global: Style.pascal,
|
||||||
override: <Dictionary<string>>{}
|
override: <Dictionary<string>>{},
|
||||||
}
|
};
|
||||||
|
|
||||||
enum = 0;
|
enum = 0;
|
||||||
constant = 0;
|
constant = 0;
|
||||||
constructor(protected session: Session<CodeModel>) {
|
constructor(protected session: Session<CodeModel>) {
|
||||||
this.codeModel = session.model;// shadow(session.model, filename);
|
this.codeModel = session.model; // shadow(session.model, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
// get our configuration for this run.
|
// 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 naming = this.options.naming || {};
|
||||||
const maxPreserve = Number(naming['preserve-uppercase-max-length']) || 3;
|
const maxPreserve = Number(naming["preserve-uppercase-max-length"]) || 3;
|
||||||
this.format = {
|
this.format = {
|
||||||
parameter: Style.select(naming.parameter, Style.camel, maxPreserve),
|
parameter: Style.select(naming.parameter, Style.camel, maxPreserve),
|
||||||
property: Style.select(naming.property, 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),
|
type: Style.select(naming.type, Style.pascal, maxPreserve),
|
||||||
local: Style.select(naming.local, Style.camel, maxPreserve),
|
local: Style.select(naming.local, Style.camel, maxPreserve),
|
||||||
global: Style.select(naming.global, Style.pascal, maxPreserve),
|
global: Style.select(naming.global, Style.pascal, maxPreserve),
|
||||||
override: naming.override || {}
|
override: naming.override || {},
|
||||||
}
|
};
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
isUnassigned(value: string) {
|
isUnassigned(value: string) {
|
||||||
return !value || (value.indexOf('·') > -1);
|
return !value || value.indexOf("·") > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
process() {
|
process() {
|
||||||
if (this.options['prenamer'] === false) {
|
if (this.options["prenamer"] === false) {
|
||||||
return this.codeModel;
|
return this.codeModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
const deduplicateSchemaNames =
|
const deduplicateSchemaNames =
|
||||||
!!this.options['lenient-model-deduplication'] ||
|
!!this.options["lenient-model-deduplication"] || !!this.options["resolve-schema-name-collisons"];
|
||||||
!!this.options['resolve-schema-name-collisons'];
|
|
||||||
|
|
||||||
// choice
|
// choice
|
||||||
const choiceSchemaNames = new Set<string>();
|
const choiceSchemaNames = new Set<string>();
|
||||||
|
@ -153,7 +203,7 @@ export class PreNamer {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const choice of values(schema.choices)) {
|
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)) {
|
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);
|
setName(schema, this.format.type, schema.type, this.format.override);
|
||||||
|
|
||||||
if (isUnassigned(schema.language.default.description)) {
|
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
|
// dictionary
|
||||||
for (const schema of values(this.codeModel.schemas.dictionaries)) {
|
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)) {
|
if (isUnassigned(schema.language.default.description)) {
|
||||||
schema.language.default.description = `Dictionary of ${schema.elementType.language.default.name}`;
|
schema.language.default.description = `Dictionary of ${schema.elementType.language.default.name}`;
|
||||||
}
|
}
|
||||||
|
@ -244,52 +299,54 @@ export class PreNamer {
|
||||||
|
|
||||||
const objectSchemaNames = new Set<string>();
|
const objectSchemaNames = new Set<string>();
|
||||||
for (const schema of values(this.codeModel.schemas.objects)) {
|
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) {
|
if (deduplicateSchemaNames) {
|
||||||
deduplicateSchemaName(
|
deduplicateSchemaName(
|
||||||
schema,
|
schema,
|
||||||
objectSchemaNames,
|
objectSchemaNames,
|
||||||
this.session,
|
this.session,
|
||||||
(schema: Schema, proposedName: string) =>
|
(schema: Schema, proposedName: string) => `${schema.language.default.namespace || ""}.${proposedName}`,
|
||||||
`${(schema.language.default.namespace || "")}.${proposedName}`);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const property of values(schema.properties)) {
|
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>();
|
const groupSchemaNames = new Set<string>();
|
||||||
for (const schema of values(this.codeModel.schemas.groups)) {
|
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) {
|
if (deduplicateSchemaNames) {
|
||||||
deduplicateSchemaName(
|
deduplicateSchemaName(
|
||||||
schema,
|
schema,
|
||||||
groupSchemaNames,
|
groupSchemaNames,
|
||||||
this.session,
|
this.session,
|
||||||
(schema: Schema, proposedName: string) =>
|
(schema: Schema, proposedName: string) => `${schema.language.default.namespace || ""}.${proposedName}`,
|
||||||
`${(schema.language.default.namespace || "")}.${proposedName}`);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const property of values(schema.properties)) {
|
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)) {
|
for (const parameter of values(this.codeModel.globalParameters)) {
|
||||||
if (parameter.schema.type === SchemaType.Constant) {
|
if (parameter.schema.type === SchemaType.Constant) {
|
||||||
setName(parameter, this.format.constantParameter, '', this.format.override);
|
setName(parameter, this.format.constantParameter, "", this.format.override);
|
||||||
} else {
|
} else {
|
||||||
setName(parameter, this.format.parameter, '', this.format.override);
|
setName(parameter, this.format.parameter, "", this.format.override);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const operationGroup of this.codeModel.operationGroups) {
|
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) {
|
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);
|
this.setParameterNames(operation);
|
||||||
for (const request of values(operation.requests)) {
|
for (const request of values(operation.requests)) {
|
||||||
|
@ -326,23 +383,20 @@ export class PreNamer {
|
||||||
private setParameterNames(parameterContainer: Operation | Request) {
|
private setParameterNames(parameterContainer: Operation | Request) {
|
||||||
for (const parameter of values(parameterContainer.signatureParameters)) {
|
for (const parameter of values(parameterContainer.signatureParameters)) {
|
||||||
if (parameter.schema.type === SchemaType.Constant) {
|
if (parameter.schema.type === SchemaType.Constant) {
|
||||||
setName(parameter, this.format.constantParameter, '', this.format.override);
|
setName(parameter, this.format.constantParameter, "", this.format.override);
|
||||||
}
|
} else {
|
||||||
else {
|
setName(parameter, this.format.parameter, "", this.format.override);
|
||||||
setName(parameter, this.format.parameter, '', this.format.override);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const parameter of values(parameterContainer.parameters)) {
|
for (const parameter of values(parameterContainer.parameters)) {
|
||||||
if ((parameterContainer.signatureParameters ?? []).indexOf(parameter) === -1) {
|
if ((parameterContainer.signatureParameters ?? []).indexOf(parameter) === -1) {
|
||||||
if (parameter.schema.type === SchemaType.Constant) {
|
if (parameter.schema.type === SchemaType.Constant) {
|
||||||
setName(parameter, this.format.constantParameter, '', this.format.override);
|
setName(parameter, this.format.constantParameter, "", this.format.override);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
if (parameter.implementation === ImplementationLocation.Client) {
|
if (parameter.implementation === ImplementationLocation.Client) {
|
||||||
setName(parameter, this.format.global, '', this.format.override);
|
setName(parameter, this.format.global, "", this.format.override);
|
||||||
}
|
} else {
|
||||||
else {
|
setName(parameter, this.format.local, "", this.format.override);
|
||||||
setName(parameter, this.format.local, '', this.format.override);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -352,13 +406,13 @@ export class PreNamer {
|
||||||
private setResponseHeaderNames(response: Response) {
|
private setResponseHeaderNames(response: Response) {
|
||||||
if (response.protocol.http) {
|
if (response.protocol.http) {
|
||||||
for (const { value: header } of items(response.protocol.http.headers)) {
|
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() {
|
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)) {
|
for (const request of values(operation.requests)) {
|
||||||
const parameters = values(operation.signatureParameters).concat(values(request.signatureParameters));
|
const parameters = values(operation.signatureParameters).concat(values(request.signatureParameters));
|
||||||
|
|
||||||
|
@ -380,21 +434,22 @@ export class PreNamer {
|
||||||
for (const parameter of collisions) {
|
for (const parameter of collisions) {
|
||||||
let options = [parameter.language.default.name];
|
let options = [parameter.language.default.name];
|
||||||
if (isVirtualParameter(parameter)) {
|
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));
|
parameter.language.default.name = this.format.parameter(selectName(options, usedNames));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fixCollisions(schema: ObjectSchema) {
|
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);
|
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 inherited = [...getAllParentProperties(schema)];
|
||||||
|
|
||||||
const all = [...owned, ...inherited, ...flattened];
|
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)) */) {
|
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));
|
const ct = inlined.get(this.format.property(each.language.default.name));
|
||||||
if (ct && ct > 1) {
|
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));
|
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 { serialize } from "@azure-tools/codegen";
|
||||||
import { values, items, length, Dictionary, refCount, clone, keys } from '@azure-tools/linq';
|
import { Host, startSession } from "@azure-tools/autorest-extension-base";
|
||||||
import { Model as oai3, Refable, Dereferenced, dereference, Schema, PropertyDetails, JsonType, StringFormat } from '@azure-tools/openapi';
|
import { Interpretations } from "../modeler/interpretations";
|
||||||
|
|
||||||
import { serialize } from '@azure-tools/codegen';
|
import { getDiff } from "recursive-diff";
|
||||||
import { Host, startSession } from '@azure-tools/autorest-extension-base';
|
|
||||||
import { Interpretations } from '../modeler/interpretations';
|
|
||||||
|
|
||||||
import { getDiff } from 'recursive-diff'
|
|
||||||
|
|
||||||
export async function processRequest(host: Host) {
|
export async function processRequest(host: Host) {
|
||||||
const debug = await host.GetValue('debug') || false;
|
const debug = (await host.GetValue("debug")) || false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const session = await startSession<oai3>(host);
|
const session = await startSession<oai3>(host);
|
||||||
|
@ -23,12 +31,22 @@ export async function processRequest(host: Host) {
|
||||||
const result = plugin.process();
|
const result = plugin.process();
|
||||||
|
|
||||||
// throw on errors.
|
// throw on errors.
|
||||||
if (!await session.getValue('ignore-errors', false)) {
|
if (!(await session.getValue("ignore-errors", false))) {
|
||||||
session.checkpoint();
|
session.checkpoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
host.WriteFile('prechecked-openapi-document.yaml', serialize(result, { sortKeys: false }), undefined, 'prechecked-openapi-document');
|
host.WriteFile(
|
||||||
host.WriteFile('original-openapi-document.yaml', serialize(input, { sortKeys: false }), undefined, 'openapi-document');
|
"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) {
|
} catch (E) {
|
||||||
if (debug) {
|
if (debug) {
|
||||||
console.error(`${__filename} - FAILURE ${JSON.stringify(E)} ${E.stack}`);
|
console.error(`${__filename} - FAILURE ${JSON.stringify(E)} ${E.stack}`);
|
||||||
|
@ -43,14 +61,14 @@ export class QualityPreChecker {
|
||||||
protected interpret: Interpretations;
|
protected interpret: Interpretations;
|
||||||
|
|
||||||
constructor(protected session: Session<oai3>) {
|
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);
|
this.interpret = new Interpretations(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
// get our configuration for this run.
|
// get our configuration for this run.
|
||||||
this.options = await this.session.getValue('modelerfour', {});
|
this.options = await this.session.getValue("modelerfour", {});
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -59,35 +77,37 @@ export class QualityPreChecker {
|
||||||
return dereference(this.input, item);
|
return dereference(this.input, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
getProperties(schema: Schema) {
|
getProperties(schema: Schema) {
|
||||||
return items(schema.properties).select(each => ({
|
return items(schema.properties).select((each) => ({
|
||||||
key: each.key,
|
key: each.key,
|
||||||
name: <string>this.interpret.getPreferredName(each.value, 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);
|
//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 }> {
|
getSchemasFromArray(
|
||||||
return values(schemas).select(a => {
|
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);
|
const { instance: schema, name } = this.resolve(a);
|
||||||
return {
|
return {
|
||||||
name: this.interpret.getName(name, schema),
|
name: this.interpret.getName(name, schema),
|
||||||
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)) {
|
for (const parent of this.getSchemasFromArray(tag, schema.allOf)) {
|
||||||
yield parent;
|
yield parent;
|
||||||
yield* this.getAllParents(parent.name, parent.schema);
|
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)) {
|
for (const parent of this.getSchemasFromArray(tag, schema.allOf)) {
|
||||||
yield* this.getAllParents(parent.name, parent.schema);
|
yield* this.getAllParents(parent.name, parent.schema);
|
||||||
}
|
}
|
||||||
|
@ -99,40 +119,51 @@ export class QualityPreChecker {
|
||||||
}
|
}
|
||||||
completed.add(schema);
|
completed.add(schema);
|
||||||
|
|
||||||
|
|
||||||
if (schema.allOf && schema.properties) {
|
if (schema.allOf && schema.properties) {
|
||||||
|
|
||||||
const myProperties = this.getProperties(schema).toArray();
|
const myProperties = this.getProperties(schema).toArray();
|
||||||
|
|
||||||
for (const { name: parentName, schema: parentSchema } of this.getAllParents(schemaName, schema)) {
|
for (const { name: parentName, schema: parentSchema } of this.getAllParents(schemaName, schema)) {
|
||||||
this.checkForHiddenProperties(parentName, parentSchema, completed);
|
this.checkForHiddenProperties(parentName, parentSchema, completed);
|
||||||
|
|
||||||
for (const { key, name: propName, property: parentProp } of this.getProperties(parentSchema)) {
|
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) {
|
if (myProp) {
|
||||||
// check if the only thing different is the description.
|
// 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) {
|
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.
|
// 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];
|
delete schema.properties[myProp.key];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (diff.length === 1) {
|
if (diff.length === 1) {
|
||||||
// special case to yell about readonly changes
|
// special case to yell about readonly changes
|
||||||
if (diff[0].path[0] === 'readOnly') {
|
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']);
|
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];
|
delete schema.properties[myProp.key];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (diff.length > 0) {
|
if (diff.length > 0) {
|
||||||
const details = diff.map(each => `${each.path.join('.')} => '${each.op === 'delete' ? '<removed>' : each.val}'`).join(',');
|
const details = diff
|
||||||
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']);
|
.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;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,13 +179,15 @@ export class QualityPreChecker {
|
||||||
completed.add(schema);
|
completed.add(schema);
|
||||||
|
|
||||||
if (schema.allOf) {
|
if (schema.allOf) {
|
||||||
|
|
||||||
const grandParents = [...this.getGrandParents(schemaName, schema)];
|
const grandParents = [...this.getGrandParents(schemaName, schema)];
|
||||||
const direct = [...this.getSchemasFromArray(schemaName, schema.allOf)];
|
const direct = [...this.getSchemasFromArray(schemaName, schema.allOf)];
|
||||||
|
|
||||||
for (const myParent of direct) {
|
for (const myParent of direct) {
|
||||||
for (const duplicate of grandParents.filter(each => each.schema === myParent.schema)) {
|
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']);
|
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;
|
return false;
|
||||||
|
|
||||||
case JsonType.String:
|
case JsonType.String:
|
||||||
return schema.enum || schema['x-ms-enum'];
|
return schema.enum || schema["x-ms-enum"];
|
||||||
|
|
||||||
case JsonType.Object:
|
case JsonType.Object:
|
||||||
// empty objects don't worry.
|
// empty objects don't worry.
|
||||||
|
@ -178,30 +211,33 @@ export class QualityPreChecker {
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
default:
|
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 {
|
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
|
// Returns true if scanning should be restarted
|
||||||
const innerCheckForDuplicateSchemas = (): any => {
|
const innerCheckForDuplicateSchemas = (): any => {
|
||||||
const errors = new Set<string>();
|
const errors = new Set<string>();
|
||||||
if (this.input.components && this.input.components.schemas) {
|
if (this.input.components && this.input.components.schemas) {
|
||||||
const dupedNames = items(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(
|
.groupBy(
|
||||||
// Make sure to check x-ms-client-name first to see if the schema is already being renamed
|
// 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.value.instance["x-ms-client-name"] || each.value.instance["x-ms-metadata"]?.name,
|
||||||
each => each);
|
(each) => each,
|
||||||
|
);
|
||||||
|
|
||||||
for (const [name, schemas] of dupedNames.entries()) {
|
for (const [name, schemas] of dupedNames.entries()) {
|
||||||
if (name && schemas.length > 1) {
|
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) {
|
if (diff.length === 0) {
|
||||||
// found two schemas that are indeed the same.
|
// found two schemas that are indeed the same.
|
||||||
|
@ -210,42 +246,67 @@ export class QualityPreChecker {
|
||||||
|
|
||||||
delete this.input.components.schemas[schemas[1].key];
|
delete this.input.components.schemas[schemas[1].key];
|
||||||
const text = JSON.stringify(this.input);
|
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
|
// update metadata to match
|
||||||
if (this.input?.components?.schemas?.[schemas[0].key]) {
|
if (this.input?.components?.schemas?.[schemas[0].key]) {
|
||||||
|
const primarySchema = this.resolve(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 primaryMetadata = primarySchema.instance['x-ms-metadata'];
|
const secondaryMetadata = schemas[1].value.instance["x-ms-metadata"];
|
||||||
const secondaryMetadata = schemas[1].value.instance['x-ms-metadata'];
|
|
||||||
|
|
||||||
if (primaryMetadata && secondaryMetadata) {
|
if (primaryMetadata && secondaryMetadata) {
|
||||||
primaryMetadata.apiVersions = [...new Set<string>([...primaryMetadata.apiVersions || [], ...secondaryMetadata.apiVersions || []])]
|
primaryMetadata.apiVersions = [
|
||||||
primaryMetadata.filename = [...new Set<string>([...primaryMetadata.filename || [], ...secondaryMetadata.filename || []])]
|
...new Set<string>([
|
||||||
primaryMetadata.originalLocations = [...new Set<string>([...primaryMetadata.originalLocations || [], ...secondaryMetadata.originalLocations || []])]
|
...(primaryMetadata.apiVersions || []),
|
||||||
primaryMetadata['x-ms-secondary-file'] = !(!primaryMetadata['x-ms-secondary-file'] || !secondaryMetadata['x-ms-secondary-file'])
|
...(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
|
// Restart the scan now that the duplicate has been removed
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// it may not be identical, but if it's not an object, I'm not sure we care too much.
|
// 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))) {
|
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');
|
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) {
|
if (diff.length > 0) {
|
||||||
const details = diff.map(each => {
|
const details = diff
|
||||||
const path = each.path.join('.');
|
.map((each) => {
|
||||||
let iValue = each.op === 'add' ? '<none>' : JSON.stringify(each.oldVal);
|
const path = each.path.join(".");
|
||||||
if (each.op !== 'update') {
|
let iValue = each.op === "add" ? "<none>" : JSON.stringify(each.oldVal);
|
||||||
const v = rdiff.find(each => each.path.join('.') === path)
|
if (each.op !== "update") {
|
||||||
iValue = JSON.stringify(v?.val);
|
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}`;
|
const nValue = each.op === "delete" ? "<none>" : JSON.stringify(each.val);
|
||||||
}).join(',');
|
return `${path}: ${iValue} => ${nValue}`;
|
||||||
|
})
|
||||||
|
.join(",");
|
||||||
errors.add(`Duplicate Schema named ${name} -- ${details} `);
|
errors.add(`Duplicate Schema named ${name} -- ${details} `);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -256,15 +317,18 @@ export class QualityPreChecker {
|
||||||
|
|
||||||
for (const each of errors) {
|
for (const each of errors) {
|
||||||
// Allow duplicate schemas if requested
|
// Allow duplicate schemas if requested
|
||||||
if (!!this.options["lenient-model-deduplication"]) {
|
if (this.options["lenient-model-deduplication"]) {
|
||||||
this.session.warning(each, ['PreCheck', 'DuplicateSchema']);
|
this.session.warning(each, ["PreCheck", "DuplicateSchema"]);
|
||||||
} else {
|
} 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()) {
|
while (innerCheckForDuplicateSchemas()) {
|
||||||
// Loop until the scan is complete
|
// Loop until the scan is complete
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,7 +338,10 @@ export class QualityPreChecker {
|
||||||
fixUpSchemasThatUseAllOfInsteadOfJustRef() {
|
fixUpSchemasThatUseAllOfInsteadOfJustRef() {
|
||||||
const schemas = this.input.components?.schemas;
|
const schemas = this.input.components?.schemas;
|
||||||
if (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
|
// we're looking for schemas that offer no possible value
|
||||||
// because they just use allOf instead of $ref
|
// because they just use allOf instead of $ref
|
||||||
if (!schema.type || schema.type === JsonType.Object) {
|
if (!schema.type || schema.type === JsonType.Object) {
|
||||||
|
@ -289,15 +356,26 @@ export class QualityPreChecker {
|
||||||
const $ref = schema?.allOf?.[0]?.$ref;
|
const $ref = schema?.allOf?.[0]?.$ref;
|
||||||
|
|
||||||
const text = JSON.stringify(this.input);
|
const text = JSON.stringify(this.input);
|
||||||
this.input = JSON.parse(text.replace(new RegExp(`"\\#\\/components\\/schemas\\/${key}"`, 'g'), `"${$ref}"`));
|
this.input = JSON.parse(
|
||||||
const location = schema['x-ms-metadata'].originalLocations[0].replace(/^.*\//, '')
|
text.replace(new RegExp(`"\\#\\/components\\/schemas\\/${key}"`, "g"), `"${$ref}"`),
|
||||||
if (schema['x-internal-autorest-anonymous-schema']) {
|
);
|
||||||
|
const location = schema["x-ms-metadata"].originalLocations[0].replace(/^.*\//, "");
|
||||||
|
if (schema["x-internal-autorest-anonymous-schema"]) {
|
||||||
delete schemas[key];
|
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 {
|
} else {
|
||||||
// NOTE: Disabled removing of non-anonymous schema for now until
|
// NOTE: Disabled removing of non-anonymous schema for now until
|
||||||
// it has been discussed in Azure/autorest.modelerfour#278
|
// 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() {
|
fixUpObjectsWithoutType() {
|
||||||
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) =>
|
||||||
if (<any>schema.type === 'file' || <any>schema.format === 'file' || <any>schema.format === 'binary') {
|
this.resolve(s),
|
||||||
|
)) {
|
||||||
|
if (<any>schema.type === "file" || <any>schema.format === "file" || <any>schema.format === "binary") {
|
||||||
// handle inconsistency in file format handling.
|
// handle inconsistency in file format handling.
|
||||||
this.session.hint(
|
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.`,
|
`'The schema ${schema?.["x-ms-metadata"]?.name || name} with 'type: ${schema.type}', format: ${
|
||||||
['PreCheck', 'BinarySchema'], schema);
|
schema.format
|
||||||
|
}' will be treated as a binary blob for binary media types.`,
|
||||||
|
["PreCheck", "BinarySchema"],
|
||||||
|
schema,
|
||||||
|
);
|
||||||
schema.type = JsonType.String;
|
schema.type = JsonType.String;
|
||||||
schema.format = StringFormat.Binary;
|
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
|
// 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.
|
// 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;
|
schema.type = JsonType.Object;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (schema.additionalProperties) {
|
if (schema.additionalProperties) {
|
||||||
// this looks like it's going to be a dictionary
|
// this looks like it's going to be a dictionary
|
||||||
// we'll mark it as object and let the processObjectSchema sort it out.
|
// 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;
|
schema.type = JsonType.Object;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -338,7 +434,13 @@ export class QualityPreChecker {
|
||||||
if (schema.allOf || schema.anyOf || schema.oneOf) {
|
if (schema.allOf || schema.anyOf || schema.oneOf) {
|
||||||
// if the model has properties, then we're going to assume they meant to say JsonType.object
|
// 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.
|
// 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;
|
schema.type = JsonType.Object;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -348,9 +450,7 @@ export class QualityPreChecker {
|
||||||
}
|
}
|
||||||
|
|
||||||
isEmptyObjectSchema(schema: Schema): boolean {
|
isEmptyObjectSchema(schema: Schema): boolean {
|
||||||
if (length(schema.properties) > 0 ||
|
if (length(schema.properties) > 0 || length(schema.allOf) > 0 || schema.additionalProperties === true) {
|
||||||
length(schema.allOf) > 0 ||
|
|
||||||
schema.additionalProperties === true) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,15 +465,21 @@ export class QualityPreChecker {
|
||||||
fixUpSchemasWithEmptyObjectParent() {
|
fixUpSchemasWithEmptyObjectParent() {
|
||||||
const schemas = this.input.components?.schemas;
|
const schemas = this.input.components?.schemas;
|
||||||
if (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 (schema.type === JsonType.Object) {
|
||||||
if (length(schema.allOf) > 1) {
|
if (length(schema.allOf) > 1) {
|
||||||
const schemaName = schema['x-ms-metadata']?.name || name;
|
const schemaName = schema["x-ms-metadata"]?.name || name;
|
||||||
schema.allOf = schema.allOf?.filter(p => {
|
schema.allOf = schema.allOf?.filter((p) => {
|
||||||
const parent = this.resolve(p).instance;
|
const parent = this.resolve(p).instance;
|
||||||
|
|
||||||
if (this.isEmptyObjectSchema(parent)) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,10 +491,8 @@ export class QualityPreChecker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
process() {
|
process() {
|
||||||
|
this.fixUpSchemasThatUseAllOfInsteadOfJustRef();
|
||||||
this.fixUpSchemasThatUseAllOfInsteadOfJustRef()
|
|
||||||
|
|
||||||
this.fixUpObjectsWithoutType();
|
this.fixUpObjectsWithoutType();
|
||||||
|
|
||||||
|
@ -397,17 +501,19 @@ export class QualityPreChecker {
|
||||||
this.checkForDuplicateSchemas();
|
this.checkForDuplicateSchemas();
|
||||||
|
|
||||||
let onlyOnce = new WeakSet<Schema>();
|
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);
|
this.checkForHiddenProperties(this.interpret.getName(name, schema), schema, onlyOnce);
|
||||||
}
|
}
|
||||||
|
|
||||||
onlyOnce = new WeakSet<Schema>();
|
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);
|
this.checkForDuplicateParents(this.interpret.getName(name, schema), schema, onlyOnce);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return this.input;
|
return this.input;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,8 @@
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { suite, test } from 'mocha-typescript';
|
import { suite, test } from "mocha-typescript";
|
||||||
import * as assert from 'assert';
|
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.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { suite, test } from 'mocha-typescript';
|
import { suite, test } from "mocha-typescript";
|
||||||
import * as assert from 'assert';
|
import * as assert from "assert";
|
||||||
import { readFile, readdir, isDirectory } from '@azure-tools/async-io';
|
import { readFile, readdir, isDirectory } from "@azure-tools/async-io";
|
||||||
import { deserialize, fail, serialize } from '@azure-tools/codegen';
|
import { deserialize, fail, serialize } from "@azure-tools/codegen";
|
||||||
import { startSession, Message, Channel } from '@azure-tools/autorest-extension-base';
|
import { startSession, Message, Channel } from "@azure-tools/autorest-extension-base";
|
||||||
import { values, keys, length } from '@azure-tools/linq';
|
import { values, keys, length } from "@azure-tools/linq";
|
||||||
import { Model } from '@azure-tools/openapi';
|
import { Model } from "@azure-tools/openapi";
|
||||||
import chalk from 'chalk';
|
import chalk from "chalk";
|
||||||
import { QualityPreChecker } from '../quality-precheck/prechecker';
|
import { QualityPreChecker } from "../quality-precheck/prechecker";
|
||||||
|
|
||||||
require('source-map-support').install();
|
require("source-map-support").install();
|
||||||
|
|
||||||
function addStyle(style: string, text: string): string {
|
function addStyle(style: string, text: string): string {
|
||||||
return `▌PUSH:${style}▐${text}▌POP▐`;
|
return `▌PUSH:${style}▐${text}▌POP▐`;
|
||||||
}
|
}
|
||||||
function compileStyledText(text: string): string {
|
function compileStyledText(text: string): string {
|
||||||
const styleStack = ['(x => x)'];
|
const styleStack = ["(x => x)"];
|
||||||
let result = '';
|
let result = "";
|
||||||
let consumedUpTo = 0;
|
let consumedUpTo = 0;
|
||||||
const appendPart = (end: number) => {
|
const appendPart = (end: number) => {
|
||||||
const CHALK = chalk;
|
const CHALK = chalk;
|
||||||
|
@ -31,10 +31,10 @@ function compileStyledText(text: string): string {
|
||||||
const commandRegex = /▌(.+?)▐/g;
|
const commandRegex = /▌(.+?)▐/g;
|
||||||
let i: RegExpExecArray | null;
|
let i: RegExpExecArray | null;
|
||||||
// eslint-disable-next-line no-cond-assign
|
// eslint-disable-next-line no-cond-assign
|
||||||
while (i = commandRegex.exec(text)) {
|
while ((i = commandRegex.exec(text))) {
|
||||||
const startIndex = i.index;
|
const startIndex = i.index;
|
||||||
const length = i[0].length;
|
const length = i[0].length;
|
||||||
const command = i[1].split(':');
|
const command = i[1].split(":");
|
||||||
|
|
||||||
// append up to here with current style
|
// append up to here with current style
|
||||||
appendPart(startIndex);
|
appendPart(startIndex);
|
||||||
|
@ -42,10 +42,10 @@ function compileStyledText(text: string): string {
|
||||||
// process command
|
// process command
|
||||||
consumedUpTo += length;
|
consumedUpTo += length;
|
||||||
switch (command[0]) {
|
switch (command[0]) {
|
||||||
case 'PUSH':
|
case "PUSH":
|
||||||
styleStack.push('CHALK.' + command[1]);
|
styleStack.push("CHALK." + command[1]);
|
||||||
break;
|
break;
|
||||||
case 'POP':
|
case "POP":
|
||||||
styleStack.pop();
|
styleStack.pop();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -55,28 +55,36 @@ function compileStyledText(text: string): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function color(text: string): string {
|
export function color(text: string): string {
|
||||||
return compileStyledText(text.
|
return compileStyledText(
|
||||||
replace(/\*\*(.*?)\*\*/gm, addStyle('bold', '$1')).
|
text
|
||||||
replace(/(\[.*?s\])/gm, addStyle('yellow.bold', '$1')).
|
.replace(/\*\*(.*?)\*\*/gm, addStyle("bold", "$1"))
|
||||||
replace(/^# (.*)/gm, addStyle('greenBright', '$1')).
|
.replace(/(\[.*?s\])/gm, addStyle("yellow.bold", "$1"))
|
||||||
replace(/^## (.*)/gm, addStyle('green', '$1')).
|
.replace(/^# (.*)/gm, addStyle("greenBright", "$1"))
|
||||||
replace(/^### (.*)/gm, addStyle('cyanBright', '$1')).
|
.replace(/^## (.*)/gm, addStyle("green", "$1"))
|
||||||
replace(/(https?:\/\/\S*)/gmi, addStyle('blue.bold.underline', '$1')).
|
.replace(/^### (.*)/gm, addStyle("cyanBright", "$1"))
|
||||||
replace(/__(.*)__/gm, addStyle('italic', '$1')).
|
.replace(/(https?:\/\/\S*)/gim, addStyle("blue.bold.underline", "$1"))
|
||||||
replace(/^>(.*)/gm, addStyle('cyan', ' $1')).
|
.replace(/__(.*)__/gm, addStyle("italic", "$1"))
|
||||||
replace(/^!(.*)/gm, addStyle('red.bold', ' $1')).
|
.replace(/^>(.*)/gm, addStyle("cyan", " $1"))
|
||||||
replace(/^(ERROR) (.*?):?(.*)/gmi, `${addStyle('red.bold', '$1')} ${addStyle('green', '$2')}:$3`).
|
.replace(/^!(.*)/gm, addStyle("red.bold", " $1"))
|
||||||
replace(/^(WARNING) (.*?):?(.*)/gmi, `${addStyle('yellow.bold', '$1')} ${addStyle('green', '$2')}:$3`).
|
.replace(/^(ERROR) (.*?):?(.*)/gim, `${addStyle("red.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(/^(WARNING) (.*?):?(.*)/gim, `${addStyle("yellow.bold", "$1")} ${addStyle("green", "$2")}:$3`)
|
||||||
replace(/`(.+?)`/gm, addStyle('gray', '$1')).
|
.replace(
|
||||||
replace(/"(.*?)"/gm, addStyle('gray', '"$1"')).
|
/^(\s* - \w*:\/\/\S*):(\d*):(\d*) (.*)/gm,
|
||||||
replace(/'(.*?)'/gm, addStyle('gray', '\'$1\'')));
|
`${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;
|
(<any>global).color = color;
|
||||||
|
|
||||||
let unexpectedErrorCount = 0;
|
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 = [];
|
const results = [];
|
||||||
for (const filename of files) {
|
for (const filename of files) {
|
||||||
const content = await readFile(`${folder}/${filename}`);
|
const content = await readFile(`${folder}/${filename}`);
|
||||||
|
@ -84,42 +92,56 @@ async function readData(folder: string, ...files: Array<string>): Promise<Array<
|
||||||
results.push({
|
results.push({
|
||||||
model,
|
model,
|
||||||
filename,
|
filename,
|
||||||
content
|
content,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function createTestSession<TInputModel>(
|
||||||
async function createTestSession<TInputModel>(config: any, folder: string, inputs: Array<string>, outputs: Array<string>) {
|
config: any,
|
||||||
|
folder: string,
|
||||||
|
inputs: Array<string>,
|
||||||
|
outputs: Array<string>,
|
||||||
|
) {
|
||||||
const filesInFolder = await readData(folder, ...inputs);
|
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>{};
|
const unexpected = <any>{};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
session: await startSession<TInputModel>({
|
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> => {
|
GetValue: async (key: string): Promise<any> => {
|
||||||
if (!key) {
|
if (!key) {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
return config[key];
|
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> => {
|
ProtectFiles: async (path: string): Promise<void> => {
|
||||||
// test
|
// test
|
||||||
},
|
},
|
||||||
WriteFile: (filename: string, content: string, sourceMap?: any, artifactType?: string): void => {
|
WriteFile: (filename: string, content: string, sourceMap?: any, artifactType?: string): void => {
|
||||||
// test
|
// test
|
||||||
},
|
},
|
||||||
Message: (message: Message): void => {
|
Message: (message: Message): void => {
|
||||||
if (expected[message.Channel]) {
|
if (expected[message.Channel]) {
|
||||||
const i = expected[message.Channel].indexOf(message.Text.trim());
|
const i = expected[message.Channel].indexOf(message.Text.trim());
|
||||||
|
|
||||||
if (i > -1) {
|
if (i > -1) {
|
||||||
// expected message found. remove it
|
// expected message found. remove it
|
||||||
expected[message.Channel].splice(i, 1);
|
expected[message.Channel].splice(i, 1);
|
||||||
if (expected[message.Channel].length === 0) {
|
if (expected[message.Channel].length === 0) {
|
||||||
delete expected[message.Channel];
|
delete expected[message.Channel];
|
||||||
|
@ -133,35 +155,40 @@ async function createTestSession<TInputModel>(config: any, folder: string, input
|
||||||
unexpected[message.Channel].push(message.Text);
|
unexpected[message.Channel].push(message.Text);
|
||||||
},
|
},
|
||||||
UpdateConfigurationFile: (filename: string, content: string): void => {
|
UpdateConfigurationFile: (filename: string, content: string): void => {
|
||||||
// test
|
// test
|
||||||
},
|
},
|
||||||
GetConfigurationFile: async (filename: string): Promise<string> => '',
|
GetConfigurationFile: async (filename: string): Promise<string> => "",
|
||||||
}),
|
}),
|
||||||
expected,
|
expected,
|
||||||
unexpected
|
unexpected,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@suite class Process {
|
@suite
|
||||||
|
class Process {
|
||||||
// to generate the oai3 doc from an oai2 input:
|
// 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
|
// 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/`);
|
const folders = await readdir(`${__dirname}/../../test/errors/`);
|
||||||
for (const each of folders) {
|
for (const each of folders) {
|
||||||
const folder = `${__dirname}/../../test/errors/${each}`;
|
const folder = `${__dirname}/../../test/errors/${each}`;
|
||||||
|
|
||||||
if (! await isDirectory(folder)) {
|
if (!(await isDirectory(folder))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
console.log(`Expecting Errors From: ${folder}`);
|
console.log(`Expecting Errors From: ${folder}`);
|
||||||
|
|
||||||
const cfg = {
|
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
|
// process OAI model
|
||||||
const prechecker = await new QualityPreChecker(session).init();
|
const prechecker = await new QualityPreChecker(session).init();
|
||||||
|
@ -169,9 +196,19 @@ async function createTestSession<TInputModel>(config: any, folder: string, input
|
||||||
// go!
|
// go!
|
||||||
await prechecker.process();
|
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.
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { suite, test } from 'mocha-typescript';
|
import { suite, test } from "mocha-typescript";
|
||||||
import * as assert from 'assert';
|
import * as assert from "assert";
|
||||||
import { ModelerFour } from '../modeler/modelerfour';
|
import { ModelerFour } from "../modeler/modelerfour";
|
||||||
import { readFile, writeFile, readdir, mkdir } from '@azure-tools/async-io';
|
import { readFile, writeFile, readdir, mkdir } from "@azure-tools/async-io";
|
||||||
import { deserialize, serialize, fail } from '@azure-tools/codegen';
|
import { deserialize, serialize, fail } from "@azure-tools/codegen";
|
||||||
import { startSession } from '@azure-tools/autorest-extension-base';
|
import { startSession } from "@azure-tools/autorest-extension-base";
|
||||||
import { values } from '@azure-tools/linq';
|
import { values } from "@azure-tools/linq";
|
||||||
import { CodeModel } from '@azure-tools/codemodel';
|
import { CodeModel } from "@azure-tools/codemodel";
|
||||||
import { Model } from '@azure-tools/openapi';
|
import { Model } from "@azure-tools/openapi";
|
||||||
import { codeModelSchema } from '@azure-tools/codemodel';
|
import { codeModelSchema } from "@azure-tools/codemodel";
|
||||||
import { ReadUri } from '@azure-tools/uri';
|
import { ReadUri } from "@azure-tools/uri";
|
||||||
import { PreNamer } from '../prenamer/prenamer';
|
import { PreNamer } from "../prenamer/prenamer";
|
||||||
import { Flattener } from '../flattener/flattener';
|
import { Flattener } from "../flattener/flattener";
|
||||||
import { Grouper } from '../grouper/grouper';
|
import { Grouper } from "../grouper/grouper";
|
||||||
import { Checker } from '../checker/checker';
|
import { Checker } from "../checker/checker";
|
||||||
import chalk from 'chalk';
|
import chalk from "chalk";
|
||||||
|
|
||||||
require('source-map-support').install();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
require("source-map-support").install();
|
||||||
|
|
||||||
function addStyle(style: string, text: string): string {
|
function addStyle(style: string, text: string): string {
|
||||||
return `▌PUSH:${style}▐${text}▌POP▐`;
|
return `▌PUSH:${style}▐${text}▌POP▐`;
|
||||||
}
|
}
|
||||||
function compileStyledText(text: string): string {
|
function compileStyledText(text: string): string {
|
||||||
const styleStack = ['(x => x)'];
|
const styleStack = ["(x => x)"];
|
||||||
let result = '';
|
let result = "";
|
||||||
let consumedUpTo = 0;
|
let consumedUpTo = 0;
|
||||||
const appendPart = (end: number) => {
|
const appendPart = (end: number) => {
|
||||||
const CHALK = chalk;
|
const CHALK = chalk;
|
||||||
|
@ -41,10 +38,10 @@ function compileStyledText(text: string): string {
|
||||||
const commandRegex = /▌(.+?)▐/g;
|
const commandRegex = /▌(.+?)▐/g;
|
||||||
let i: RegExpExecArray | null;
|
let i: RegExpExecArray | null;
|
||||||
// eslint-disable-next-line no-cond-assign
|
// eslint-disable-next-line no-cond-assign
|
||||||
while (i = commandRegex.exec(text)) {
|
while ((i = commandRegex.exec(text))) {
|
||||||
const startIndex = i.index;
|
const startIndex = i.index;
|
||||||
const length = i[0].length;
|
const length = i[0].length;
|
||||||
const command = i[1].split(':');
|
const command = i[1].split(":");
|
||||||
|
|
||||||
// append up to here with current style
|
// append up to here with current style
|
||||||
appendPart(startIndex);
|
appendPart(startIndex);
|
||||||
|
@ -52,10 +49,10 @@ function compileStyledText(text: string): string {
|
||||||
// process command
|
// process command
|
||||||
consumedUpTo += length;
|
consumedUpTo += length;
|
||||||
switch (command[0]) {
|
switch (command[0]) {
|
||||||
case 'PUSH':
|
case "PUSH":
|
||||||
styleStack.push('CHALK.' + command[1]);
|
styleStack.push("CHALK." + command[1]);
|
||||||
break;
|
break;
|
||||||
case 'POP':
|
case "POP":
|
||||||
styleStack.pop();
|
styleStack.pop();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -65,22 +62,27 @@ function compileStyledText(text: string): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function color(text: string): string {
|
export function color(text: string): string {
|
||||||
return compileStyledText(text.
|
return compileStyledText(
|
||||||
replace(/\*\*(.*?)\*\*/gm, addStyle('bold', '$1')).
|
text
|
||||||
replace(/(\[.*?s\])/gm, addStyle('yellow.bold', '$1')).
|
.replace(/\*\*(.*?)\*\*/gm, addStyle("bold", "$1"))
|
||||||
replace(/^# (.*)/gm, addStyle('greenBright', '$1')).
|
.replace(/(\[.*?s\])/gm, addStyle("yellow.bold", "$1"))
|
||||||
replace(/^## (.*)/gm, addStyle('green', '$1')).
|
.replace(/^# (.*)/gm, addStyle("greenBright", "$1"))
|
||||||
replace(/^### (.*)/gm, addStyle('cyanBright', '$1')).
|
.replace(/^## (.*)/gm, addStyle("green", "$1"))
|
||||||
replace(/(https?:\/\/\S*)/gmi, addStyle('blue.bold.underline', '$1')).
|
.replace(/^### (.*)/gm, addStyle("cyanBright", "$1"))
|
||||||
replace(/__(.*)__/gm, addStyle('italic', '$1')).
|
.replace(/(https?:\/\/\S*)/gim, addStyle("blue.bold.underline", "$1"))
|
||||||
replace(/^>(.*)/gm, addStyle('cyan', ' $1')).
|
.replace(/__(.*)__/gm, addStyle("italic", "$1"))
|
||||||
replace(/^!(.*)/gm, addStyle('red.bold', ' $1')).
|
.replace(/^>(.*)/gm, addStyle("cyan", " $1"))
|
||||||
replace(/^(ERROR) (.*?):?(.*)/gmi, `${addStyle('red.bold', '$1')} ${addStyle('green', '$2')}:$3`).
|
.replace(/^!(.*)/gm, addStyle("red.bold", " $1"))
|
||||||
replace(/^(WARNING) (.*?):?(.*)/gmi, `${addStyle('yellow.bold', '$1')} ${addStyle('green', '$2')}:$3`).
|
.replace(/^(ERROR) (.*?):?(.*)/gim, `${addStyle("red.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(/^(WARNING) (.*?):?(.*)/gim, `${addStyle("yellow.bold", "$1")} ${addStyle("green", "$2")}:$3`)
|
||||||
replace(/`(.+?)`/gm, addStyle('gray', '$1')).
|
.replace(
|
||||||
replace(/"(.*?)"/gm, addStyle('gray', '"$1"')).
|
/^(\s* - \w*:\/\/\S*):(\d*):(\d*) (.*)/gm,
|
||||||
replace(/'(.*?)'/gm, addStyle('gray', '\'$1\'')));
|
`${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;
|
(<any>global).color = color;
|
||||||
|
|
||||||
|
@ -88,7 +90,10 @@ let errorCount = 0;
|
||||||
|
|
||||||
const resources = `${__dirname}/../../test/resources/process`;
|
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 = [];
|
const results = [];
|
||||||
for (const filename of files) {
|
for (const filename of files) {
|
||||||
const content = await readFile(`${folder}/${filename}`);
|
const content = await readFile(`${folder}/${filename}`);
|
||||||
|
@ -96,128 +101,138 @@ async function readData(folder: string, ...files: Array<string>): Promise<Array<
|
||||||
results.push({
|
results.push({
|
||||||
model,
|
model,
|
||||||
filename,
|
filename,
|
||||||
content
|
content,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function cts<TInputModel>(config: any, filename: string, content: string) {
|
async function cts<TInputModel>(config: any, filename: string, content: string) {
|
||||||
const ii = [{
|
const ii = [
|
||||||
model: deserialize<any>(content, filename),
|
{
|
||||||
filename,
|
model: deserialize<any>(content, filename),
|
||||||
content
|
filename,
|
||||||
}];
|
content,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return await startSession<TInputModel>({
|
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> => {
|
GetValue: async (key: string): Promise<any> => {
|
||||||
if (!key) {
|
if (!key) {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
return config[key];
|
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> => {
|
ProtectFiles: async (path: string): Promise<void> => {
|
||||||
// test
|
// test
|
||||||
},
|
},
|
||||||
WriteFile: (filename: string, content: string, sourceMap?: any, artifactType?: string): void => {
|
WriteFile: (filename: string, content: string, sourceMap?: any, artifactType?: string): void => {
|
||||||
// test
|
// test
|
||||||
},
|
},
|
||||||
Message: (message: any): void => {
|
Message: (message: any): void => {
|
||||||
// test
|
// 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') {
|
if (message.Channel === "error") {
|
||||||
errorCount++;
|
errorCount++;
|
||||||
}
|
}
|
||||||
console.error(color(`${message.Channel} ${message.Text}`));
|
console.error(color(`${message.Channel} ${message.Text}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
UpdateConfigurationFile: (filename: string, content: string): void => {
|
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 ii = await readData(folder, ...inputs);
|
||||||
const oo = await readData(folder, ...outputs);
|
const oo = await readData(folder, ...outputs);
|
||||||
|
|
||||||
return await startSession<TInputModel>({
|
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> => {
|
GetValue: async (key: string): Promise<any> => {
|
||||||
if (!key) {
|
if (!key) {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
return config[key];
|
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> => {
|
ProtectFiles: async (path: string): Promise<void> => {
|
||||||
// test
|
// test
|
||||||
},
|
},
|
||||||
WriteFile: (filename: string, content: string, sourceMap?: any, artifactType?: string): void => {
|
WriteFile: (filename: string, content: string, sourceMap?: any, artifactType?: string): void => {
|
||||||
// test
|
// test
|
||||||
},
|
},
|
||||||
Message: (message: any): void => {
|
Message: (message: any): void => {
|
||||||
// test
|
// 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') {
|
if (message.Channel === "error") {
|
||||||
|
|
||||||
errorCount++;
|
errorCount++;
|
||||||
}
|
}
|
||||||
console.error(color(`${message.Channel} ${message.Text}`));
|
console.error(color(`${message.Channel} ${message.Text}`));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UpdateConfigurationFile: (filename: string, content: string): void => {
|
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) {
|
async function createPassThruSession(config: any, input: string, inputArtifactType: string) {
|
||||||
return await startSession<CodeModel>({
|
return await startSession<CodeModel>(
|
||||||
ReadFile: async (filename: string): Promise<string> => input,
|
{
|
||||||
GetValue: async (key: string): Promise<any> => {
|
ReadFile: async (filename: string): Promise<string> => input,
|
||||||
if (!key) {
|
GetValue: async (key: string): Promise<any> => {
|
||||||
return config;
|
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++;
|
|
||||||
}
|
}
|
||||||
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
|
codeModelSchema,
|
||||||
},
|
);
|
||||||
GetConfigurationFile: async (filename: string): Promise<string> => '',
|
|
||||||
}, {}, codeModelSchema);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@suite
|
@suite
|
||||||
class Process {
|
class Process {
|
||||||
@test
|
@test
|
||||||
async 'simple model test'() {
|
async "simple model test"() {
|
||||||
const session = await createTestSession<Model>({}, resources, ['input2.yaml'], ['output1.yaml']);
|
const session = await createTestSession<Model>({}, resources, ["input2.yaml"], ["output1.yaml"]);
|
||||||
|
|
||||||
// process OAI model
|
// process OAI model
|
||||||
const modeler = await new ModelerFour(session).init();
|
const modeler = await new ModelerFour(session).init();
|
||||||
|
@ -230,32 +245,32 @@ class Process {
|
||||||
|
|
||||||
//await (writeFile(`${__dirname}/../../output.yaml`, yaml));
|
//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
|
@test
|
||||||
async 'acceptance-suite'() {
|
async "acceptance-suite"() {
|
||||||
const folders = await readdir(`${__dirname}/../../test/scenarios/`);
|
const folders = await readdir(`${__dirname}/../../test/scenarios/`);
|
||||||
for (const each of folders) {
|
for (const each of folders) {
|
||||||
console.log(`Processing: ${each}`);
|
console.log(`Processing: ${each}`);
|
||||||
|
|
||||||
const cfg = {
|
const cfg = {
|
||||||
modelerfour: {
|
"modelerfour": {
|
||||||
'flatten-models': true,
|
"flatten-models": true,
|
||||||
'flatten-payloads': true,
|
"flatten-payloads": true,
|
||||||
'group-parameters': true,
|
"group-parameters": true,
|
||||||
'resolve-schema-name-collisons': true,
|
"resolve-schema-name-collisons": true,
|
||||||
'additional-checks': true,
|
"additional-checks": true,
|
||||||
//'always-create-content-type-parameter': true,
|
//'always-create-content-type-parameter': true,
|
||||||
naming: {
|
"naming": {
|
||||||
override: {
|
override: {
|
||||||
'$host': '$host',
|
$host: "$host",
|
||||||
'cmyk': 'CMYK'
|
cmyk: "CMYK",
|
||||||
},
|
},
|
||||||
local: "_ + camel",
|
local: "_ + camel",
|
||||||
constantParameter: 'pascal',
|
constantParameter: "pascal",
|
||||||
/*
|
/*
|
||||||
for when playing with python style settings :
|
for when playing with python style settings :
|
||||||
|
|
||||||
|
@ -268,12 +283,17 @@ class Process {
|
||||||
constant: 'uppercase',
|
constant: 'uppercase',
|
||||||
type: 'pascalcase',
|
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
|
// process OAI model
|
||||||
const modeler = await new ModelerFour(session).init();
|
const modeler = await new ModelerFour(session).init();
|
||||||
|
@ -283,24 +303,24 @@ class Process {
|
||||||
|
|
||||||
const yaml = serialize(codeModel, codeModelSchema);
|
const yaml = serialize(codeModel, codeModelSchema);
|
||||||
await mkdir(`${__dirname}/../../test/scenarios/${each}`);
|
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 flattened = await flattener.process();
|
||||||
const flatteneyaml = serialize(flattened, codeModelSchema);
|
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 grouped = await grouper.process();
|
||||||
const groupedYaml = serialize(grouped, codeModelSchema);
|
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 named = await namer.process();
|
||||||
const namedyaml = serialize(named, codeModelSchema);
|
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();
|
await checker.process();
|
||||||
|
|
||||||
assert(errorCount === 0, "Errors Encountered");
|
assert(errorCount === 0, "Errors Encountered");
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -9,7 +9,7 @@ import {
|
||||||
Schema,
|
Schema,
|
||||||
PropertyDetails,
|
PropertyDetails,
|
||||||
JsonType,
|
JsonType,
|
||||||
StringFormat
|
StringFormat,
|
||||||
} from "@azure-tools/openapi";
|
} from "@azure-tools/openapi";
|
||||||
import {
|
import {
|
||||||
createTestSession,
|
createTestSession,
|
||||||
|
@ -18,7 +18,7 @@ import {
|
||||||
addOperation,
|
addOperation,
|
||||||
response,
|
response,
|
||||||
InitialTestSpec,
|
InitialTestSpec,
|
||||||
responses
|
responses,
|
||||||
} from "./unitTestUtil";
|
} from "./unitTestUtil";
|
||||||
|
|
||||||
class PreCheckerClient {
|
class PreCheckerClient {
|
||||||
|
@ -29,7 +29,7 @@ class PreCheckerClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
static async create(spec: any): Promise<PreCheckerClient> {
|
static async create(spec: any): Promise<PreCheckerClient> {
|
||||||
const precheckerErrors: any[] = [];
|
const precheckerErrors: Array<any> = [];
|
||||||
const session = await createTestSession({}, spec, precheckerErrors);
|
const session = await createTestSession({}, spec, precheckerErrors);
|
||||||
const prechecker = await new QualityPreChecker(session).init();
|
const prechecker = await new QualityPreChecker(session).init();
|
||||||
|
|
||||||
|
@ -52,35 +52,29 @@ class PreChecker {
|
||||||
nullable: true,
|
nullable: true,
|
||||||
properties: {
|
properties: {
|
||||||
hack: {
|
hack: {
|
||||||
type: "boolean"
|
type: "boolean",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
addSchema(spec, "ChildSchema", {
|
addSchema(spec, "ChildSchema", {
|
||||||
type: "object",
|
type: "object",
|
||||||
allOf: [
|
allOf: [{ type: "object" }, { $ref: "#/components/schemas/ParentSchema" }],
|
||||||
{ type: "object" },
|
|
||||||
{ $ref: "#/components/schemas/ParentSchema" }
|
|
||||||
],
|
|
||||||
properties: {
|
properties: {
|
||||||
childOfHack: {
|
childOfHack: {
|
||||||
type: "integer"
|
type: "integer",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const client = await PreCheckerClient.create(spec);
|
const client = await PreCheckerClient.create(spec);
|
||||||
const model = client.result;
|
const model = client.result;
|
||||||
|
|
||||||
const childSchemaRef =
|
const childSchemaRef = model.components?.schemas && model.components?.schemas["ChildSchema"];
|
||||||
model.components?.schemas && model.components?.schemas["ChildSchema"];
|
|
||||||
if (childSchemaRef) {
|
if (childSchemaRef) {
|
||||||
const childSchema = client.resolve<Schema>(childSchemaRef);
|
const childSchema = client.resolve<Schema>(childSchemaRef);
|
||||||
assert.strictEqual(childSchema.instance.allOf?.length, 1);
|
assert.strictEqual(childSchema.instance.allOf?.length, 1);
|
||||||
const parent = client.resolve(
|
const parent = client.resolve(childSchema.instance.allOf && childSchema.instance.allOf[0]);
|
||||||
childSchema.instance.allOf && childSchema.instance.allOf[0]
|
|
||||||
);
|
|
||||||
assert.strictEqual(parent.name, "ParentSchema");
|
assert.strictEqual(parent.name, "ParentSchema");
|
||||||
} else {
|
} else {
|
||||||
assert.fail("No 'ChildSchema' found!");
|
assert.fail("No 'ChildSchema' found!");
|
||||||
|
|
|
@ -4,54 +4,37 @@ import { values, clone } from "@azure-tools/linq";
|
||||||
import { Model } from "@azure-tools/openapi";
|
import { Model } from "@azure-tools/openapi";
|
||||||
import { codeModelSchema } from "@azure-tools/codemodel";
|
import { codeModelSchema } from "@azure-tools/codemodel";
|
||||||
|
|
||||||
export async function createTestSession(
|
export async function createTestSession(config: any, openApiModel: any, messageList: Array<any>) {
|
||||||
config: any,
|
|
||||||
openApiModel: any,
|
|
||||||
messageList: any[]
|
|
||||||
) {
|
|
||||||
const openApiText = JSON.stringify(openApiModel);
|
const openApiText = JSON.stringify(openApiModel);
|
||||||
const ii = [
|
const ii = [
|
||||||
{
|
{
|
||||||
model: openApiModel as Model,
|
model: openApiModel as Model,
|
||||||
filename: "openapi-3.json",
|
filename: "openapi-3.json",
|
||||||
content: openApiText
|
content: openApiText,
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return await startSession<Model>(
|
return await startSession<Model>(
|
||||||
{
|
{
|
||||||
ReadFile: async (filename: string): Promise<string> =>
|
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> => {
|
GetValue: async (key: string): Promise<any> => {
|
||||||
if (!key) {
|
if (!key) {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
return config[key];
|
return config[key];
|
||||||
},
|
},
|
||||||
ListInputs: async (artifactType?: string): Promise<Array<string>> =>
|
ListInputs: async (artifactType?: string): Promise<Array<string>> => ii.map((each) => each.filename),
|
||||||
ii.map(each => each.filename),
|
|
||||||
|
|
||||||
ProtectFiles: async (path: string): Promise<void> => {
|
ProtectFiles: async (path: string): Promise<void> => {
|
||||||
// test
|
// test
|
||||||
},
|
},
|
||||||
WriteFile: (
|
WriteFile: (filename: string, content: string, sourceMap?: any, artifactType?: string): void => {
|
||||||
filename: string,
|
|
||||||
content: string,
|
|
||||||
sourceMap?: any,
|
|
||||||
artifactType?: string
|
|
||||||
): void => {
|
|
||||||
// test
|
// test
|
||||||
},
|
},
|
||||||
Message: (message: any): void => {
|
Message: (message: any): void => {
|
||||||
// test
|
// test
|
||||||
if (
|
if (message.Channel === "warning" || message.Channel === "error" || message.Channel === "verbose") {
|
||||||
message.Channel === "warning" ||
|
|
||||||
message.Channel === "error" ||
|
|
||||||
message.Channel === "verbose"
|
|
||||||
) {
|
|
||||||
if (message.Channel === "error") {
|
if (message.Channel === "error") {
|
||||||
messageList.push(message);
|
messageList.push(message);
|
||||||
}
|
}
|
||||||
|
@ -60,10 +43,10 @@ export async function createTestSession(
|
||||||
UpdateConfigurationFile: (filename: string, content: string): void => {
|
UpdateConfigurationFile: (filename: string, content: string): void => {
|
||||||
// test
|
// test
|
||||||
},
|
},
|
||||||
GetConfigurationFile: async (filename: string): Promise<string> => ""
|
GetConfigurationFile: async (filename: string): Promise<string> => "",
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
codeModelSchema
|
codeModelSchema,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,30 +54,27 @@ export function response(
|
||||||
code: number | "default",
|
code: number | "default",
|
||||||
contentType: string,
|
contentType: string,
|
||||||
schema: any,
|
schema: any,
|
||||||
description: string = "The response.",
|
description = "The response.",
|
||||||
extraProperties?: any
|
extraProperties?: any,
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
[code]: {
|
[code]: {
|
||||||
description,
|
description,
|
||||||
content: {
|
content: {
|
||||||
[contentType]: {
|
[contentType]: {
|
||||||
schema
|
schema,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
...extraProperties
|
...extraProperties,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function responses(...responses: any[]) {
|
export function responses(...responses: Array<any>) {
|
||||||
return responses.reduce(
|
return responses.reduce((responsesDict, response) => Object.assign(responsesDict, response), {});
|
||||||
(responsesDict, response) => Object.assign(responsesDict, response),
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function properties(...properties: any[]) {
|
export function properties(...properties: Array<any>) {
|
||||||
// TODO: Accept string or property object
|
// TODO: Accept string or property object
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,44 +85,36 @@ export const InitialTestSpec = {
|
||||||
contact: {
|
contact: {
|
||||||
name: "Microsoft Corporation",
|
name: "Microsoft Corporation",
|
||||||
url: "https://microsoft.com",
|
url: "https://microsoft.com",
|
||||||
email: "devnull@microsoft.com"
|
email: "devnull@microsoft.com",
|
||||||
},
|
},
|
||||||
license: "MIT",
|
license: "MIT",
|
||||||
version: "1.0"
|
version: "1.0",
|
||||||
},
|
},
|
||||||
paths: {},
|
paths: {},
|
||||||
components: {
|
components: {
|
||||||
schemas: {}
|
schemas: {},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TestSpecCustomizer = (spec: any) => any;
|
export type TestSpecCustomizer = (spec: any) => any;
|
||||||
|
|
||||||
export function createTestSpec(...customizers: TestSpecCustomizer[]): any {
|
export function createTestSpec(...customizers: Array<TestSpecCustomizer>): any {
|
||||||
return customizers.reduce<any>(
|
return customizers.reduce<any>((spec: any, customizer: TestSpecCustomizer) => {
|
||||||
(spec: any, customizer: TestSpecCustomizer) => {
|
return customizer(spec);
|
||||||
return customizer(spec);
|
}, clone(InitialTestSpec));
|
||||||
},
|
|
||||||
clone(InitialTestSpec)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addOperation(
|
export function addOperation(
|
||||||
spec: any,
|
spec: any,
|
||||||
path: string,
|
path: string,
|
||||||
operationDict: any,
|
operationDict: any,
|
||||||
metadata: any = { apiVersions: ["1.0.0"] }
|
metadata: any = { apiVersions: ["1.0.0"] },
|
||||||
): void {
|
): void {
|
||||||
operationDict = { ...operationDict, ...{ "x-ms-metadata": metadata } };
|
operationDict = { ...operationDict, ...{ "x-ms-metadata": metadata } };
|
||||||
spec.paths[path] = operationDict;
|
spec.paths[path] = operationDict;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addSchema(
|
export function addSchema(spec: any, name: string, schemaDict: any, metadata: any = { apiVersions: ["1.0.0"] }): void {
|
||||||
spec: any,
|
|
||||||
name: string,
|
|
||||||
schemaDict: any,
|
|
||||||
metadata: any = { apiVersions: ["1.0.0"] }
|
|
||||||
): void {
|
|
||||||
schemaDict = { ...schemaDict, ...{ "x-ms-metadata": metadata } };
|
schemaDict = { ...schemaDict, ...{ "x-ms-metadata": metadata } };
|
||||||
spec.components.schemas[name] = schemaDict;
|
spec.components.schemas[name] = schemaDict;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,17 +3,8 @@
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "dist",
|
"outDir": "dist",
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
"types": [
|
"types": ["mocha"]
|
||||||
"mocha"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["."],
|
||||||
"."
|
"exclude": ["dist", "resources", "node_modules", "**/*.d.ts"]
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"dist",
|
|
||||||
"resources",
|
|
||||||
"node_modules",
|
|
||||||
"**/*.d.ts"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,4 +101,4 @@
|
||||||
"shouldPublish": true
|
"shouldPublish": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,9 @@
|
||||||
"stripInternal": true,
|
"stripInternal": true,
|
||||||
"noEmitHelpers": false,
|
"noEmitHelpers": false,
|
||||||
"target": "es2018",
|
"target": "es2018",
|
||||||
"types": [
|
"types": ["node"],
|
||||||
"node"
|
"lib": ["es2018"],
|
||||||
],
|
|
||||||
"lib": [
|
|
||||||
"es2018"
|
|
||||||
],
|
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"newLine": "LF"
|
"newLine": "LF"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче