This commit is contained in:
Annaji Sharma Ganti 2019-06-04 22:40:17 -07:00 коммит произвёл GitHub
Родитель 1f5532ad6b
Коммит 5c846dcb48
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
101 изменённых файлов: 14071 добавлений и 52 удалений

Просмотреть файл

@ -0,0 +1,14 @@
steps:
- task: NodeTool@0
displayName: 'Use Node 8.x'
inputs:
versionSpec: 8.x
- task: Npm@1
displayName: 'npm install'
- task: Npm@1
displayName: 'Build'
inputs:
command: custom
customCommand: run build

Просмотреть файл

@ -0,0 +1,10 @@
steps:
- task: Npm@1
displayName: 'Lint'
inputs:
command: custom
customCommand: run lint
- task: ComponentGovernanceComponentDetection@0
displayName: 'Component Detection'
condition: ne(variables['System.PullRequest.IsFork'], 'True')

Просмотреть файл

@ -0,0 +1,19 @@
steps:
- task: Npm@1
displayName: 'Package'
inputs:
command: custom
customCommand: run package
- task: CopyFiles@2
displayName: 'Copy vsix to staging directory'
inputs:
Contents: '**/*.vsix'
TargetFolder: '$(build.artifactstagingdirectory)'
- task: PublishBuildArtifacts@1
displayName: 'Publish artifacts: vsix'
inputs:
PathtoPublish: '$(build.artifactstagingdirectory)'
ArtifactName: vsix
condition: ne(variables['System.PullRequest.IsFork'], 'True')

Просмотреть файл

@ -0,0 +1,31 @@
steps:
- script: |
sudo cp .azure-pipelines/linux/xvfb.init /etc/init.d/xvfb
sudo chmod +x /etc/init.d/xvfb
sudo update-rc.d xvfb defaults
sudo service xvfb start
displayName: 'Start X Virtual Frame Buffer'
condition: eq(variables['Agent.OS'], 'Linux')
- task: UsePythonVersion@0
displayName: 'Use Python 3.6.x'
inputs:
versionSpec: 3.6.x
- task: Npm@1
displayName: 'Test'
inputs:
command: custom
customCommand: test
env:
SERVICE_PRINCIPAL_CLIENT_ID: $(SERVICE_PRINCIPAL_CLIENT_ID)
SERVICE_PRINCIPAL_SECRET: $(SERVICE_PRINCIPAL_SECRET)
SERVICE_PRINCIPAL_DOMAIN: $(SERVICE_PRINCIPAL_DOMAIN)
DISPLAY: :10 # Only necessary for linux tests
- task: PublishTestResults@2
displayName: 'Publish Test Results'
inputs:
testResultsFiles: '*-results.xml'
testRunTitle: '$(Agent.OS)'
condition: succeededOrFailed()

Просмотреть файл

@ -0,0 +1,56 @@
#!/bin/bash
#
# COPIED FROM https://github.com/Microsoft/vscode/blob/e29c517386fe6f3a40e2f0ff00effae4919406aa/build/tfs/linux/x64/xvfb.init
#
#
# /etc/rc.d/init.d/xvfbd
#
# chkconfig: 345 95 28
# description: Starts/Stops X Virtual Framebuffer server
# processname: Xvfb
#
### BEGIN INIT INFO
# Provides: xvfb
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start xvfb at boot time
# Description: Enable xvfb provided by daemon.
### END INIT INFO
[ "${NETWORKING}" = "no" ] && exit 0
PROG="/usr/bin/Xvfb"
PROG_OPTIONS=":10 -ac"
PROG_OUTPUT="/tmp/Xvfb.out"
case "$1" in
start)
echo "Starting : X Virtual Frame Buffer "
$PROG $PROG_OPTIONS>>$PROG_OUTPUT 2>&1 &
disown -ar
;;
stop)
echo "Shutting down : X Virtual Frame Buffer"
killproc $PROG
RETVAL=$?
[ $RETVAL -eq 0 ] && /bin/rm -f /var/lock/subsys/Xvfb
/var/run/Xvfb.pid
echo
;;
restart|reload)
$0 stop
$0 start
RETVAL=$?
;;
status)
status Xvfb
RETVAL=$?
;;
*)
echo $"Usage: $0 (start|stop|restart|reload|status)"
exit 1
esac
exit $RETVAL

26
.azure-pipelines/main.yml Normal file
Просмотреть файл

@ -0,0 +1,26 @@
jobs:
- job: Windows
pool:
vmImage: VS2017-Win2016
steps:
- template: common/build.yml
- template: common/lint.yml
- template: common/test.yml
- template: windows/intellinav.yml
- job: Linux
pool:
vmImage: ubuntu-16.04
steps:
- template: common/build.yml
- template: common/publish-vsix.yml # Only publish vsix from linux build since we use this to release and want to stay consistent
- template: common/lint.yml
- template: common/test.yml
- job: macOS
pool:
vmImage: macOS 10.13
steps:
- template: common/build.yml
- template: common/lint.yml
- template: common/test.yml

Просмотреть файл

@ -0,0 +1,7 @@
steps:
- task: RichCodeNavIndexer@0
inputs:
serviceConnection: 'azuretools-dev' # name of the IntelliNav service connection
languages: 'typescript' # languages in your repo, separated by commas
githubServiceConnection: 'GitHub connection' # if your repo is on GitHub, this is the name of the GitHub service connection (GitHub organization by default)
continueOnError: true

3
.gitattributes поставляемый Normal file
Просмотреть файл

@ -0,0 +1,3 @@
# Set default behavior to automatically normalize line endings.
* text=auto

325
.gitignore поставляемый
Просмотреть файл

@ -1,61 +1,302 @@
# Logs
logs
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Chutzpah Test files
_Chutzpah*
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Coverage directory used by tools like istanbul
coverage
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# nyc test coverage
.nyc_output
# TFS 2012 Local Workspace
$tf/
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Guidance Automation Toolkit
*.gpState
# Bower dependency directory (https://bower.io/)
bower_components
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# node-waf configuration
.lock-wscript
# JustCode is a .NET coding add-in
.JustCode
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# TeamCity is a build add-in
_TeamCity*
# Dependency directories
node_modules/
jspm_packages/
# DotCover is a Code Coverage Tool
*.dotCover
# TypeScript v1 declaration files
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# 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
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Visual Studio 6 build log
*.plg
# Optional eslint cache
.eslintcache
# Visual Studio 6 workspace options file
*.opt
# Optional REPL history
.node_repl_history
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Output of 'npm pack'
*.tgz
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Yarn Integrity file
.yarn-integrity
# Paket dependency manager
.paket/paket.exe
paket-files/
# dotenv environment variables file
.env
# FAKE - F# Make
.fake/
# next.js build output
.next
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
\.DS_Store
# VS Code Extension Development
out
node_modules
*.vsix
dist
*.zip
# Artifacts from running vscode extension tests
.vscode-test
test-results.xml
src/**/*.js

8
.vscode/extensions.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,8 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"ms-vscode.vscode-typescript-tslint-plugin",
"ms-vscode.azure-account",
]
}

97
.vscode/launch.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,97 @@
// A launch configuration that compiles the extension and then opens it inside a new window
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"stopOnEntry": false,
"sourceMaps": true,
// outFiles is used for locating generated JavaScript files, see https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_source-maps
"outFiles": [
"${workspaceFolder}/**/*.js"
],
"preLaunchTask": "npm: webpack",
"env": {
"DEBUGTELEMETRY": "1",
"NODE_DEBUG": ""
}
},
{
"name": "Launch Extension (no build)",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"stopOnEntry": false,
"sourceMaps": true,
// outFiles is used for locating generated JavaScript files, see https://code.visualstudio.com/docs/nodejs/nodejs-debugging#_source-maps
"outFiles": [
"${workspaceFolder}/**/*.js"
],
"env": {
"DEBUGTELEMETRY": "1",
"NODE_DEBUG": ""
}
},
{
"name": "Launch Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/out/test"
],
"stopOnEntry": false,
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/out/**/*.js"
],
"preLaunchTask": "npm: compile",
"env": {
"AZCODE_APIM_IGNORE_BUNDLE": "1",
"MOCHA_grep": "", // RegExp of tests to run (empty for all)
"MOCHA_enableTimeouts": "0", // Disable time-outs
"DEBUGTELEMETRY": "1",
"NODE_DEBUG": "",
"ENABLE_LONG_RUNNING_TESTS": "",
"FUNC_PATH": "func"
}
},
{
"name": "Launch Tests (webpack)",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/dist/test"
],
"stopOnEntry": false,
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
],
"preLaunchTask": "npm: webpack",
"env": {
"MOCHA_grep": "", // RegExp of tests to run (empty for all)
"MOCHA_enableTimeouts": "0", // Disable time-outs
"DEBUGTELEMETRY": "1",
"NODE_DEBUG": "",
"ENABLE_LONG_RUNNING_TESTS": "",
"FUNC_PATH": "func"
}
}
]
}

15
.vscode/settings.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,15 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.exclude": {
"out": false // set this to true to hide the "out" folder with the compiled JS files
},
"search.exclude": {
"out": true // set this to false to include "out" folder in search results
},
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
"typescript.tsc.autoDetect": "off",
"typescript.preferences.importModuleSpecifier": "relative",
"typescript.tsdk": "node_modules/typescript/lib"
}

20
.vscode/tasks.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,20 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "watch",
"problemMatcher": "$tsc-watch",
"isBackground": true,
"presentation": {
"reveal": "never"
},
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

29
.vscodeignore Normal file
Просмотреть файл

@ -0,0 +1,29 @@
# These files are excluded during vsce package
.vscode/**
.vscode-test/**
out
src/**
.gitignore
tsconfig.json
vsc-extension-quickstart.md
.github/**
test-results.xml
.azure-pipelines/**
*.cmd
**/*.map
out/test/**
README.dev.md
test/**
tsd.json
typings/**
build
out/**
tslint.json
node_modules/**
**/*.map
dist/test/**
gulp*
webpack.config*
stats.json
*.vsix
*.zip

7
CHANGELOG.md Normal file
Просмотреть файл

@ -0,0 +1,7 @@
# Change Log
All notable changes to the "Azure Api Managment VS Code" extension will be documented in this file.
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
## [Unreleased]
- Initial release

Просмотреть файл

@ -1,14 +1,43 @@
# Azure API Management Extension for Visual Studio Code (Preview)
# Contributing
Expose, publish, and manage microservices architectures as APIs. Quickly create consistent and modern API gateways for existing back-end services hosted anywhere.
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.microsoft.com.
## Installation
1. Download and install the [Azure API Management extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-apimanagement) for Visual Studio Code
2. Wait for the extension to finish installing then reload Visual Studio Code when prompted
3. Once complete, you'll see an Azure icon in the Activity Bar
> If your activity bar is hidden, you won't be able to access the extension. Show the Activity Bar by clicking View > Appearance > Show Activity Bar
4. Sign in to your Azure Account by clicking Sign in to Azure…
> If you don't already have an Azure Account, click "Create a Free Azure Account".
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
## Create your first API
Coming soon...
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## Contributing
There are a couple of ways you can contribute to this repo:
* **Ideas, feature requests and bugs**: We are open to all ideas and we want to get rid of bugs! Use the Issues section to either report a new issue, provide your ideas or contribute to existing threads.
* **Documentation**: Found a typo or strangely worded sentences? Submit a PR!
* **Code**: Contribute bug fixes, features or design changes:
* Clone the repository locally and open in VS Code.
* Install [TSLint for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=ms-vscode.vscode-typescript-tslint-plugin).
* Open the terminal (press `` CTRL+` ``) and run `npm install`.
* To build, press `F1` and type in `Tasks: Run Build Task`.
* Debug: press `F5` to start debugging the extension.
### Legal
Before we can accept your pull request you will need to sign a **Contribution License Agreement**. All you need to do is to submit a pull request, then the PR will get appropriately labelled (e.g. `cla-required`, `cla-norequired`, `cla-signed`, `cla-already-signed`). If you already signed the agreement we will continue with reviewing the PR, otherwise system will tell you how you can sign the CLA. Once you sign the CLA all future PR's will be labeled as `cla-signed`.
### Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
## Telemetry
VS Code collects usage data and sends it to Microsoft to help improve our products and services. Read our [privacy statement](https://go.microsoft.com/fwlink/?LinkID=528096&clcid=0x409) to learn more. If you dont wish to send usage data to Microsoft, you can set the `telemetry.enableTelemetry` setting to `false`. Learn more in our [FAQ](https://code.visualstudio.com/docs/supporting/faq#_how-to-disable-telemetry-reporting).
## License
[MIT](LICENSE.md)

20
extension.bundle.ts Normal file
Просмотреть файл

@ -0,0 +1,20 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/**
* This is the external face of extension.bundle.js, the main webpack bundle for the extension.
* Anything needing to be exposed outside of the extension sources must be exported from here, because
* everything else will be in private modules in extension.bundle.js.
*/
// Export activate/deactivate for main.js
export { activateInternal, deactivateInternal } from './src/extension';
// Exports for tests
// The tests are not packaged with the webpack bundle and therefore only have access to code exported from this file.
//
// The tests should import '../extension.bundle.ts'. At design-time they live in tests/ and so will pick up this file (extension.bundle.ts).
// At runtime the tests live in dist/tests and will therefore pick up the main webpack bundle at dist/extension.bundle.js.
export { ext } from './src/extensionVariables';

23
gulpfile.ts Normal file
Просмотреть файл

@ -0,0 +1,23 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// tslint:disable:no-unsafe-any
import * as cp from 'child_process';
import * as gulp from 'gulp';
import * as path from 'path';
import { gulp_installAzureAccount, gulp_webpack } from 'vscode-azureextensiondev';
const env = process.env;
function test(): cp.ChildProcess {
env.DEBUGTELEMETRY = '1';
env.CODE_TESTS_PATH = path.join(__dirname, 'dist/test');
return cp.spawn('node', ['./node_modules/vscode/bin/test'], { stdio: 'inherit', env });
}
exports['webpack-dev'] = () => gulp_webpack('development');
exports['webpack-prod'] = () => gulp_webpack('production');
exports.test = gulp.series(gulp_installAzureAccount, test);

34
main.js Normal file
Просмотреть файл

@ -0,0 +1,34 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
"use strict";
// This is the extension entrypoint, which imports extension.js, the actual extension code.
//
// This is in a separate file so we can properly measure extension.js load time.
let perfStats = {
loadStartTime: Date.now(),
loadEndTime: undefined
};
Object.defineProperty(exports, "__esModule", { value: true });
const ignoreBundle = !/^(false|0)?$/i.test(process.env.AZCODE_APIM_IGNORE_BUNDLE || '');
const extensionPath = ignoreBundle ? "./out/src/extension" : "./dist/extension.bundle";
const extension = require(extensionPath);
async function activate(ctx) {
return await extension.activateInternal(ctx, perfStats);
}
async function deactivate(ctx) {
return await extension.deactivateInternal();
}
exports.activate = activate;
exports.deactivate = deactivate;
perfStats.loadEndTime = Date.now();

9272
package-lock.json сгенерированный Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

375
package.json Normal file
Просмотреть файл

@ -0,0 +1,375 @@
{
"name": "vscode-apimanagement",
"displayName": "Azure API Managment",
"description": "An Azure API Management extension for Visual Studio Code.",
"version": "0.1.0-alpha",
"publisher": "ms-azuretools",
"icon": "resources/azure-apim.png",
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
"engines": {
"vscode": "^1.31.0"
},
"categories": [
"Azure"
],
"repository": {
"type": "git",
"url": "https://github.com/Microsoft/vscode-apimanagement"
},
"galleryBanner": {
"color": "#0072c6",
"theme": "dark"
},
"keywords": [
"Azure",
"API Management",
"APIM",
"OpenAPI",
"swagger"
],
"preview": true,
"activationEvents": [
"onCommand:azureApiManagement.Refresh",
"onCommand:azureApiManagement.selectSubscriptions",
"onView:azureApiManagementExplorer",
"onLanguage:policy",
"onCommand:azureApiManagement.createService",
"onCommand:azureApiManagement.deleteService",
"onCommand:azureApiManagement.copySubscriptionKey",
"onCommand:azureApiManagement.deleteApi",
"onCommand:azureApiManagement.deleteOperation",
"onCommand:azureApiManagement.importOpenApiByFile",
"onCommand:azureApiManagement.importOpenApiByLink",
"onCommand:azureApiManagement.testOperation",
"onCommand:azureApiManagement.openInPortal",
"onCommand:azureApiManagement.showApi",
"onCommand:azureApiManagement.showArmApi",
"onCommand:azureApiManagement.showArmApiOperation",
"onCommand:azureApiManagement.showServicePolicy",
"onCommand:azureApiManagement.showApiPolicy",
"onCommand:azureApiManagement.showOperationPolicy"
],
"main": "main",
"contributes": {
"jsonValidation": [
{
"fileMatch": "/*-api-arm.json",
"url": "./resources/schemas/ApiCreateOrUpdateParameter.json"
},
{
"fileMatch": "/*-operation-arm.json",
"url": "./resources/schemas/OperationUpdateContract.json"
}
],
"snippets": [
{
"language": "aspnetcorerazor",
"path": "./snippets.json"
}
],
"commands": [
{
"command": "azureApiManagement.createService",
"title": "%azureApiManagement.createService%",
"category": "Azure API Management"
},
{
"command": "azureApiManagement.deleteService",
"title": "%azureApiManagement.deleteService%",
"category": "Azure API Management"
},
{
"command": "azureApiManagement.copySubscriptionKey",
"title": "%azureApiManagement.copySubscriptionKey%",
"category": "Azure API Management"
},
{
"command": "azureApiManagement.deleteApi",
"title": "%azureApiManagement.deleteApi%",
"category": "Azure API Management"
},
{
"command": "azureApiManagement.deleteOperation",
"title": "%azureApiManagement.deleteOperation%",
"category": "Azure API Management"
},
{
"command": "azureApiManagement.importOpenApiByFile",
"title": "%azureApiManagement.importOpenApiByFile%",
"category": "Azure API Management"
},
{
"command": "azureApiManagement.importOpenApiByLink",
"title": "%azureApiManagement.importOpenApiByLink%",
"category": "Azure API Management"
},
{
"command": "azureApiManagement.testOperation",
"title": "%azureApiManagement.testOperation%",
"category": "Azure API Management"
},
{
"command": "azureApiManagement.openInPortal",
"title": "%azureApiManagement.openInPortal%",
"category": "Azure API Management"
},
{
"command": "azureApiManagement.selectSubscriptions",
"title": "Select Subscription...",
"icon": {
"light": "resources/light/filter.svg",
"dark": "resources/dark/filter.svg"
}
},
{
"command": "azureApiManagement.Refresh",
"title": "Refresh",
"category": "Azure Api Management",
"icon": {
"light": "resources/light/refresh.svg",
"dark": "resources/dark/refresh.svg"
}
},
{
"command": "azureApiManagement.LoadMore",
"title": "Load More",
"category": "Azure Api Management",
"icon": {
"light": "resources/light/refresh.svg",
"dark": "resources/dark/refresh.svg"
}
},
{
"command": "azureApiManagement.showApi",
"title": "%azureApiManagement.showApi%",
"category": "Azure API Management"
},
{
"command": "azureApiManagement.showArmApi",
"title": "%azureApiManagement.showArmApi%",
"category": "Azure API Management"
},
{
"command": "azureApiManagement.showArmApiOperation",
"title": "%azureApiManagement.showArmApiOperation%",
"category": "Azure API Management"
},
{
"command": "azureApiManagement.showServicePolicy",
"title": "%azureApiManagement.showServicePolicy%",
"category": "Azure API Management"
},
{
"command": "azureApiManagement.showApiPolicy",
"title": "%azureApiManagement.showApiPolicy%",
"category": "Azure API Management"
},
{
"command": "azureApiManagement.showOperationPolicy",
"title": "%azureApiManagement.showOperationPolicy%",
"category": "Azure API Management"
}
],
"viewsContainers": {
"activitybar": [
{
"id": "azure",
"title": "Azure",
"icon": "resources/azure.svg"
}
]
},
"views": {
"azure": [
{
"id": "azureApiManagementExplorer",
"name": "Api Management",
"when": "config.azureApiManagement.showExplorer == true"
}
]
},
"menus": {
"editor/title": [],
"commandPalette": [
{
"command": "azureApiManagement.Refresh",
"when": "never"
},
{
"command": "azureApiManagement.LoadMore",
"when": "never"
}
],
"view/title": [
{
"command": "azureApiManagement.Refresh",
"when": "view == azureApiManagementExplorer",
"group": "navigation@1"
}
],
"view/item/context": [
{
"command": "azureApiManagement.selectSubscriptions",
"when": "view == azureApiManagementExplorer && viewItem == azureextensionui.azureSubscription",
"group": "inline"
},
{
"command": "azureApiManagement.Refresh",
"when": "view == azureApiManagementExplorer && viewItem == azureextensionui.azureSubscription",
"group": "3@1"
},
{
"command": "azureApiManagement.createService",
"when": "view == azureApiManagementExplorer && viewItem == azureextensionui.azureSubscription",
"group": "2@1"
},
{
"command": "azureApiManagement.openInPortal",
"when": "view == azureApiManagementExplorer && viewItem == azureextensionui.azureSubscription",
"group": "1@1"
},
{
"command": "azureApiManagement.openInPortal",
"when": "view == azureApiManagementExplorer && viewItem == azureApiManagementService",
"group": "1@1"
},
{
"command": "azureApiManagement.copySubscriptionKey",
"when": "view == azureApiManagementExplorer && viewItem == azureApiManagementService",
"group": "2@1"
},
{
"command": "azureApiManagement.deleteService",
"when": "view == azureApiManagementExplorer && viewItem == azureApiManagementService",
"group": "2@2"
},
{
"command": "azureApiManagement.Refresh",
"when": "view == azureApiManagementExplorer && viewItem == azureApiManagementService",
"group": "3@1"
},
{
"command": "azureApiManagement.importOpenApiByFile",
"when": "view == azureApiManagementExplorer && viewItem == azureApiManagementApis",
"group": "1@1"
},
{
"command": "azureApiManagement.importOpenApiByLink",
"when": "view == azureApiManagementExplorer && viewItem == azureApiManagementApis",
"group": "1@2"
},
{
"command": "azureApiManagement.Refresh",
"when": "view == azureApiManagementExplorer && viewItem == azureApiManagementApis",
"group": "2@1"
},
{
"command": "azureApiManagement.showApi",
"when": "view == azureApiManagementExplorer && viewItem == azureApiManagementApi",
"group": "1@1"
},
{
"command": "azureApiManagement.deleteApi",
"when": "view == azureApiManagementExplorer && viewItem == azureApiManagementApi",
"group": "1@2"
},
{
"command": "azureApiManagement.Refresh",
"when": "view == azureApiManagementExplorer && viewItem == azureApiManagementApi",
"group": "2@1"
},
{
"command": "azureApiManagement.Refresh",
"when": "view == azureApiManagementExplorer && viewItem == azureApiManagementOperations",
"group": "1@1"
},
{
"command": "azureApiManagement.deleteOperation",
"when": "view == azureApiManagementExplorer && viewItem == azureApiManagementApiOperation",
"group": "1@2"
},
{
"command": "azureApiManagement.testOperation",
"when": "view == azureApiManagementExplorer && viewItem == azureApiManagementApiOperation",
"group": "1@1"
}
]
},
"configuration": [
{
"title": "Azure API Management",
"properties": {
"azureApiManagement.showExplorer": {
"type": "boolean",
"default": true,
"description": "%azureApiManagement.showExplorer%"
},
"azureApiManagement.showSavePrompt": {
"type": "boolean",
"default": true,
"description": "%azureApiManagement.showSavePrompt%"
},
"azureApiManagement.advancedCreation": {
"type": "boolean",
"default": false,
"description": "%azureApiManagement.advancedCreationDescription%"
}
}
}
]
},
"scripts": {
"vscode:prepublish": "npm run webpack-prod",
"build": "tsc -p ./",
"compile": "tsc -watch -p ./",
"package": "vsce package",
"lint": "tslint --project tsconfig.json -e src/*.d.ts -t verbose",
"lint-fix": "tslint --project tsconfig.json -t verbose --fix",
"postinstall": "node ./node_modules/vscode/bin/install",
"pretest": "npm run build && npm run webpack",
"test": "gulp test",
"watch": "tsc -watch -p ./",
"all": "npm i && npm run lint && npm test",
"webpack": "npm run build && gulp webpack-dev",
"webpack-prod": "npm run build && gulp webpack-prod",
"webpack-profile": "webpack --profile --json --mode production > webpack-stats.json && echo Use http://webpack.github.io/analyse to analyze the stats"
},
"devDependencies": {
"@types/fs-extra": "^4.0.3",
"@types/gulp": "^4.0.6",
"@types/node": "^8.10.25",
"@types/request": "^2.47.0",
"@types/request-promise": "^4.1.43",
"@types/swagger-parser": "^4.0.3",
"@types/mocha": "^5.2.6",
"tslint": "^5.7.0",
"typescript": "^3.3.1",
"ts-node": "^7.0.1",
"tslint-microsoft-contrib": "5.0.1",
"vscode-extension-telemetry": "^0.0.18",
"gulp": "^4.0.0",
"vsce": "^1.59.0",
"vscode": "^1.1.33",
"vscode-azureextensiondev": "0.1.8",
"webpack": "4.29.6",
"webpack-cli": "^3.3.0",
"mocha": "^5.2.0",
"mocha-junit-reporter": "^1.18.0",
"mocha-multi-reporters": "^1.1.7"
},
"dependencies": {
"vscode-azureextensionui": "^0.23.3",
"swagger-parser": "^6.0.5",
"vscode-nls": "^4.1.0",
"azure-arm-apimanagement": "^5.1.0",
"azure-arm-resource": "^3.0.0-preview",
"fs-extra": "^4.0.2",
"opn": "^5.3.0",
"request": "^2.83.0",
"request-promise": "^4.2.2"
},
"extensionDependencies": [
"ms-vscode.azure-account",
"humao.rest-client"
]
}

20
package.nls.json Normal file
Просмотреть файл

@ -0,0 +1,20 @@
{
"azureApiManagement.openInPortal": "Open in Portal",
"azureApiManagement.importOpenApiByFile": "Import from OpenAPI file",
"azureApiManagement.importOpenApiByLink": "Import from OpenAPI link",
"azureApiManagement.showExplorer": "Show or hide the Azure API Management Explorer.",
"azureApiManagement.showSavePrompt": "Show warning dialog on remote file uploading.",
"azureApiManagement.createService": "Create API Management in Azure",
"azureApiManagement.deleteService": "Delete API Management",
"azureApiManagement.copySubscriptionKey" : "Copy Subscription Key",
"azureApiManagement.showApi": "Edit OpenAPI",
"azureApiManagement.showArmApi": "Edit API",
"azureApiManagement.showArmApiOperation": "Edit Operation",
"azureApiManagement.showServicePolicy": "Edit Global policy",
"azureApiManagement.showApiPolicy": "Edit API policy",
"azureApiManagement.showOperationPolicy": "Edit Operation policy",
"azureApiManagement.deleteApi": "Delete API",
"azureApiManagement.deleteOperation": "Delete Operation",
"azureApiManagement.testOperation": "Test Operation",
"azureApiManagement.advancedCreationDescription":"Enables advanced creation of Azure API Management Instance, which will prompt for several additional values instead of using a default."
}

Двоичные данные
resources/azure-apim.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 18 KiB

6
resources/azure.svg Normal file
Просмотреть файл

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg height="28" width="28" version="1.1" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">
<g>
<path fill="#0072C6" d="M11.423,44.326l23.623-4.156L22.894,25.748l6.328-17.346L50,44.33L11.423,44.326z M27.566,5.67L11.469,40.109v-0.034H0l12.717-21.975L27.566,5.67z" />
</g>
</svg>

После

Ширина:  |  Высота:  |  Размер: 331 B

7
resources/dark/api.svg Normal file
Просмотреть файл

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 50 50">
<rect y="20.84" width="19.55" height="8.32" fill="#7fba00"/>
<rect x="41.68" y="20.84" width="10.26" height="8.32" fill="#59b4d9"/>
<circle cx="20.12" cy="25" r="9.46" fill="#7fba00"/>
<path d="M20.12 0.48v10.96a1 1 0 0 1 0 27.14v10.96a1 1 0 0 0 0-49.05Z" fill="#59b4d9"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 430 B

14
resources/dark/apim.svg Normal file
Просмотреть файл

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
<path fill="#59B4D9" d="M20.5,32.5h-8.9l0,0c-4.4,0-8-3.6-8-8.1s3.4-8.1,8-8.1c0.8,0,1.7,0.2,2.7,0.3l1.7,0.5l0.5-1.7
C18.1,10.2,23.1,7,28.8,7c6.9,0,12.2,5.3,12.2,12.2c0,1.1-0.2,2.2-0.5,3.3L39.8,25l2.7-0.3c0.2,0,0.3,0,0.5,0c2,0,3.6,1.7,3.6,3.6
s-1.4,3.3-3.1,3.6c0,0-8.6,0-8.9,0c-0.3-2.5-1.6-4.7-3.3-6.4C29.1,23.3,26.1,22,23,22c-2.2,0-4.4,0.6-6.1,1.7l1.9,3
c1.4-0.8,2.8-1.1,4.4-1.1c2.2,0,4.4,0.8,5.8,2.5c1.6,1.6,2.5,3.6,2.5,5.8s-0.8,4.4-2.5,5.8c-1.6,1.7-3.6,2.5-5.8,2.5
c-1.6,0-3.1-0.5-4.4-1.4l-1.9,3c1.9,1.1,4.1,1.9,6.1,1.9c3.1,0,6.1-1.1,8.3-3.4c1.9-1.9,3-4.4,3.3-7c0.5,0,9.1,0,9.1,0h0.2
c3.6-0.3,6.3-3.4,6.3-7.2c0-3-2.8-5.8-6.1-6.4c0.2-0.6,0.2-0.9,0.2-1.9c0-8.8-6.2-15.8-16.1-15.8l0,0c-6.3,0-11.9,3.6-14.2,9.4
c-0.8,0-1.4-0.3-2.3-0.3C5.2,13,0,18,0,24.4c0,6.3,5.2,11.3,11.4,11.3h9.8L20.5,32.5z"/>
<circle fill="#68217A" cx="22.8" cy="33.8" r="5.6"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 1.3 KiB

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-bg{fill:#c5c5c5}.icon-vs-fg{fill:#2b282e}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M0 0v3.043l5 6V16h6V9.043l5-6V0H0z" id="outline" style="display: none;"/><path class="icon-vs-fg" d="M7 14h2V8.319l5-6V2H2v.319l5 6V14z" id="iconFg" style="display: none;"/><path class="icon-vs-bg" d="M10 15H6V8.681l-5-6V1h14v1.681l-5 6V15zm-3-1h2V8.319l5-6V2H2v.319l5 6V14z" id="iconBg"/></svg>

После

Ширина:  |  Высота:  |  Размер: 596 B

8
resources/dark/list.svg Normal file
Просмотреть файл

@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="#e0e0e0" viewBox="0 0 24 24" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svg="http://www.w3.org/2000/svg">
<rect x="8" y="4" width="13" height="2"/>
<rect x="8" y="11" width="13" height="2"/>
<rect x="8" y="18" width="13" height="2"/>
<rect x="2" y="3" width="4" height="4"/>
<rect x="2" y="10" width="4" height="4"/>
<rect x="2" y="17" width="4" height="4"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 424 B

1
resources/dark/op.svg Normal file
Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#2d2d30}.icon-canvas-transparent{opacity:0}.icon-vs-bg{fill:#c5c5c5}</style></defs><title>Process_16x</title><path class="icon-canvas-transparent" d="M16 0v16H0V0z"/><path class="icon-vs-out" d="M16 4.586l-1.018.6L16 5.743v.72l-.031.122a5.462 5.462 0 0 1-.5 1.264l-.376.681-1.306-.34.373 1.283-.657.4a4.885 4.885 0 0 1-.6.333 4.055 4.055 0 0 1-.549.183l-.851.244-.681-1.161-.65 1.185-.2-.051A3.58 3.58 0 0 1 10 11a3.852 3.852 0 0 1-.046.535l-.1.77-.782.191-.354.044.7.9-.476.614a5.038 5.038 0 0 1-.89.891l-.614.477-.9-.7-.138 1.12-.77.1A4.623 4.623 0 0 1 5 16a3.882 3.882 0 0 1-.536-.046l-.769-.1-.195-.782-.044-.353-.9.7-.614-.477a5.038 5.038 0 0 1-.89-.891l-.475-.614.7-.9L.158 12.4l-.1-.769A4.553 4.553 0 0 1 0 11a3.853 3.853 0 0 1 .046-.535l.1-.769.781-.196.354-.044-.7-.9.475-.614a5.022 5.022 0 0 1 .89-.891l.614-.477.9.7.14-1.116.77-.1A4.548 4.548 0 0 1 5 6a3.153 3.153 0 0 1 .387.033l-.014-.049L6.532 5.3l-1.181-.643.194-.757a5.422 5.422 0 0 1 .5-1.262l.375-.683 1.307.34-.376-1.287.764-.452a4.069 4.069 0 0 1 .506-.271A4.077 4.077 0 0 1 9.159.1l.363-.1h.578l.6 1.021L11.257 0h.719l.124.032a5.426 5.426 0 0 1 1.262.5l.681.376-.34 1.3L15 1.838l.452.769a4.289 4.289 0 0 1 .267.5 4.07 4.07 0 0 1 .185.556l.1.354z"/><path class="icon-vs-bg" d="M13.8 5.675a3.058 3.058 0 0 0-.012-.947l1.181-.693A4.266 4.266 0 0 0 14.8 3.5a4.563 4.563 0 0 0-.271-.487l-1.318.387a3.015 3.015 0 0 0-.68-.658l.347-1.329A4.421 4.421 0 0 0 11.849 1l-.661 1.2a3.075 3.075 0 0 0-.948.012l-.694-1.177a4.36 4.36 0 0 0-.53.168 4.32 4.32 0 0 0-.487.271l.383 1.315a3.044 3.044 0 0 0-.659.681l-1.33-.347a4.436 4.436 0 0 0-.409 1.03l1.2.66a3.039 3.039 0 0 0 .011.947l-1.181.693a4.294 4.294 0 0 0 .168.531 4.244 4.244 0 0 0 .271.487l1.317-.38a3.019 3.019 0 0 0 .681.658l-.346 1.329a4.454 4.454 0 0 0 1.03.41l.661-1.2a3.076 3.076 0 0 0 .948-.012l.693 1.181a4.272 4.272 0 0 0 .53-.168 4.108 4.108 0 0 0 .486-.271L12.6 7.7a3.041 3.041 0 0 0 .659-.681l1.33.347A4.392 4.392 0 0 0 15 6.335zm-2.169 1.59a2.2 2.2 0 1 1 1.149-2.891 2.2 2.2 0 0 1-1.152 2.891zm-3.917 3.077a2.744 2.744 0 0 0-.331-.795l.768-.984a4 4 0 0 0-.712-.713l-.985.768a2.783 2.783 0 0 0-.8-.331L5.5 7.051A3.9 3.9 0 0 0 5 7a3.9 3.9 0 0 0-.5.051l-.157 1.235a2.783 2.783 0 0 0-.8.331l-.981-.767a4 4 0 0 0-.712.713l.768.984a2.744 2.744 0 0 0-.331.795l-1.236.158A3.9 3.9 0 0 0 1 11a3.9 3.9 0 0 0 .051.5l1.235.154a2.744 2.744 0 0 0 .331.795l-.768.984a4 4 0 0 0 .712.713l.985-.768a2.783 2.783 0 0 0 .8.331l.154 1.24A3.886 3.886 0 0 0 5 15a3.886 3.886 0 0 0 .5-.051l.153-1.235a2.783 2.783 0 0 0 .8-.331l.985.768a4 4 0 0 0 .712-.713l-.768-.984a2.744 2.744 0 0 0 .331-.795l1.236-.159A3.9 3.9 0 0 0 9 11a3.9 3.9 0 0 0-.051-.5zM5 13a2 2 0 1 1 2-2 2 2 0 0 1-2 2zm1-2a1 1 0 1 1-1-1 1 1 0 0 1 1 1zm5.767-6.191a1.1 1.1 0 1 1-1.445-.575 1.1 1.1 0 0 1 1.445.575z"/></svg>

После

Ширина:  |  Высота:  |  Размер: 2.8 KiB

14
resources/dark/policy.svg Normal file
Просмотреть файл

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
<line fill="#7FBA00" x1="42.8" y1="39" x2="42.8" y2="43"/>
<path fill="#A0A1A2" d="M44,6H14v4h29v13h-6v4h7c1.65,0,3-1.35,3-3V9C47,7.35,45.65,6,44,6z"/>
<polygon fill="#A0A1A2" points="38,30 32,25 38,20 "/>
<path fill="#7FBA00" d="M7,40V27h10v-4H6c-1.65,0-3,1.35-3,3v15c0,1.65,1.35,3,3,3h27v-4H7z"/>
<polygon fill="#7FBA00" points="31,37 37,42 31,47 "/>
<line fill="#7FBA00" x1="41.8" y1="39" x2="41.8" y2="43"/>
<line fill="#7FBA00" x1="19.9" y1="24" x2="19.9" y2="28"/>
<path fill="#0399C6" d="M19,29.581v-9.162c0-0.8,0.661-1.442,1.461-1.419L29.623,19c0.751,0.022,1.355,0.626,1.377,1.377
l-0.001,9.162c0.024,0.8-0.618,1.461-1.419,1.461h-9.162C19.635,31,19,30.365,19,29.581z"/>
<circle fill="#0399C6" cx="7" cy="8" r="5"/>
<circle fill="#0399C6" cx="43" cy="42" r="5"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 892 B

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M13.451 5.609l-.579-.939-1.068.812-.076.094c-.335.415-.927 1.341-1.124 2.876l-.021.165.033.163.071.345c0 1.654-1.346 3-3 3-.795 0-1.545-.311-2.107-.868-.563-.567-.873-1.317-.873-2.111 0-1.431 1.007-2.632 2.351-2.929v2.926s2.528-2.087 2.984-2.461h.012l3.061-2.582-4.919-4.1h-1.137v2.404c-3.429.318-6.121 3.211-6.121 6.721 0 1.809.707 3.508 1.986 4.782 1.277 1.282 2.976 1.988 4.784 1.988 3.722 0 6.75-3.028 6.75-6.75 0-1.245-.349-2.468-1.007-3.536z" fill="#2D2D30"/><path d="M12.6 6.134l-.094.071c-.269.333-.746 1.096-.91 2.375.057.277.092.495.092.545 0 2.206-1.794 4-4 4-1.098 0-2.093-.445-2.817-1.164-.718-.724-1.163-1.718-1.163-2.815 0-2.206 1.794-4 4-4l.351.025v1.85s1.626-1.342 1.631-1.339l1.869-1.577-3.5-2.917v2.218l-.371-.03c-3.176 0-5.75 2.574-5.75 5.75 0 1.593.648 3.034 1.695 4.076 1.042 1.046 2.482 1.694 4.076 1.694 3.176 0 5.75-2.574 5.75-5.75-.001-1.106-.318-2.135-.859-3.012z" fill="#C5C5C5"/></svg>

После

Ширина:  |  Высота:  |  Размер: 986 B

7
resources/light/api.svg Normal file
Просмотреть файл

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 50 50">
<rect y="20.84" width="19.55" height="8.32" fill="#7fba00"/>
<rect x="41.68" y="20.84" width="10.26" height="8.32" fill="#59b4d9"/>
<circle cx="20.12" cy="25" r="9.46" fill="#7fba00"/>
<path d="M20.12 0.48v10.96a1 1 0 0 1 0 27.14v10.96a1 1 0 0 0 0-49.05Z" fill="#59b4d9"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 430 B

14
resources/light/apim.svg Normal file
Просмотреть файл

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
<path fill="#59B4D9" d="M20.5,32.5h-8.9l0,0c-4.4,0-8-3.6-8-8.1s3.4-8.1,8-8.1c0.8,0,1.7,0.2,2.7,0.3l1.7,0.5l0.5-1.7
C18.1,10.2,23.1,7,28.8,7c6.9,0,12.2,5.3,12.2,12.2c0,1.1-0.2,2.2-0.5,3.3L39.8,25l2.7-0.3c0.2,0,0.3,0,0.5,0c2,0,3.6,1.7,3.6,3.6
s-1.4,3.3-3.1,3.6c0,0-8.6,0-8.9,0c-0.3-2.5-1.6-4.7-3.3-6.4C29.1,23.3,26.1,22,23,22c-2.2,0-4.4,0.6-6.1,1.7l1.9,3
c1.4-0.8,2.8-1.1,4.4-1.1c2.2,0,4.4,0.8,5.8,2.5c1.6,1.6,2.5,3.6,2.5,5.8s-0.8,4.4-2.5,5.8c-1.6,1.7-3.6,2.5-5.8,2.5
c-1.6,0-3.1-0.5-4.4-1.4l-1.9,3c1.9,1.1,4.1,1.9,6.1,1.9c3.1,0,6.1-1.1,8.3-3.4c1.9-1.9,3-4.4,3.3-7c0.5,0,9.1,0,9.1,0h0.2
c3.6-0.3,6.3-3.4,6.3-7.2c0-3-2.8-5.8-6.1-6.4c0.2-0.6,0.2-0.9,0.2-1.9c0-8.8-6.2-15.8-16.1-15.8l0,0c-6.3,0-11.9,3.6-14.2,9.4
c-0.8,0-1.4-0.3-2.3-0.3C5.2,13,0,18,0,24.4c0,6.3,5.2,11.3,11.4,11.3h9.8L20.5,32.5z"/>
<circle fill="#68217A" cx="22.8" cy="33.8" r="5.6"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 1.3 KiB

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}.icon-vs-fg{fill:#f0eff1}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M0 0v3.043l5 6V16h6V9.043l5-6V0H0z" id="outline" style="display: none;"/><path class="icon-vs-fg" d="M7 14h2V8.319l5-6V2H2v.319l5 6V14z" id="iconFg" style="display: none;"/><path class="icon-vs-bg" d="M10 15H6V8.681l-5-6V1h14v1.681l-5 6V15zm-3-1h2V8.319l5-6V2H2v.319l5 6V14z" id="iconBg"/></svg>

После

Ширина:  |  Высота:  |  Размер: 596 B

8
resources/light/list.svg Normal file
Просмотреть файл

@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="#464f59" viewBox="0 0 24 24" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svg="http://www.w3.org/2000/svg">
<rect x="8" y="4" width="13" height="2"/>
<rect x="8" y="11" width="13" height="2"/>
<rect x="8" y="18" width="13" height="2"/>
<rect x="2" y="3" width="4" height="4"/>
<rect x="2" y="10" width="4" height="4"/>
<rect x="2" y="17" width="4" height="4"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 424 B

1
resources/light/op.svg Normal file
Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6}.icon-canvas-transparent{opacity:0}.icon-vs-bg{fill:#424242}</style></defs><title>Process_16x</title><path class="icon-canvas-transparent" d="M16 0v16H0V0z"/><path class="icon-vs-out" d="M16 4.586l-1.018.6L16 5.743v.72l-.031.122a5.462 5.462 0 0 1-.5 1.264l-.376.681-1.306-.34.373 1.283-.657.4a4.885 4.885 0 0 1-.6.333 4.055 4.055 0 0 1-.549.183l-.851.244-.681-1.161-.65 1.185-.2-.051A3.58 3.58 0 0 1 10 11a3.852 3.852 0 0 1-.046.535l-.1.77-.782.191-.354.044.7.9-.476.614a5.038 5.038 0 0 1-.89.891l-.614.477-.9-.7-.138 1.12-.77.1A4.623 4.623 0 0 1 5 16a3.882 3.882 0 0 1-.536-.046l-.769-.1-.195-.782-.044-.353-.9.7-.614-.477a5.038 5.038 0 0 1-.89-.891l-.475-.614.7-.9L.158 12.4l-.1-.769A4.553 4.553 0 0 1 0 11a3.853 3.853 0 0 1 .046-.535l.1-.769.781-.196.354-.044-.7-.9.475-.614a5.022 5.022 0 0 1 .89-.891l.614-.477.9.7.14-1.116.77-.1A4.548 4.548 0 0 1 5 6a3.153 3.153 0 0 1 .387.033l-.014-.049L6.532 5.3l-1.181-.643.194-.757a5.422 5.422 0 0 1 .5-1.262l.375-.683 1.307.34-.376-1.287.764-.452a4.069 4.069 0 0 1 .506-.271A4.077 4.077 0 0 1 9.159.1l.363-.1h.578l.6 1.021L11.257 0h.719l.124.032a5.426 5.426 0 0 1 1.262.5l.681.376-.34 1.3L15 1.838l.452.769a4.289 4.289 0 0 1 .267.5 4.07 4.07 0 0 1 .185.556l.1.354z" style="display: none;"/><path class="icon-vs-bg" d="M13.8 5.675a3.058 3.058 0 0 0-.012-.947l1.181-.693A4.266 4.266 0 0 0 14.8 3.5a4.563 4.563 0 0 0-.271-.487l-1.318.387a3.015 3.015 0 0 0-.68-.658l.347-1.329A4.421 4.421 0 0 0 11.849 1l-.661 1.2a3.075 3.075 0 0 0-.948.012l-.694-1.177a4.36 4.36 0 0 0-.53.168 4.32 4.32 0 0 0-.487.271l.383 1.315a3.044 3.044 0 0 0-.659.681l-1.33-.347a4.436 4.436 0 0 0-.409 1.03l1.2.66a3.039 3.039 0 0 0 .011.947l-1.181.693a4.294 4.294 0 0 0 .168.531 4.244 4.244 0 0 0 .271.487l1.317-.38a3.019 3.019 0 0 0 .681.658l-.346 1.329a4.454 4.454 0 0 0 1.03.41l.661-1.2a3.076 3.076 0 0 0 .948-.012l.693 1.181a4.272 4.272 0 0 0 .53-.168 4.108 4.108 0 0 0 .486-.271L12.6 7.7a3.041 3.041 0 0 0 .659-.681l1.33.347A4.392 4.392 0 0 0 15 6.335zm-2.169 1.59a2.2 2.2 0 1 1 1.149-2.891 2.2 2.2 0 0 1-1.152 2.891zm-3.917 3.077a2.744 2.744 0 0 0-.331-.795l.768-.984a4 4 0 0 0-.712-.713l-.985.768a2.783 2.783 0 0 0-.8-.331L5.5 7.051A3.9 3.9 0 0 0 5 7a3.9 3.9 0 0 0-.5.051l-.157 1.235a2.783 2.783 0 0 0-.8.331l-.981-.767a4 4 0 0 0-.712.713l.768.984a2.744 2.744 0 0 0-.331.795l-1.236.158A3.9 3.9 0 0 0 1 11a3.9 3.9 0 0 0 .051.5l1.235.154a2.744 2.744 0 0 0 .331.795l-.768.984a4 4 0 0 0 .712.713l.985-.768a2.783 2.783 0 0 0 .8.331l.154 1.24A3.886 3.886 0 0 0 5 15a3.886 3.886 0 0 0 .5-.051l.153-1.235a2.783 2.783 0 0 0 .8-.331l.985.768a4 4 0 0 0 .712-.713l-.768-.984a2.744 2.744 0 0 0 .331-.795l1.236-.159A3.9 3.9 0 0 0 9 11a3.9 3.9 0 0 0-.051-.5zM5 13a2 2 0 1 1 2-2 2 2 0 0 1-2 2zm1-2a1 1 0 1 1-1-1 1 1 0 0 1 1 1zm5.767-6.191a1.1 1.1 0 1 1-1.445-.575 1.1 1.1 0 0 1 1.445.575z"/></svg>

После

Ширина:  |  Высота:  |  Размер: 2.9 KiB

Просмотреть файл

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
<line fill="#7FBA00" x1="42.8" y1="39" x2="42.8" y2="43"/>
<path fill="#A0A1A2" d="M44,6H14v4h29v13h-6v4h7c1.65,0,3-1.35,3-3V9C47,7.35,45.65,6,44,6z"/>
<polygon fill="#A0A1A2" points="38,30 32,25 38,20 "/>
<path fill="#7FBA00" d="M7,40V27h10v-4H6c-1.65,0-3,1.35-3,3v15c0,1.65,1.35,3,3,3h27v-4H7z"/>
<polygon fill="#7FBA00" points="31,37 37,42 31,47 "/>
<line fill="#7FBA00" x1="41.8" y1="39" x2="41.8" y2="43"/>
<line fill="#7FBA00" x1="19.9" y1="24" x2="19.9" y2="28"/>
<path fill="#0399C6" d="M19,29.581v-9.162c0-0.8,0.661-1.442,1.461-1.419L29.623,19c0.751,0.022,1.355,0.626,1.377,1.377
l-0.001,9.162c0.024,0.8-0.618,1.461-1.419,1.461h-9.162C19.635,31,19,30.365,19,29.581z"/>
<circle fill="#0399C6" cx="7" cy="8" r="5"/>
<circle fill="#0399C6" cx="43" cy="42" r="5"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 892 B

Просмотреть файл

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M13.451 5.609l-.579-.939-1.068.812-.076.094c-.335.415-.927 1.341-1.124 2.876l-.021.165.033.163.071.345c0 1.654-1.346 3-3 3-.795 0-1.545-.311-2.107-.868-.563-.567-.873-1.317-.873-2.111 0-1.431 1.007-2.632 2.351-2.929v2.926s2.528-2.087 2.984-2.461h.012l3.061-2.582-4.919-4.1h-1.137v2.404c-3.429.318-6.121 3.211-6.121 6.721 0 1.809.707 3.508 1.986 4.782 1.277 1.282 2.976 1.988 4.784 1.988 3.722 0 6.75-3.028 6.75-6.75 0-1.245-.349-2.468-1.007-3.536z" fill="#F6F6F6"/><path d="M12.6 6.134l-.094.071c-.269.333-.746 1.096-.91 2.375.057.277.092.495.092.545 0 2.206-1.794 4-4 4-1.098 0-2.093-.445-2.817-1.164-.718-.724-1.163-1.718-1.163-2.815 0-2.206 1.794-4 4-4l.351.025v1.85s1.626-1.342 1.631-1.339l1.869-1.577-3.5-2.917v2.218l-.371-.03c-3.176 0-5.75 2.574-5.75 5.75 0 1.593.648 3.034 1.695 4.076 1.042 1.046 2.482 1.694 4.076 1.694 3.176 0 5.75-2.574 5.75-5.75-.001-1.106-.318-2.135-.859-3.012z" fill="#424242"/></svg>

После

Ширина:  |  Высота:  |  Размер: 986 B

Просмотреть файл

@ -0,0 +1,220 @@
{
"$schema": "http://json-schema.org/draft-06/schema#",
"definitions": {
"ApiCreateOrUpdateParameter": {
"type": "object",
"properties": {
"description": {
"type": "string",
"description": "Description of the API. May include HTML formatting tags."
},
"authenticationSettings": {
"$ref": "#/definitions/AuthenticationSettingsContract",
"description": "Collection of authentication settings included into this API."
},
"subscriptionKeyParameterNames": {
"$ref": "#/definitions/SubscriptionKeyParameterNamesContract",
"description": "Protocols over which API is made available."
},
"apiType": {
"type": "string",
"description": "Type of API. Possible values include: 'http', 'soap'"
},
"apiRevision": {
"type": "string",
"description": "Describes the Revision of the Api. If no value is provided, default revision 1 is created"
},
"apiVersion": {
"type": "string",
"description": "Indicates the Version identifier of the API if the API is versioned"
},
"isCurrent": {
"type": "boolean",
"description": "Indicates if API revision is current api revision."
},
"isOnline": {
"type": "boolean",
"description": "Indicates if API revision is accessible via the gateway."
},
"apiRevisionDescription": {
"type": "string",
"description": "Description of the Api Revision."
},
"apiVersionDescription": {
"type": "string",
"description": "Description of the Api Version."
},
"apiVersionSetId": {
"type": "string",
"description": "A resource identifier for the related ApiVersionSet."
},
"subscriptionRequired": {
"type": "boolean",
"description": "Specifies whether an API or Product subscription is required for accessing the API."
},
"sourceApiId": {
"type": "string",
"description": "API identifier of the source API."
},
"displayName": {
"type": "string",
"description": "API name. Must be 1 to 300 characters long."
},
"serviceUrl": {
"type": "string",
"description": "Absolute URL of the backend service implementing this API. Cannot be more than 2000 characters\nlong."
},
"path": {
"type": "string",
"description": "Relative URL uniquely identifying this API and all of its resource paths within the API\nManagement service instance. It is appended to the API endpoint base URL specified during the\nservice instance creation to form a public URL for this API."
},
"protocols": {
"type": "array",
"items": {
"type": "string"
},
"description": "Describes on which protocols the operations in this API can be invoked."
},
"apiVersionSet": {
"$ref": "#/definitions/ApiVersionSetContractDetails",
"description": "Version set details"
},
"value": {
"type": "string",
"description": "Content value when Importing an API."
},
"format": {
"type": "string",
"description": "Format of the Content in which the API is getting imported. Possible values include:\n'wadl-xml', 'wadl-link-json', 'swagger-json', 'swagger-link-json', 'wsdl', 'wsdl-link',\n'openapi', 'openapi+json', 'openapi-link'"
},
"wsdlSelector": {
"$ref": "#/definitions/ApiCreateOrUpdatePropertiesWsdlSelector",
"description": "Criteria to limit import of WSDL to a subset of the document."
},
"soapApiType": {
"type": "string",
"description": "Type of Api to create.\n* `http` creates a SOAP to REST API\n* `soap` creates a SOAP pass-through API. Possible values include: 'SoapToRest',\n'SoapPassThrough'"
}
},
"required": [
"path"
],
"additionalProperties": false,
"description": "API Create or Update Parameters."
},
"AuthenticationSettingsContract": {
"type": "object",
"properties": {
"oAuth2": {
"$ref": "#/definitions/OAuth2AuthenticationSettingsContract",
"description": "OAuth2 Authentication settings"
},
"openid": {
"$ref": "#/definitions/OpenIdAuthenticationSettingsContract",
"description": "OpenID Connect Authentication Settings"
},
"subscriptionKeyRequired": {
"type": "boolean",
"description": "Specifies whether subscription key is required during call to this API, true - API is included\ninto closed products only, false - API is included into open products alone, null - there is a\nmix of products."
}
},
"additionalProperties": false,
"description": "API Authentication Settings."
},
"OAuth2AuthenticationSettingsContract": {
"type": "object",
"properties": {
"authorizationServerId": {
"type": "string",
"description": "OAuth authorization server identifier."
},
"scope": {
"type": "string",
"description": "operations scope."
}
},
"additionalProperties": false,
"description": "API OAuth2 Authentication settings details."
},
"OpenIdAuthenticationSettingsContract": {
"type": "object",
"properties": {
"openidProviderId": {
"type": "string",
"description": "OAuth authorization server identifier."
},
"bearerTokenSendingMethods": {
"type": "array",
"items": {
"type": "string"
},
"description": "How to send token to the server."
}
},
"additionalProperties": false,
"description": "API OAuth2 Authentication settings details."
},
"SubscriptionKeyParameterNamesContract": {
"type": "object",
"properties": {
"header": {
"type": "string",
"description": "Subscription key header name."
},
"query": {
"type": "string",
"description": "Subscription key query string parameter name."
}
},
"additionalProperties": false,
"description": "Subscription key parameter names details."
},
"ApiVersionSetContractDetails": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Identifier for existing API Version Set. Omit this value to create a new Version Set."
},
"name": {
"type": "string",
"description": "The display Name of the API Version Set."
},
"description": {
"type": "string",
"description": "Description of API Version Set."
},
"versioningScheme": {
"type": "string",
"description": "An value that determines where the API Version identifer will be located in a HTTP request.\nPossible values include: 'Segment', 'Query', 'Header'"
},
"versionQueryName": {
"type": "string",
"description": "Name of query parameter that indicates the API Version if versioningScheme is set to `query`."
},
"versionHeaderName": {
"type": "string",
"description": "Name of HTTP header parameter that indicates the API Version if versioningScheme is set to\n`header`."
}
},
"additionalProperties": false,
"description": "An API Version Set contains the common configuration for a set of API Versions relating"
},
"ApiCreateOrUpdatePropertiesWsdlSelector": {
"type": "object",
"properties": {
"wsdlServiceName": {
"type": "string",
"description": "Name of service to import from WSDL"
},
"wsdlEndpointName": {
"type": "string",
"description": "Name of endpoint(port) to import from WSDL"
}
},
"additionalProperties": false,
"description": "Criteria to limit import of WSDL to a subset of the document."
}
},
"$ref": "#/definitions/ApiCreateOrUpdateParameter"
}

Просмотреть файл

@ -0,0 +1,186 @@
{
"$schema": "http://json-schema.org/draft-06/schema#",
"definitions": {
"OperationUpdateContract": {
"type": "object",
"properties": {
"templateParameters": {
"type": "array",
"items": {
"$ref": "#/definitions/ParameterContract"
},
"description": "Collection of URL template parameters."
},
"description": {
"type": "string",
"description": "Description of the operation. May include HTML formatting tags."
},
"request": {
"$ref": "#/definitions/RequestContract",
"description": "An entity containing request details."
},
"responses": {
"type": "array",
"items": {
"$ref": "#/definitions/ResponseContract"
},
"description": "Array of Operation responses."
},
"policies": {
"type": "string",
"description": "Operation Policies"
},
"displayName": {
"type": "string",
"description": "Operation Name."
},
"method": {
"type": "string",
"description": "A Valid HTTP Operation Method. Typical Http Methods like GET, PUT, POST but not limited by\nonly them."
},
"urlTemplate": {
"type": "string",
"description": "Relative URL template identifying the target resource for this operation. May include\nparameters. Example: /customers/{cid}/orders/{oid}/?date={date}"
}
},
"additionalProperties": false,
"description": "Api Operation Update Contract details."
},
"ParameterContract": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Parameter name."
},
"description": {
"type": "string",
"description": "Parameter description."
},
"type": {
"type": "string",
"description": "Parameter type."
},
"defaultValue": {
"type": "string",
"description": "Default parameter value."
},
"required": {
"type": "boolean",
"description": "Specifies whether parameter is required or not."
},
"values": {
"type": "array",
"items": {
"type": "string"
},
"description": "Parameter values."
}
},
"required": [
"name",
"type"
],
"additionalProperties": false,
"description": "Operation parameters details."
},
"RequestContract": {
"type": "object",
"properties": {
"description": {
"type": "string",
"description": "Operation request description."
},
"queryParameters": {
"type": "array",
"items": {
"$ref": "#/definitions/ParameterContract"
},
"description": "Collection of operation request query parameters."
},
"headers": {
"type": "array",
"items": {
"$ref": "#/definitions/ParameterContract"
},
"description": "Collection of operation request headers."
},
"representations": {
"type": "array",
"items": {
"$ref": "#/definitions/RepresentationContract"
},
"description": "Collection of operation request representations."
}
},
"additionalProperties": false,
"description": "Operation request details."
},
"RepresentationContract": {
"type": "object",
"properties": {
"contentType": {
"type": "string",
"description": "Specifies a registered or custom content type for this representation, e.g. application/xml."
},
"sample": {
"type": "string",
"description": "An example of the representation."
},
"schemaId": {
"type": "string",
"description": "Schema identifier. Applicable only if 'contentType' value is neither\n'application/x-www-form-urlencoded' nor 'multipart/form-data'."
},
"typeName": {
"type": "string",
"description": "Type name defined by the schema. Applicable only if 'contentType' value is neither\n'application/x-www-form-urlencoded' nor 'multipart/form-data'."
},
"formParameters": {
"type": "array",
"items": {
"$ref": "#/definitions/ParameterContract"
},
"description": "Collection of form parameters. Required if 'contentType' value is either\n'application/x-www-form-urlencoded' or 'multipart/form-data'.."
}
},
"required": [
"contentType"
],
"additionalProperties": false,
"description": "Operation request/response representation details."
},
"ResponseContract": {
"type": "object",
"properties": {
"statusCode": {
"type": "number",
"description": "Operation response HTTP status code."
},
"description": {
"type": "string",
"description": "Operation response description."
},
"representations": {
"type": "array",
"items": {
"$ref": "#/definitions/RepresentationContract"
},
"description": "Collection of operation response representations."
},
"headers": {
"type": "array",
"items": {
"$ref": "#/definitions/ParameterContract"
},
"description": "Collection of operation response headers."
}
},
"required": [
"statusCode"
],
"additionalProperties": false,
"description": "Operation response details."
}
},
"$ref": "#/definitions/OperationUpdateContract"
}

Просмотреть файл

@ -0,0 +1 @@
The files are generated using https://marketplace.visualstudio.com/items?itemName=marcoq.vscode-typescript-to-json-schema

439
snippets.json Normal file
Просмотреть файл

@ -0,0 +1,439 @@
{
"policy-document": {
"prefix": "policy-document",
"body": [
"<policies>",
"\t<inbound>",
"\t\t<base/>",
"\t\t$0",
"\t</inbound>",
"\t<backend>",
"\t\t<base/>",
"\t</backend>",
"\t<outbound>",
"\t\t<base/>",
"\t</outbound>",
"\t<on-error>",
"\t\t<base/>",
"\t</on-error>",
"</policies>"
],
"description": "Policy document boilerplate for scopes below Global"
},
"policy-document-global": {
"prefix": "policy-document-global",
"body": [
"<policies>",
"\t<inbound>",
"\t\t$0",
"\t</inbound>",
"\t<backend>",
"\t\t<forward-request/>",
"\t</backend>",
"\t<outbound>",
"\t</outbound>",
"\t<on-error>",
"\t</on-error>",
"</policies>"
],
"description": "Policy document boilerplate for Global scope"
},
"authentication-basic": {
"prefix": "authentication-basic",
"body": [
"<authentication-basic username=\"$1\" password=\"$2\" />"
],
"description": "Authenticate with the backend service using Basic authentication. Use in the inbound section at API scope."
},
"authentication-certificate": {
"prefix": "authentication-certificate",
"description": "Authenticate with the backend service using a client certificate. Use in the inbound section at API scope.",
"body": [
"<authentication-certificate thumbprint=\"$1\" />"
]
},
"base": {
"prefix": "base",
"body": [
"<base />"
]
},
"cache-lookup": {
"prefix": "cache-lookup",
"description": "Perform cache lookup and return a valid cached response when available. Appropriately respond to cache validation requests from callers. Use anywhere in the inbound section at Product, API, or Operation scopes.",
"body": [
"<cache-lookup vary-by-developer=\"${1|true,false|}\" vary-by-developer-groups=\"${2|true,false|}\" downstream-caching-type=\"${3|none,private,public|}\" must-revalidate=\"${4|true,false|}\" allow-private-response-caching=\"${4|true,false|}\">",
"\t<vary-by-header>${5:header name}</vary-by-header>",
"\t<vary-by-query-parameter>${6:query parameter}</vary-by-query-parameter>",
"</cache-lookup>"
]
},
"cache-lookup-value": {
"prefix": "cache-lookup-value",
"description": "Perform cache lookup and returned value under the key, if available, or default. If value is not present and no default is specified, variable will not be set. Use at any scope in any section except <backend>.",
"body": [
"<cache-lookup-value key=\"$1\" default-value=\"$2\" variable-name=\"$3\" />"
]
},
"cache-remove-value": {
"prefix": "cache-remove-value",
"description": "Remove value from cache under the key. Use at any scope in any section except <backend>.",
"body": [
"<cache-remove-value key=\"$1\" />"
]
},
"cache-store": {
"prefix": "cache-store",
"description": "Cache responses according to the specified cache configuration. Use anywhere in the outbound section at Product, API, or Operation scopes.",
"body": [
"<cache-store duration=\"${1:seconds}\" />"
]
},
"cache-store-value": {
"prefix": "cache-store-value",
"description": "Store value in cache under a key for duration. Use at any scope in any section except <backend>.",
"body": [
"<cache-store-value key=\"$1\" value=\"@($2)\" duration=\"${3:seconds}\" />"
]
},
"check-header": {
"prefix": "check-header",
"description": "Check header and return specified HTTP status code if it doesn't exist or match expected value. Works for both response and request headers – policy can be applied in inbound or outbound sections at any scope.",
"body": [
"<check-header name=\"$1\" failed-check-httpcode=\"$2\" failed-check-error-message=\"$3\" ignore-case=\"${4|true,false|}\">",
"\t<value>$5</value>",
"</check-header>"
]
},
"choose": {
"prefix": "choose",
"description": "Conditionally apply policy statements based on the results of the evaluation of Boolean expressions. Use at any scope in the inbound and outbound sections.",
"body": [
"<choose>",
"\t<when condition=\"@($1)\">",
"\t\t$0",
"\t</when>",
"\t<otherwise>",
"\t</otherwise>",
"</choose>"
]
},
"cors": {
"prefix": "cors",
"description": "CORS stands for cross-origin resource sharing. Add CORS support to an operation or an API to allow cross-domain calls from browser-based clients. Use in the inbound section only.",
"body": [
"<cors allow-credentials=\"${1|true,false|}\">",
"\t<allowed-origins>",
"\t\t<origin>${2:*}</origin>",
"\t</allowed-origins>",
"\t<allowed-methods>",
"\t\t<method>${3:*}</method>",
"\t</allowed-methods>",
"\t<allowed-headers>",
"\t\t<header>${4:*}</header>",
"\t</allowed-headers>",
"\t<expose-headers>",
"\t\t<header>${5:*}</header>",
"\t</expose-headers>",
"</cors>"
]
},
"cross-domain": {
"prefix": "cross-domain",
"description": "Make the API accessible from Adobe Flash and Microsoft Silverlight browser-based clients. Use in the inbound section at Global scope.",
"body": [
"<cross-domain>",
"\t<cross-domain-policy>",
"\t\t<allow-http-request-headers-from domain=\"*\" headers=\"*\" />",
"\t</cross-domain-policy>",
"</cross-domain>"
]
},
"find-and-replace": {
"prefix": "find-and-replace",
"description": "Find a request or response substring and replace it with a different substring. Use in the inbound and outbound sections at any scope.",
"body": [
"<find-and-replace from=\"$1\" to=\"$2\" />"
]
},
"forward-request": {
"prefix": "forward-request",
"description": "Forward request to the backend service using information in the context and receive a response, waiting no longer then specified timeout value. Use at any scope in the backend section.",
"body": [
"<forward-request timeout=\"${1:seconds}\" />"
]
},
"ip-filter": {
"prefix": "ip-filter",
"description": "Allow calls only from specific IP addresses and/or address ranges. Forbid calls from specific IP addresses and/or address ranges. Use in the inbound section at any scope.",
"body": [
"<ip-filter action=\"${1|allow,forbid|}\">",
"\t<address-range from=\"$2\" to=\"$3\" />",
"</ip-filter>"
]
},
"jsonp": {
"prefix": "jsonp",
"description": "Add support for JSONP to an operation or an API to allow cross-domain calls from JavaScript browser-based clients. Use in the outbound section only.",
"body": [
"<jsonp callback-parameter-name=\"$1\" />"
]
},
"json-to-xml": {
"prefix": "json-to-xml",
"description": "Convert request or response body from JSON to XML. Use in the inbound or outbound sections at API or Operation scopes.",
"body": [
"<json-to-xml apply=\"${1|always,content-type-json|}\" consider-accept-header=\"${2|true,false|}\" parse-date=\"${3|true,false|}\" />"
]
},
"limit-concurrency": {
"prefix": "limit-concurrency",
"description": "Limit how many calls may be processed in parallel for the duration of this policy's body.",
"body": [
"<limit-concurrency key=\"$1\" max-count=\"$2\" >",
"</limit-concurrency>"
]
},
"log-to-eventhub": {
"prefix": "log-to-eventhub",
"description": "Send custom messages to Event Hub. Use at any scope in the inbound or outbound sections.",
"body": [
"<log-to-eventhub logger-id =\"$1\">",
"\t@($2)",
"</log-to-eventhub>"
]
},
"mock-response": {
"prefix": "mock-response",
"description": "Mock response based on operation responses samples/schemas. Use at any scope in the inbound or outbound sections.",
"body": [
"<mock-response status-code=\"$1\" content-type=\"$2\" />"
]
},
"proxy": {
"prefix": "proxy",
"description": "Route requests forwarded to backends via an HTTP proxy. Use at any scope in the inbound section.",
"body": [
"<proxy url=\"$1\" username=\"$2\" password=\"$3\" />"
]
},
"quota": {
"prefix": "quota",
"description": "Enforce a renewable or lifetime call volume and/or bandwidth quota per subscription. Use in the inbound section at Product scope.",
"body": [
"<quota calls=\"${1:number}\" bandwidth=\"${2:kilobytes}\" renewal-period=\"${3:seconds}\">",
"\t<api name=\"$4\" calls=\"${5:number}\" bandwidth=\"${6:kilobytes}\">",
"\t\t<operation name=\"$7\" calls=\"${8:number}\" bandwidth=\"${9:kilobytes}\" />",
"\t</api>",
"</quota>"
]
},
"quota-by-key": {
"prefix": "quota-by-key",
"description": "Enforce a renewable or lifetime call volume and/or bandwidth quota per calculated key. Use in the inbound section at any scope.",
"body": [
"<quota-by-key calls=\"${1:number}\" bandwidth=\"${2:kilobytes}\" renewal-period=\"$3\" counter-key=\"@($4)\" />"
]
},
"rate-limit": {
"prefix": "rate-limit",
"description": "Arrest usage spikes by limiting calls and/or bandwidth consumption rate per subscription. Use in the inbound section at Product scope.",
"body": [
"<rate-limit calls=\"${1:number}\" renewal-period=\"${2:seconds}\">",
"\t<api name=\"$3\" calls=\"${4:number}\">",
"\t\t<operation name=\"$5\" calls=\"${6:number}\" />",
"\t</api>",
"</rate-limit>"
]
},
"rate-limit-by-key": {
"prefix": "rate-limit-by-key",
"description": "Arrest usage spikes by limiting calls and/or bandwidth consumption rate per calculated key. Use in the inbound section at any scope.",
"body": [
"<rate-limit-by-key calls=\"${1:number}\" renewal-period=\"${2:seconds}\" counter-key=\"@($3)\" />"
]
},
"redirect-content-urls": {
"prefix": "redirect-content-urls",
"description": "Use in the outbound section to re-write response body links and Location header values making them point to the proxy. Use in the inbound section for an opposite effect. Apply at API or Operation scopes.",
"body": [
"<redirect-content-urls />"
]
},
"retry": {
"prefix": "retry",
"description": "Retry execution of the enclosed policy statements, if and until the condition is met. Execution will repeat at the specified time interval, up to the specified count.",
"body": [
"<retry condition=\"@($1)\" count=\"${2:number}\" interval=\"${3:seconds}\">",
"\t$0",
"</retry>"
]
},
"return-response": {
"prefix": "return-response",
"description": "Abort pipeline execution and return the specified response directly to the caller. Use at any scope in the inbound and outbound sections.",
"body": [
"<return-response>",
"\t<set-status code=\"$1\" reason=\"$2\" />",
"\t<set-header name=\"$3\" exists-action=\"${4|override,skip,append,delete|}\">",
"\t\t<value>$5</value>",
"\t</set-header>",
"\t<set-body>$6</set-body>",
"</return-response>"
]
},
"rewrite-uri": {
"prefix": "rewrite-uri",
"description": "Convert request URL from its public form to the form expected by the web service. Use anywhere in the inbound section at Operation scope only.",
"body": [
"<rewrite-uri template=\"$1\" copy-unmatched-params=\"${2|true,false|}\" />"
]
},
"send-one-way-request": {
"prefix": "send-one-way-request",
"description": "Send provided request to the specified URL, without waiting for response. Use at any scope in the inbound and outbound sections.",
"body": [
"<send-one-way-request mode=\"${1|new,copy|}\">",
"\t<set-url>$2</set-url>",
"\t<set-method>${3|GET,PUT,PATCH,DELETE|}</set-method>",
"\t<set-header name=\"$4\" exists-action=\"${5|override,skip,append,delete|}\">",
"\t\t<value>$6</value>",
"\t</set-header>",
"\t<set-body>$7</set-body>",
"</send-one-way-request>"
]
},
"send-request": {
"prefix": "send-request",
"description": "Send provided request to the specified URL, waiting no longer then set timeout value. Use at any scope in the inbound and outbound sections.",
"body": [
"<send-request mode=\"${1|new,copy|}\" response-variable-name=\"$2\" timeout=\"${3:seconds}\" ignore-error=\"${4|true,false|}\">",
"\t<set-url>$5</set-url>",
"\t<set-method>${6|GET,PUT,PATCH,DELETE|}</set-method>",
"\t<set-header name=\"$7\" exists-action=\"${8|override,skip,append,delete|}\">",
"\t\t<value>$9</value>",
"\t</set-header>",
"\t<set-body>$10</set-body>",
"</send-request>"
]
},
"set-backend-service": {
"prefix": "set-backend-service",
"description": "Change backend service where the incoming calls will be directed. Use in the inbound section only at any scope.",
"body": [
"<set-backend-service base-url=\"$1\" />"
]
},
"set-body": {
"prefix": "set-body",
"description": "Set message body to a specific string value. The policy has no effect on the Content-Type header value. Use at any scope in the inbound or outbound sections.",
"body": [
"<set-body template=\"${1|none,liquid|}\">$2</set-body>"
]
},
"set-header": {
"prefix": "set-header",
"description": "Add a new header, change the value of an existing header or remove a header. Works for both response and request headers – policy can be applied in inbound or outbound sections at any scope.",
"body": [
"<set-header name=\"$1\" exists-action=\"${2|override,skip,append,delete|}\">",
"\t<value>$3</value>",
"</set-header>"
]
},
"set-method": {
"prefix": "set-method",
"description": "Change HTTP method to the specified value",
"body": [
"<set-method>${1|GET,PUT,PATCH,DELETE|}</set-method>"
]
},
"set-query-parameter": {
"prefix": "set-query-parameter",
"description": "Add a new query string parameter, change the value of an existing parameter or remove a parameter. Can be applied in the inbound section at any scope.",
"body": [
"<set-query-parameter name=\"$1\" exists-action=\"${2|override,skip,append,delete|}\">",
"\t<value>$3</value>",
"</set-query-parameter>"
]
},
"set-status": {
"prefix": "set-status",
"description": "Change HTTP status code to the specified value. Use at any scope in the outbound sections.",
"body": [
"<set-status code=\"$1\" reason=\"$2\" />"
]
},
"set-variable": {
"prefix": "set-variable",
"description": "Persist a value in a named context variable for later access from expressions. Use at any scope in the inbound and outbound sections.",
"body": [
"<set-variable name=\"$1\" value=\"@($2)\" />"
]
},
"trace": {
"prefix": "trace",
"description": "Output information into trace logs, if request is executed with tracing enabled.",
"body": [
"<trace source=\"$1\">",
"\t@($2)",
"</trace>"
]
},
"validate-jwt": {
"prefix": "validate-jwt",
"description": "Check and validate a JWT in a header or query parameter. Use in the inbound section at any scope.",
"body": [
"<validate-jwt header-name=\"$1\" failed-validation-httpcode=\"${2:401}\" failed-validation-error-message=\"${3:Unauthorized}\" require-expiration-time=\"${4|true,false|}\" require-scheme=\"${5:Bearer}\" require-signed-tokens=\"${6|true,false|}\" clock-skew=\"${7:0}\">",
"\t<openid-config url=\"$8\">",
"\t<issuer-signing-keys>",
"\t\t<key>${9:Base64 Encoded Key}</key>",
"\t</issuer-signing-keys>",
"\t<audiences>",
"\t\t<audience>$10</audience>",
"\t</audiences>",
"\t<issuers>",
"\t\t<issuer>$11</issuer>",
"\t</issuers>",
"\t<required-claims>",
"\t\t<claim name=\"$12\" match=\"${13|all,any|}\">",
"\t\t\t<value>$14</value>",
"\t\t</claim>",
"\t</required-claims>",
"</validate-jwt>"
]
},
"wait": {
"prefix": "wait",
"description": "Wait for all or any of the send request policies to complete before proceeding. Use at any scope in the inbound and outbound sections.",
"body": [
"<wait for=\"${8|all,any|}\">",
"\t$0",
"</wait>"
]
},
"xml-to-json": {
"prefix": "xml-to-json",
"description": "Convert request or response body from XML to either \"JSON friendly\" or \"XML faithful\" form of JSON. Use in the inbound or outbound sections at API or Operation scopes.",
"body": [
"<xml-to-json kind=\"${1|javascript-friendly,direct|}\" apply=\"${2|always,content-type-xml|}\" consider-accept-header=\"${3|true,false|}\" />"
]
},
"xsl-transform": {
"prefix": "xsl-transform",
"description": "Transform request or response body using XSLTransform. Use in the inbound, outbound and on-error sections at any scope.",
"body": [
"<xsl-transform>",
"\t<parameter name=\"$1\">@($2)</parameter>",
"\t<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">",
"\t\t<xsl:output method=\"xml\" indent=\"yes\" />",
"\t\t<xsl:param name=\"$3\" />",
"\t\t<xsl:template match=\"* | @* | node()\">",
"\t\t\t<xsl:copy>",
"\t\t\t\t<xsl:apply-templates select=\"@* | node()|*\" />",
"\t\t\t</xsl:copy>",
"\t\t</xsl:template>",
"\t</xsl:stylesheet>",
"</xsl-transform>"
]
}
}

Просмотреть файл

@ -0,0 +1,15 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { ServiceTreeItem } from '../explorer/ServiceTreeItem';
import { ext } from '../extensionVariables';
export async function copySubscriptionKey(node?: ServiceTreeItem): Promise<void> {
if (!node) {
node = <ServiceTreeItem>await ext.tree.showTreeItemPicker(ServiceTreeItem.contextValue);
}
const key = await node.copySubscriptionKey();
vscode.env.clipboard.writeText(key);
}

Просмотреть файл

@ -0,0 +1,22 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzureParentTreeItem, AzureTreeItem, IActionContext, SubscriptionTreeItem } from 'vscode-azureextensionui';
import { ext } from '../extensionVariables';
import { treeUtils } from '../utils/treeUtils';
export async function createService(this: IActionContext, subscription?: AzureParentTreeItem | string, resourceGroup?: string): Promise<string> {
let node: AzureParentTreeItem;
if (typeof subscription === 'string') {
node = await treeUtils.getSubscriptionNode(ext.tree, subscription);
} else if (!subscription) {
node = <AzureParentTreeItem>await ext.tree.showTreeItemPicker(SubscriptionTreeItem.contextValue);
} else {
node = subscription;
}
const serviceNode: AzureTreeItem = await node.createChild({ actionContext: this, resourceGroup });
return serviceNode.fullId;
}

Просмотреть файл

@ -0,0 +1,16 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import ApiManagementClient from 'azure-arm-apimanagement';
import { ApiManagementServiceResource } from 'azure-arm-apimanagement/lib/models';
import { IResourceGroupWizardContext, IStorageAccountWizardContext } from 'vscode-azureextensionui';
export interface IServiceWizardContext extends IResourceGroupWizardContext, IStorageAccountWizardContext {
client: ApiManagementClient;
sku? : string;
email? : string;
serviceName?: string;
service?: ApiManagementServiceResource;
}

Просмотреть файл

@ -0,0 +1,50 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ApiManagementModels } from "azure-arm-apimanagement";
import { ApiManagementServiceResource } from "azure-arm-apimanagement/lib/models";
import { MessageItem, Progress, window } from "vscode";
import { AzureWizardExecuteStep } from "vscode-azureextensionui";
import { ext } from "../../extensionVariables";
import { localize } from "../../localize";
import { nonNullProp, nonNullValueAndProp } from "../../utils/nonNull";
import { IServiceWizardContext } from "./IServiceWizardContext";
export class ServiceCreateStep extends AzureWizardExecuteStep<IServiceWizardContext> {
public priority: number = 140;
public async execute(wizardContext: IServiceWizardContext, progress: Progress<{ message?: string; increment?: number }>): Promise<void> {
const creatingNewService: string = localize('creatingNewAPIManagementService', 'Creating new API Management service "{0}"...', wizardContext.serviceName);
ext.outputChannel.appendLine(creatingNewService);
progress.report({ message: creatingNewService });
wizardContext.service = await wizardContext.client.apiManagementService.createOrUpdate(nonNullValueAndProp(wizardContext.resourceGroup, 'name'), nonNullProp(wizardContext, 'serviceName'), <ApiManagementServiceResource>{
location: nonNullValueAndProp(wizardContext.location, 'name'),
sku: <ApiManagementModels.ApiManagementServiceSkuProperties>{
name: nonNullValueAndProp(wizardContext, 'sku')
},
publisherEmail: nonNullValueAndProp(wizardContext, 'email'),
publisherName: nonNullValueAndProp(wizardContext, 'email')
});
const createdNewService: string = localize('createdNewAPIManagementService', 'Created new API Management service "{0}".', wizardContext.service.name);
ext.outputChannel.appendLine(createdNewService);
ext.outputChannel.appendLine('');
const viewOutput: MessageItem = {
title: localize('viewOutput', 'View Output')
};
// Note: intentionally not waiting for the result of this before returning
window.showInformationMessage(createdNewService, viewOutput).then((result: MessageItem | undefined) => {
if (result === viewOutput) {
ext.outputChannel.show();
}
});
}
public shouldExecute(wizardContext: IServiceWizardContext): boolean {
return !wizardContext.service;
}
}

Просмотреть файл

@ -0,0 +1,41 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ApiManagementModels } from "azure-arm-apimanagement";
import { AzureNameStep, IAzureNamingRules, ResourceGroupListStep, resourceGroupNamingRules } from "vscode-azureextensionui";
import { ext } from "../../extensionVariables";
import { localize } from "../../localize";
import { nonNullProp } from "../../utils/nonNull";
import { IServiceWizardContext } from "./IServiceWizardContext";
export class ServiceNameStep extends AzureNameStep<IServiceWizardContext> {
public async prompt(wizardContext: IServiceWizardContext): Promise<void> {
const prompt: string = localize('serviceNamePrompt', 'Enter a globally unique name for the new API Management instance.');
wizardContext.serviceName = (await ext.ui.showInputBox({
prompt,
validateInput: async (value: string): Promise<string | undefined> => {
value = value ? value.trim() : '';
const nameAvailability: ApiManagementModels.ApiManagementServiceNameAvailabilityResult = await wizardContext.client.apiManagementService.checkNameAvailability({name: value});
if (!nameAvailability.nameAvailable) {
return nameAvailability.message;
} else {
return undefined;
}
}
})).trim();
const namingRules: IAzureNamingRules[] = [resourceGroupNamingRules];
wizardContext.relatedNameTask = this.generateRelatedName(wizardContext, nonNullProp(wizardContext, "serviceName"), namingRules);
}
public shouldPrompt(wizardContext: IServiceWizardContext): boolean {
return !wizardContext.serviceName;
}
protected async isRelatedNameAvailable(wizardContext: IServiceWizardContext, name: string): Promise<boolean> {
return ResourceGroupListStep.isNameAvailable(wizardContext, name);
}
}

Просмотреть файл

@ -0,0 +1,20 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzureWizardPromptStep } from "vscode-azureextensionui";
import { ext } from "../../extensionVariables";
import { IServiceWizardContext } from "./IServiceWizardContext";
export class ServiceSkuStep extends AzureWizardPromptStep<IServiceWizardContext> {
public async prompt(wizardContext: IServiceWizardContext): Promise<void> {
const skus = ['Consumption', 'Developer', 'Standard', 'Basic', 'Premium'];
const sku = await ext.ui.showQuickPick( skus.map((s) => {return { label: s, description: '', detail: '' }; }) , { canPickMany: false});
wizardContext.sku = sku.label;
}
public shouldPrompt(wizardContext: IServiceWizardContext): boolean {
return !wizardContext.sku;
}
}

Просмотреть файл

@ -0,0 +1,15 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzureTreeItem } from "vscode-azureextensionui";
import { ext } from "../extensionVariables";
export async function deleteNode(expectedContextValue: string | RegExp, node?: AzureTreeItem): Promise<void> {
if (!node) {
node = await ext.tree.showTreeItemPicker(expectedContextValue);
}
await node.deleteTreeItem();
}

Просмотреть файл

@ -0,0 +1,119 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as fse from 'fs-extra';
import { OpenDialogOptions, ProgressLocation, Uri, window, workspace } from "vscode";
import { ApisTreeItem } from "../explorer/ApisTreeItem";
import { ServiceTreeItem } from '../explorer/ServiceTreeItem';
import { ext } from "../extensionVariables";
import { localize } from "../localize";
import { IOpenApiImportObject } from "../openApi/OpenApiImportObject";
import { OpenApiParser } from '../openApi/OpenApiParser';
import { processError } from '../utils/errorUtil';
import { requestUtil } from '../utils/requestUtil';
// tslint:disable: no-any
export async function importOpenApi(node?: ApisTreeItem, importUsingLink: boolean = false): Promise<void> {
if (!node) {
const serviceNode = <ServiceTreeItem>await ext.tree.showTreeItemPicker(ServiceTreeItem.contextValue);
node = serviceNode.apisTreeItem;
}
let documentString: string | undefined;
if (!importUsingLink) {
const uris = await askDocument();
const uri = uris[0];
const fileContent = await fse.readFile(uri.fsPath);
documentString = fileContent.toString();
} else {
const openApiLink = await askLink();
documentString = await requestUtil(openApiLink);
}
if (documentString !== undefined && documentString.trim() !== "") {
const documentJson = JSON.parse(documentString);
const document = await parseDocument(documentJson);
const apiName = await askApiName();
window.withProgress(
{
location: ProgressLocation.Notification,
title: localize("importingApi", `Importing API '${apiName}' to API Management service ${node.root.serviceName} ...`),
cancellable: false
},
// tslint:disable-next-line:no-non-null-assertion
async () => { return node!.createChild({ apiName: apiName, document: document }); }
).then(async () => {
// tslint:disable-next-line:no-non-null-assertion
await node!.refresh();
window.showInformationMessage(localize("importedApi", `Imported API '${apiName}' to API Management succesfully.`));
});
}
}
async function askApiName() : Promise<string> {
const apiNamePrompt: string = localize('apiNamePrompt', 'Enter API Name.');
return (await ext.ui.showInputBox({
prompt: apiNamePrompt,
validateInput: async (value: string): Promise<string | undefined> => {
value = value ? value.trim() : '';
return validateApiName(value);
}
})).trim();
}
async function askDocument(): Promise<Uri[]> {
const openDialogOptions: OpenDialogOptions = {
canSelectFiles: true,
canSelectFolders: false,
canSelectMany: false,
openLabel: "Import",
filters: {
JSON: ["json"]
}
};
const rootPath = workspace.rootPath;
if (rootPath) {
openDialogOptions.defaultUri = Uri.file(rootPath);
}
return await ext.ui.showOpenDialog(openDialogOptions);
}
async function askLink() : Promise<string> {
const promptStr: string = localize('apiLinkPrompt', 'Specify a OpenAPI 2.0 or 3.0 link.');
return (await ext.ui.showInputBox({
prompt: promptStr,
placeHolder: 'https://',
validateInput: async (value: string): Promise<string | undefined> => {
value = value ? value.trim() : '';
const regexp = /http(s?):\/\/[\d\w][\d\w]*(\.[\d\w][\d\w-]*)*(:\d+)?(\/[\d\w-\.\?,'/\\\+&amp;=:%\$#_]*)?/;
const isUrlValid = regexp.test(value);
if (!isUrlValid) {
return localize("invalidOpenApiLink", "Provide a valid link. example - https://petstore.swagger.io/v2/swagger.json");
} else {
return undefined;
}
}
})).trim();
}
// tslint:disable: no-unsafe-any
async function parseDocument(documentJson: any): Promise<IOpenApiImportObject> {
try {
return await new OpenApiParser().parse(documentJson);
} catch (error) {
throw new Error(processError(error, localize("openApiJsonParseError", "Could not parse the provided OpenAPI document.")));
}
}
function validateApiName(apiName: string): string | undefined {
if (apiName.length > 256) {
return localize("apiNameMaxLength", 'API name cannot be more than 256 characters long.');
}
if (apiName.match(/^[^*#&+:<>?]+$/) === null) {
return localize("apiNameMaxLength", 'Invalid API Name.');
}
return undefined;
}

Просмотреть файл

@ -0,0 +1,15 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzureTreeItem } from 'vscode-azureextensionui';
import { ServiceTreeItem } from '../explorer/ServiceTreeItem';
import { ext } from '../extensionVariables';
export async function openInPortal(node?: AzureTreeItem): Promise<void> {
if (!node) {
node = await ext.tree.showTreeItemPicker(ServiceTreeItem.contextValue);
}
await node.openInPortal();
}

Просмотреть файл

@ -0,0 +1,26 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import { ApiOperationTreeItem } from "../explorer/ApiOperationTreeItem";
import { ext } from '../extensionVariables';
import { createTemporaryFile } from "../utils/fsUtil";
import { nameUtil } from '../utils/nameUtil';
import { writeToEditor } from '../utils/vscodeUtils';
export async function testOperation(node?: ApiOperationTreeItem): Promise<void> {
if (!node) {
node = <ApiOperationTreeItem>await ext.tree.showTreeItemPicker(ApiOperationTreeItem.contextValue);
}
// using https://github.com/Huachao/vscode-restclient
const fileName = `${nameUtil(node.root)}.http`;
const localFilePath: string = await createTemporaryFile(fileName);
const data: string = await node.getOperationTestInfo();
const document: vscode.TextDocument = await vscode.workspace.openTextDocument(localFilePath);
const textEditor: vscode.TextEditor = await vscode.window.showTextDocument(document);
await writeToEditor(textEditor, data);
await textEditor.document.save();
}

75
src/constants.ts Normal file
Просмотреть файл

@ -0,0 +1,75 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export const extensionName = 'vscode-azureapim';
export const extensionPrefix: string = 'azureApiManagement';
export const doubleClickDebounceDelay = 500; //milliseconds
export const topItemCount: number = 20;
export const swaggerSchema = "application/vnd.ms-azure-apim.swagger.definitions+json";
export const openApiSchema = "application/vnd.oai.openapi.components+json";
export const swaggerExport = "swagger";
export const openApiExport = "openapi%2Bjson";
export const openApiAcceptHeader = "application/vnd.oai.openapi+json";
export const swaggerAcceptHeader = "application/vnd.swagger.doc+json";
export const showSavePromptConfigKey = "azureApiManagement.showSavePrompt";
export const policyFormat = "rawxml";
export const emptyGlobalPolicyXml =
`<!--
IMPORTANT:
- Policy elements can appear only within the <inbound>, <outbound>, <backend> section elements.
- To apply a policy to the incoming request (before it is forwarded to the backend service), place a corresponding policy element within the <inbound> section element.
- To apply a policy to the outgoing response (before it is sent back to the caller), place a corresponding policy element within the <outbound> section element.
- To add a policy, place the cursor at the desired insertion point and select a policy from the sidebar.
- To remove a policy, delete the corresponding policy statement from the policy document.
- Position the <base> element within a section element to inherit all policies from the corresponding section element in the enclosing scope.
- Remove the <base> element to prevent inheriting policies from the corresponding section element in the enclosing scope.
- Policies are applied in the order of their appearance, from the top down.
- Comments within policy elements are not supported and may disappear. Place your comments between policy elements or at a higher level scope.
-->
<policies>
<inbound>
</inbound>
<backend>
<forward-request />
</backend>
<outbound>
</outbound>
<on-error>
</on-error>
</policies>`;
export const emptyPolicyXml =
`<!--
IMPORTANT:
- Policy elements can appear only within the <inbound>, <outbound>, <backend> section elements.
- To apply a policy to the incoming request (before it is forwarded to the backend service), place a corresponding policy element within the <inbound> section element.
- To apply a policy to the outgoing response (before it is sent back to the caller), place a corresponding policy element within the <outbound> section element.
- To add a policy, place the cursor at the desired insertion point and select a policy from the sidebar.
- To remove a policy, delete the corresponding policy statement from the policy document.
- Position the <base> element within a section element to inherit all policies from the corresponding section element in the enclosing scope.
- Remove the <base> element to prevent inheriting policies from the corresponding section element in the enclosing scope.
- Policies are applied in the order of their appearance, from the top down.
- Comments within policy elements are not supported and may disappear. Place your comments between policy elements or at a higher level scope.
-->
<policies>
<inbound>
<base />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>`;
export const sessionFolderKey = "currentSessionWorkingFolder";

Просмотреть файл

@ -0,0 +1,133 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ApiManagementClient, ApiManagementModels } from 'azure-arm-apimanagement';
import { MessageItem } from 'vscode';
import { AzureTreeItem, AzureWizard, AzureWizardExecuteStep, AzureWizardPromptStep, createAzureClient, createTreeItemsWithErrorHandling, IActionContext, LocationListStep, parseError, ResourceGroupCreateStep, ResourceGroupListStep, SubscriptionTreeItem } from 'vscode-azureextensionui';
import { IServiceWizardContext } from '../commands/createService/IServiceWizardContext';
import { ServiceCreateStep } from '../commands/createService/ServiceCreateStep';
import { ServiceNameStep } from '../commands/createService/ServiceNameStep';
import { ServiceSkuStep } from '../commands/createService/ServiceSkuStep';
import { extensionPrefix } from '../constants';
import { ext } from '../extensionVariables';
import { localize } from "../localize";
import { nonNullProp } from '../utils/nonNull';
import { getWorkspaceSetting, updateGlobalSetting } from '../vsCodeConfig/settings';
import { ServiceTreeItem } from './ServiceTreeItem';
export class ApiManagementProvider extends SubscriptionTreeItem {
public readonly childTypeLabel: string = localize('azureApiManagement.ApimService', 'API Management Service');
private _nextLink: string | undefined;
public hasMoreChildrenImpl(): boolean {
return this._nextLink !== undefined;
}
public async loadMoreChildrenImpl(clearCache: boolean): Promise<AzureTreeItem[]> {
if (clearCache) {
this._nextLink = undefined;
}
const client: ApiManagementClient = createAzureClient(this.root, ApiManagementClient);
let apiManagementServiceList: ApiManagementModels.ApiManagementServiceListResult;
try {
apiManagementServiceList = this._nextLink === undefined ?
await client.apiManagementService.list() :
await client.apiManagementService.listNext(this._nextLink);
} catch (error) {
if (parseError(error).errorType.toLowerCase() === 'notfound') {
// This error type means the 'Microsoft.ApiManagement' provider has not been registered in this subscription
// In that case, we know there are no Api Management Services, so we can return an empty array
// (The provider will be registered automatically if the user creates a new Api Management Instance)
return [];
} else {
throw error;
}
}
this._nextLink = apiManagementServiceList.nextLink;
return createTreeItemsWithErrorHandling(
this,
apiManagementServiceList,
"invalidApiManagementService",
async (service: ApiManagementModels.ApiManagementServiceResource) => new ServiceTreeItem(this, client, service),
(service: ApiManagementModels.ApiManagementServiceResource) => {
return service.name;
});
}
public async createChildImpl(showCreatingTreeItem: (label: string) => void, userOptions?: { actionContext: IActionContext; resourceGroup?: string }): Promise<AzureTreeItem> {
// Ideally actionContext should always be defined, but there's a bug with the NodePicker. Create a 'fake' actionContext until that bug is fixed
// https://github.com/Microsoft/vscode-azuretools/issues/120
// tslint:disable-next-line:strict-boolean-expressions
const actionContext: IActionContext = userOptions ? userOptions.actionContext : <IActionContext>{ properties: {}, measurements: {} };
const client: ApiManagementClient = createAzureClient(this.root, ApiManagementClient);
const wizardContext: IServiceWizardContext = {
client: client,
subscriptionId: this.root.subscriptionId,
subscriptionDisplayName: this.root.subscriptionDisplayName,
credentials: this.root.credentials,
environment: this.root.environment
};
const promptSteps: AzureWizardPromptStep<IServiceWizardContext>[] = [];
const executeSteps: AzureWizardExecuteStep<IServiceWizardContext>[] = [];
promptSteps.push(new ServiceNameStep());
wizardContext.email = this.root.userId;
const advancedCreationKey: string = 'advancedCreation';
const advancedCreation: boolean = !!getWorkspaceSetting(advancedCreationKey);
actionContext.properties.advancedCreation = String(advancedCreation);
if (!advancedCreation) {
wizardContext.sku = "Consumption";
await LocationListStep.setLocation(wizardContext, 'westus');
executeSteps.push(new ResourceGroupCreateStep());
} else {
promptSteps.push(new ServiceSkuStep());
promptSteps.push(new LocationListStep());
promptSteps.push(new ResourceGroupListStep());
}
executeSteps.push(new ServiceCreateStep());
const title: string = localize('serviceCreatingTitle', 'Create new API Management instance in Azure');
const wizard: AzureWizard<IServiceWizardContext> = new AzureWizard(wizardContext, { promptSteps, executeSteps, title });
await wizard.prompt(actionContext);
showCreatingTreeItem(nonNullProp(wizardContext, 'serviceName'));
if (!advancedCreation) {
const newName: string | undefined = await wizardContext.relatedNameTask;
if (!newName) {
throw new Error(localize('noUniqueName', 'Failed to generate unique name for resources.'));
}
wizardContext.newResourceGroupName = newName;
}
try {
await wizard.execute(actionContext);
} catch (error) {
if (!parseError(error).isUserCancelledError && !advancedCreation) {
const message: string = localize('tryAdvancedCreate', 'Modify the setting "{0}.{1}" if you want to change the default values when creating a API Management Instance in Azure.', extensionPrefix, advancedCreationKey);
const btn: MessageItem = { title: localize('turnOn', 'Turn on advanced creation') };
// tslint:disable-next-line: no-floating-promises
ext.ui.showWarningMessage(message, btn).then(async result => {
if (result === btn) {
await updateGlobalSetting(advancedCreationKey, true);
}
});
}
throw error;
}
const service: ApiManagementModels.ApiManagementServiceResource = nonNullProp(wizardContext, 'service');
return new ServiceTreeItem(this, client, service);
}
}

Просмотреть файл

@ -0,0 +1,88 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ApiManagementModels } from "azure-arm-apimanagement";
import { ProgressLocation, window } from "vscode";
import { AzureParentTreeItem, AzureTreeItem, DialogResponses, ISubscriptionRoot, UserCancelledError } from "vscode-azureextensionui";
import { localize } from "../localize";
import { OperationConsole } from "../operationConsole/OperationConsole";
import { nonNullProp } from "../utils/nonNull";
import { treeUtils } from "../utils/treeUtils";
import { IApiTreeRoot } from "./IApiTreeRoot";
import { IOperationTreeRoot } from "./IOperationTreeRoot";
import { OperationPolicyTreeItem } from "./OperationPolicyTreeItem";
export class ApiOperationTreeItem extends AzureParentTreeItem<IOperationTreeRoot> {
public static contextValue: string = 'azureApiManagementApiOperation';
public contextValue: string = ApiOperationTreeItem.contextValue;
public readonly commandId: string = 'azureApiManagement.showArmApiOperation';
public readonly policyTreeItem: OperationPolicyTreeItem;
private _name: string;
private _label: string;
public get root(): IOperationTreeRoot {
return this._root;
}
public get iconPath(): { light: string, dark: string } {
return treeUtils.getThemedIconPath('op');
}
public get label() : string {
return this._label;
}
public get id(): string {
return this._name;
}
private _root: IOperationTreeRoot;
constructor(
parent: AzureParentTreeItem,
public readonly operationContract: ApiManagementModels.OperationContract) {
super(parent);
this._root = this.createRoot(parent.root);
this.policyTreeItem = new OperationPolicyTreeItem(this);
this._label = `[${nonNullProp(this.operationContract, 'method')}] ${nonNullProp(this.operationContract, 'displayName')}`;
this._name = nonNullProp(this.operationContract, 'name');
}
public async loadMoreChildrenImpl(): Promise<AzureTreeItem<IOperationTreeRoot>[]> {
return [this.policyTreeItem];
}
public hasMoreChildrenImpl(): boolean {
return false;
}
public async deleteTreeItemImpl() : Promise<void> {
const message: string = localize("confirmDeleteOperation", `Are you sure you want to delete operation '${this.root.opName}'?`);
const result = await window.showWarningMessage(message, { modal: true }, DialogResponses.deleteResponse, DialogResponses.cancel);
if (result === DialogResponses.deleteResponse) {
const deletingMessage: string = `Deleting operation "${this.root.opName}"...`;
await window.withProgress({ location: ProgressLocation.Notification, title: deletingMessage }, async () => {
await this.root.client.apiOperation.deleteMethod(this.root.resourceGroupName, this.root.serviceName, this.root.apiName, this.root.opName, '*');
});
// don't wait
window.showInformationMessage(localize("deletedOperation", `Successfully deleted API "${this.root.apiName}".`));
} else {
throw new UserCancelledError();
}
}
public async getOperationTestInfo(): Promise<string> {
return await new OperationConsole().buildRequestInfo(this.root);
}
private createRoot(subRoot: ISubscriptionRoot): IOperationTreeRoot {
return Object.assign({}, <IApiTreeRoot>subRoot, {
opName : nonNullProp(this.operationContract, 'name')
});
}
}

Просмотреть файл

@ -0,0 +1,49 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ApiManagementModels } from "azure-arm-apimanagement";
import { AzureParentTreeItem, AzureTreeItem, createTreeItemsWithErrorHandling } from "vscode-azureextensionui";
import { topItemCount } from "../constants";
import { localize } from "../localize";
import { treeUtils } from "../utils/treeUtils";
import { ApiOperationTreeItem } from "./ApiOperationTreeItem";
import { IApiTreeRoot } from "./IApiTreeRoot";
export class ApiOperationsTreeItem extends AzureParentTreeItem<IApiTreeRoot> {
public get iconPath(): { light: string, dark: string } {
return treeUtils.getThemedIconPath('list');
}
public static contextValue: string = 'azureApiManagementOperations';
public label: string = "Operations";
public contextValue: string = ApiOperationsTreeItem.contextValue;
public readonly childTypeLabel: string = localize('azureApiManagement.Operation', 'Operation');
private _nextLink: string | undefined;
public hasMoreChildrenImpl(): boolean {
return this._nextLink !== undefined;
}
public async loadMoreChildrenImpl(clearCache: boolean): Promise<AzureTreeItem<IApiTreeRoot>[]> {
if (clearCache) {
this._nextLink = undefined;
}
const operationCollection: ApiManagementModels.OperationCollection = this._nextLink === undefined ?
await this.root.client.apiOperation.listByApi(this.root.resourceGroupName, this.root.serviceName, this.root.apiName, {top: topItemCount}) :
await this.root.client.apiOperation.listByApiNext(this._nextLink);
this._nextLink = operationCollection.nextLink;
return createTreeItemsWithErrorHandling(
this,
operationCollection,
"invalidApiManagementApiOperation",
async (op: ApiManagementModels.OperationContract) => new ApiOperationTreeItem(this, op),
(op: ApiManagementModels.OperationContract) => {
return op.name;
});
}
}

Просмотреть файл

@ -0,0 +1,19 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzureTreeItem } from "vscode-azureextensionui";
import { treeUtils } from "../utils/treeUtils";
import { IApiTreeRoot } from "./IApiTreeRoot";
export class ApiPolicyTreeItem extends AzureTreeItem<IApiTreeRoot> {
public get iconPath(): { light: string, dark: string } {
return treeUtils.getThemedIconPath('policy');
}
public static contextValue: string = 'azureApiManagementApiPolicy';
public label: string = "Policy";
public contextValue: string = ApiPolicyTreeItem.contextValue;
public readonly commandId: string = 'azureApiManagement.showApiPolicy';
}

101
src/explorer/ApiTreeItem.ts Normal file
Просмотреть файл

@ -0,0 +1,101 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ApiManagementModels } from "azure-arm-apimanagement";
import { ProgressLocation, window } from "vscode";
import { AzureParentTreeItem, AzureTreeItem, DialogResponses, ISubscriptionRoot, UserCancelledError } from "vscode-azureextensionui";
import { localize } from "../localize";
import { nonNullProp } from "../utils/nonNull";
import { treeUtils } from "../utils/treeUtils";
import { ApiOperationsTreeItem } from "./ApiOperationsTreeItem";
import { ApiOperationTreeItem } from "./ApiOperationTreeItem";
import { ApiPolicyTreeItem } from "./ApiPolicyTreeItem";
import { IApiTreeRoot } from "./IApiTreeRoot";
import { IServiceTreeRoot } from "./IServiceTreeRoot";
import { OperationPolicyTreeItem } from "./OperationPolicyTreeItem";
export class ApiTreeItem extends AzureParentTreeItem<IApiTreeRoot> {
public static contextValue: string = 'azureApiManagementApi';
public contextValue: string = ApiTreeItem.contextValue;
public readonly commandId: string = 'azureApiManagement.showArmApi';
public readonly policyTreeItem: ApiPolicyTreeItem;
private _name: string;
private _label: string;
private _root: IApiTreeRoot;
private readonly _operationsTreeItem: ApiOperationsTreeItem;
constructor(
parent: AzureParentTreeItem,
public readonly apiContract: ApiManagementModels.ApiContract,
apiVersion?: string) {
super(parent);
if (!apiVersion) {
this._label = nonNullProp(this.apiContract, 'displayName');
} else {
this._label = apiVersion;
}
this._name = nonNullProp(this.apiContract, 'name');
this._root = this.createRoot(parent.root);
this._operationsTreeItem = new ApiOperationsTreeItem(this);
this.policyTreeItem = new ApiPolicyTreeItem(this);
}
public get id(): string {
return this._name;
}
public get label() : string {
return this._label;
}
public get root(): IApiTreeRoot {
return this._root;
}
public get iconPath(): { light: string, dark: string } {
return treeUtils.getThemedIconPath('api');
}
public async loadMoreChildrenImpl(): Promise<AzureTreeItem<IApiTreeRoot>[]> {
return [this._operationsTreeItem, this.policyTreeItem];
}
public hasMoreChildrenImpl(): boolean {
return false;
}
public async deleteTreeItemImpl(): Promise<void> {
const message: string = localize("confirmDeleteApi", `Are you sure you want to delete API '${this.root.apiName}' and its contents?`);
const result = await window.showWarningMessage(message, { modal: true }, DialogResponses.deleteResponse, DialogResponses.cancel);
if (result === DialogResponses.deleteResponse) {
const deletingMessage: string = localize("deletingApi", `Deleting API "${this.root.apiName}"...`);
await window.withProgress({ location: ProgressLocation.Notification, title: deletingMessage }, async () => {
await this.root.client.api.deleteMethod(this.root.resourceGroupName, this.root.serviceName, this.root.apiName, '*');
});
// don't wait
window.showInformationMessage(localize("deletedApi", `Successfully deleted API "${this.root.apiName}".`));
} else {
throw new UserCancelledError();
}
}
public pickTreeItemImpl(expectedContextValue: string | RegExp): AzureTreeItem<IApiTreeRoot> | undefined {
if (expectedContextValue === OperationPolicyTreeItem.contextValue
|| expectedContextValue === ApiOperationTreeItem.contextValue ) {
return this._operationsTreeItem;
}
return undefined;
}
private createRoot(subRoot: ISubscriptionRoot): IApiTreeRoot {
return Object.assign({}, <IServiceTreeRoot>subRoot, {
apiName: nonNullProp(this.apiContract, 'name')
});
}
}

Просмотреть файл

@ -0,0 +1,54 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ApiContract, ApiVersionSetContractDetails } from "azure-arm-apimanagement/lib/models";
import { AzureParentTreeItem, AzureTreeItem } from "vscode-azureextensionui";
import { nonNullProp, nonNullValue } from "../utils/nonNull";
import { treeUtils } from "../utils/treeUtils";
import { ApiTreeItem } from "./ApiTreeItem";
import { IServiceTreeRoot } from "./IServiceTreeRoot";
export class ApiVersionSetTreeItem extends AzureParentTreeItem<IServiceTreeRoot> {
public static contextValue: string = 'azureApiManagementApiVersionSet';
public contextValue: string = ApiVersionSetTreeItem.contextValue;
private _apis: ApiContract[] = [];
private _apiVersionSet: ApiVersionSetContractDetails;
constructor(
parent: AzureParentTreeItem,
public readonly initialApi: ApiContract) {
super(parent);
this._apis.push(initialApi);
this._apiVersionSet = nonNullValue(initialApi, "apiVersionSet");
this.id = `${this._apiVersionSet.id}-vs`;
}
public get label() : string {
return nonNullProp(this._apiVersionSet, "name");
}
public get description() : string {
return 'Version Set';
}
public hasMoreChildrenImpl(): boolean {
return false;
}
public get iconPath(): { light: string, dark: string } {
return treeUtils.getThemedIconPath('list');
}
public async loadMoreChildrenImpl(): Promise<AzureTreeItem<IServiceTreeRoot>[]> {
const apis = this._apis.map(async (api) => new ApiTreeItem(this, api, api.apiVersion ? api.apiVersion : "Original"));
return Promise.all(apis);
}
public addApiToSet(api: ApiContract) : void {
this._apis.push(api);
}
}

Просмотреть файл

@ -0,0 +1,109 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ApiManagementModels } from "azure-arm-apimanagement";
import { window } from "vscode";
import { AzureParentTreeItem, AzureTreeItem, createTreeItemsWithErrorHandling, DialogResponses, IParsedError, parseError, UserCancelledError } from "vscode-azureextensionui";
import { topItemCount } from "../constants";
import { localize } from "../localize";
import { IOpenApiImportObject } from "../openApi/OpenApiImportObject";
import { processError } from "../utils/errorUtil";
import { treeUtils } from "../utils/treeUtils";
import { ApiTreeItem } from "./ApiTreeItem";
import { ApiVersionSetTreeItem } from "./ApiVersionSetTreeItem";
import { IServiceTreeRoot } from "./IServiceTreeRoot";
export class ApisTreeItem extends AzureParentTreeItem<IServiceTreeRoot> {
public get iconPath(): { light: string, dark: string } {
return treeUtils.getThemedIconPath('list');
}
public static contextValue: string = 'azureApiManagementApis';
public label: string = "APIs";
public contextValue: string = ApisTreeItem.contextValue;
public readonly childTypeLabel: string = localize('azureApiManagement.Api', 'API');
private _nextLink: string | undefined;
public hasMoreChildrenImpl(): boolean {
return this._nextLink !== undefined;
}
public async loadMoreChildrenImpl(clearCache: boolean): Promise<AzureTreeItem<IServiceTreeRoot>[]> {
if (clearCache) {
this._nextLink = undefined;
}
const apiCollection: ApiManagementModels.ApiCollection = this._nextLink === undefined ?
await this.root.client.api.listByService(this.root.resourceGroupName, this.root.serviceName, { expandApiVersionSet: true, top: topItemCount }) :
await this.root.client.api.listByServiceNext(this._nextLink);
this._nextLink = apiCollection.nextLink;
const versionSetMap: Map<string, ApiVersionSetTreeItem> = new Map<string, ApiVersionSetTreeItem>();
return await createTreeItemsWithErrorHandling(
this,
apiCollection,
"invalidApiManagementApi",
async (api: ApiManagementModels.ApiContract) => {
if (api.apiVersionSetId && api.apiVersionSet) {
let apiVersionSetTreeItem = versionSetMap.get(api.apiVersionSetId);
if (!apiVersionSetTreeItem) {
apiVersionSetTreeItem = new ApiVersionSetTreeItem(this, api);
versionSetMap.set(api.apiVersionSetId, apiVersionSetTreeItem);
return apiVersionSetTreeItem;
} else {
apiVersionSetTreeItem.addApiToSet(api);
return undefined;
}
} else {
return new ApiTreeItem(this, api);
}
},
(api: ApiManagementModels.ApiContract) => {
return api.name;
});
}
public async createChildImpl(showCreatingTreeItem: (label: string) => void, userOptions?: { apiName: string, document?: IOpenApiImportObject }): Promise<ApiTreeItem> {
if (userOptions && userOptions.document && userOptions.apiName) {
const apiName = userOptions.apiName;
showCreatingTreeItem(apiName);
try {
userOptions.document.info.title = apiName;
let apiExists: boolean = true;
try {
await this.root.client.api.get(this.root.resourceGroupName, this.root.serviceName, apiName);
} catch (error) {
const err: IParsedError = parseError(error);
if (err.errorType.toLocaleLowerCase() === 'notfound' || err.errorType.toLowerCase() === 'resourcenotfound') {
apiExists = false;
}
}
if (apiExists) {
const overwriteFlag = await window.showWarningMessage(localize("apiAlreadyExists", `API "${apiName}" already exists. Import will trigger an 'Override' of exisiting API. Do you want to continue?`), { modal: true }, DialogResponses.yes, DialogResponses.cancel);
if (overwriteFlag !== DialogResponses.yes) {
throw new UserCancelledError();
}
}
const openApiImportPayload: ApiManagementModels.ApiCreateOrUpdateParameter = { displayName: apiName, path: apiName, format: '', value: '' };
openApiImportPayload.protocols = userOptions.document.schemes === undefined ? ["https"] : userOptions.document.schemes;
openApiImportPayload.format = userOptions.document.importFormat;
openApiImportPayload.value = JSON.stringify(userOptions.document.sourceDocument);
const options = { ifMatch: "*" };
const api = await this.root.client.api.createOrUpdate(this.root.resourceGroupName, this.root.serviceName, apiName, openApiImportPayload, options);
return new ApiTreeItem(this, api);
} catch (error) {
throw new Error(processError(error, localize("createAPIFailed", `Failed to create the API ${userOptions.apiName}`)));
}
} else {
throw Error("Expected either OpenAPI document or link.");
}
}
}

Просмотреть файл

@ -0,0 +1,10 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IServiceTreeRoot } from "./IServiceTreeRoot";
export interface IApiTreeRoot extends IServiceTreeRoot {
apiName: string;
}

Просмотреть файл

@ -0,0 +1,10 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IApiTreeRoot } from "./IApiTreeRoot";
export interface IOperationTreeRoot extends IApiTreeRoot {
opName: string;
}

Просмотреть файл

@ -0,0 +1,12 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import ApiManagementClient from "azure-arm-apimanagement";
import { ISubscriptionRoot } from "vscode-azureextensionui";
export interface IServiceTreeRoot extends ISubscriptionRoot {
client: ApiManagementClient;
resourceGroupName: string;
serviceName: string;
}

Просмотреть файл

@ -0,0 +1,19 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzureTreeItem } from "vscode-azureextensionui";
import { treeUtils } from "../utils/treeUtils";
import { IOperationTreeRoot } from "./IOperationTreeRoot";
export class OperationPolicyTreeItem extends AzureTreeItem<IOperationTreeRoot> {
public get iconPath(): { light: string, dark: string } {
return treeUtils.getThemedIconPath('policy');
}
public static contextValue: string = 'azureApiManagementOperationPolicy';
public label: string = "Policy";
public contextValue: string = OperationPolicyTreeItem.contextValue;
public readonly commandId: string = 'azureApiManagement.showOperationPolicy';
}

Просмотреть файл

@ -0,0 +1,19 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { AzureTreeItem } from "vscode-azureextensionui";
import { treeUtils } from "../utils/treeUtils";
import { IServiceTreeRoot } from "./IServiceTreeRoot";
export class ServicePolicyTreeItem extends AzureTreeItem<IServiceTreeRoot> {
public get iconPath(): { light: string, dark: string } {
return treeUtils.getThemedIconPath('policy');
}
public static contextValue: string = 'azureApiManagementServicePolicy';
public label: string = "Policy";
public contextValue: string = ServicePolicyTreeItem.contextValue;
public readonly commandId: string = 'azureApiManagement.showServicePolicy';
}

Просмотреть файл

@ -0,0 +1,99 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ApiManagementClient, ApiManagementModels } from "azure-arm-apimanagement";
import { ProgressLocation, window } from "vscode";
import { AzureParentTreeItem, AzureTreeItem, DialogResponses, ISubscriptionRoot, UserCancelledError } from "vscode-azureextensionui";
import { localize } from "../localize";
import { getResourceGroupFromId } from "../utils/azure";
import { nonNullProp } from "../utils/nonNull";
import { treeUtils } from '../utils/treeUtils';
import { ApiOperationTreeItem } from "./ApiOperationTreeItem";
import { ApiPolicyTreeItem } from "./ApiPolicyTreeItem";
import { ApisTreeItem } from "./ApisTreeItem";
import { ApiTreeItem } from "./ApiTreeItem";
import { IServiceTreeRoot } from "./IServiceTreeRoot";
import { OperationPolicyTreeItem } from "./OperationPolicyTreeItem";
import { ServicePolicyTreeItem } from "./ServicePolicyTreeItem";
export class ServiceTreeItem extends AzureParentTreeItem<IServiceTreeRoot> {
public get root(): IServiceTreeRoot {
return this._root;
}
public get iconPath(): { light: string, dark: string } {
return treeUtils.getThemedIconPath('apim');
}
public get id(): string {
return nonNullProp(this.apiManagementService, 'id');
}
public static contextValue: string = 'azureApiManagementService';
public label: string = nonNullProp(this.apiManagementService, 'name');
public contextValue: string = ServiceTreeItem.contextValue;
public readonly apisTreeItem: ApisTreeItem;
public readonly servicePolicyTreeItem: ServicePolicyTreeItem;
private _root: IServiceTreeRoot;
constructor(
parent: AzureParentTreeItem,
public readonly apiManagementClient: ApiManagementClient,
public readonly apiManagementService: ApiManagementModels.ApiManagementServiceResource) {
super(parent);
this._root = this.createRoot(parent.root, apiManagementClient);
this.servicePolicyTreeItem = new ServicePolicyTreeItem(this);
this.apisTreeItem = new ApisTreeItem(this);
}
public async loadMoreChildrenImpl(): Promise<AzureTreeItem<IServiceTreeRoot>[]> {
return [this.apisTreeItem, this.servicePolicyTreeItem];
}
public hasMoreChildrenImpl(): boolean {
return false;
}
public async deleteTreeItemImpl() : Promise<void> {
const message: string = localize("confirmDeleteService", `Are you sure you want to delete API Management instance '${this.root.serviceName}' and its contents?`);
const result = await window.showWarningMessage(message, { modal: true }, DialogResponses.deleteResponse, DialogResponses.cancel);
if (result === DialogResponses.deleteResponse) {
const deletingMessage: string = localize("deletingService", `Deleting API Management instance "${this.root.serviceName}"...`);
await window.withProgress({ location: ProgressLocation.Notification, title: deletingMessage }, async () => {
await this.root.client.apiManagementService.deleteMethod(this.root.resourceGroupName, this.root.serviceName);
});
// don't wait
window.showInformationMessage(localize("deletedService", `Successfully deleted API Management instance "${this.root.serviceName}".`));
} else {
throw new UserCancelledError();
}
}
public async copySubscriptionKey(): Promise<string> {
const subscription = await this.root.client.subscription.get(this.root.resourceGroupName, this.root.serviceName, "master");
return subscription.secondaryKey;
}
public pickTreeItemImpl(expectedContextValue: string | RegExp): AzureTreeItem<IServiceTreeRoot> | undefined {
if (expectedContextValue === ApiTreeItem.contextValue
|| expectedContextValue === ApiPolicyTreeItem.contextValue
|| expectedContextValue === ApiOperationTreeItem.contextValue
|| expectedContextValue === OperationPolicyTreeItem.contextValue) {
return this.apisTreeItem;
}
return undefined;
}
private createRoot(subRoot: ISubscriptionRoot, client: ApiManagementClient): IServiceTreeRoot {
return Object.assign({}, subRoot, {
client: client,
serviceName : nonNullProp(this.apiManagementService, 'name'),
resourceGroupName: getResourceGroupFromId(nonNullProp(this.apiManagementService, 'id'))
});
}
}

Просмотреть файл

@ -0,0 +1,112 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as fse from 'fs-extra';
import * as path from 'path';
import * as vscode from 'vscode';
import { DialogResponses, IActionContext, UserCancelledError } from 'vscode-azureextensionui';
import { ext } from '../../extensionVariables';
import { localize } from "../../localize";
import { createTemporaryFile } from '../../utils/fsUtil';
import { writeToEditor } from '../../utils/vscodeUtils';
// tslint:disable-next-line:no-unsafe-any
export abstract class Editor<ContextT> implements vscode.Disposable {
private fileMap: { [key: string]: [vscode.TextDocument, ContextT] } = {};
private ignoreSave: boolean = false;
constructor(private readonly showSavePromptKey: string) {
}
public abstract getData(context: ContextT): Promise<string>;
public abstract updateData(context: ContextT, data: string): Promise<string>;
public abstract getFilename(context: ContextT): Promise<string>;
public abstract getSaveConfirmationText(context: ContextT): Promise<string>;
public abstract getSize(context: ContextT): Promise<number>;
public async showEditor(context: ContextT, sizeLimit?: number /* in Megabytes */): Promise<void> {
const fileName: string = await this.getFilename(context);
this.appendLineToOutput(localize('opening', 'Opening "{0}"...', fileName));
if (sizeLimit !== undefined) {
const size: number = await this.getSize(context);
if (size > sizeLimit) {
const message: string = localize('tooLargeError', '"{0}" is too large to download.', fileName);
throw new Error(message);
}
}
const localFilePath: string = await createTemporaryFile(fileName);
const document: vscode.TextDocument = await vscode.workspace.openTextDocument(localFilePath);
if (document.isDirty) {
const overwriteFlag = await vscode.window.showWarningMessage(`You are about to overwrite "${fileName}", which has unsaved changes. Do you want to continue?`, { modal: true }, DialogResponses.yes, DialogResponses.cancel);
if (overwriteFlag !== DialogResponses.yes) {
throw new UserCancelledError();
}
}
this.fileMap[localFilePath] = [document, context];
const data: string = await this.getData(context);
const textEditor: vscode.TextEditor = await vscode.window.showTextDocument(document);
await this.updateEditor(data, textEditor);
}
public async updateMatchingContext(doc: vscode.Uri): Promise<void> {
const filePath: string | undefined = Object.keys(this.fileMap).find((fsPath: string) => path.relative(doc.fsPath, fsPath) === '');
if (filePath) {
const [textDocument, context]: [vscode.TextDocument, ContextT] = this.fileMap[filePath];
await this.updateRemote(context, textDocument);
}
}
public async dispose(): Promise<void> {
Object.keys(this.fileMap).forEach(async (key: string) => await fse.remove(path.dirname(key)));
}
public async onDidSaveTextDocument(actionContext: IActionContext, globalState: vscode.Memento, doc: vscode.TextDocument): Promise<void> {
actionContext.suppressTelemetry = true;
const filePath: string | undefined = Object.keys(this.fileMap).find((fsPath: string) => path.relative(doc.uri.fsPath, fsPath) === '');
if (!this.ignoreSave && filePath) {
actionContext.suppressTelemetry = false;
const context: ContextT = this.fileMap[filePath][1];
const showSaveWarning: boolean | undefined = vscode.workspace.getConfiguration().get(this.showSavePromptKey);
if (showSaveWarning) {
const message: string = await this.getSaveConfirmationText(context);
const result: vscode.MessageItem | undefined = await vscode.window.showWarningMessage(message, DialogResponses.upload, DialogResponses.alwaysUpload, DialogResponses.dontUpload);
if (result === DialogResponses.alwaysUpload) {
await vscode.workspace.getConfiguration().update(this.showSavePromptKey, false, vscode.ConfigurationTarget.Global);
await globalState.update(this.showSavePromptKey, true);
} else if (result === DialogResponses.dontUpload) {
throw new UserCancelledError();
}
}
await this.updateRemote(context, doc);
}
}
protected appendLineToOutput(value: string): void {
ext.outputChannel.appendLine(value);
ext.outputChannel.show(true);
}
private async updateRemote(context: ContextT, doc: vscode.TextDocument): Promise<void> {
const filename: string = await this.getFilename(context);
this.appendLineToOutput(localize('updating', 'Updating "{0}" ...', filename));
const updatedData: string = await this.updateData(context, doc.getText());
this.appendLineToOutput(localize('done', 'Updated "{0}".', filename));
await this.updateEditor(updatedData, vscode.window.activeTextEditor);
}
private async updateEditor(data: string, textEditor?: vscode.TextEditor): Promise<void> {
if (!!textEditor) {
await writeToEditor(textEditor, data);
this.ignoreSave = true;
try {
await textEditor.document.save();
} finally {
this.ignoreSave = false;
}
}
}
}

Просмотреть файл

@ -0,0 +1,25 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ApiManagementModels } from "azure-arm-apimanagement";
import { AzureTreeItem } from "vscode-azureextensionui";
import { IApiTreeRoot } from "../../IApiTreeRoot";
import { BaseArmResourceEditor } from "./BaseArmResourceEditor";
// tslint:disable-next-line:no-any
export class ApiResourceEditor extends BaseArmResourceEditor<IApiTreeRoot> {
public entityType: string = "API";
constructor() {
super();
}
public async getDataInternal(context: AzureTreeItem<IApiTreeRoot>): Promise<ApiManagementModels.ApiContract> {
return await context.root.client.api.get(context.root.resourceGroupName, context.root.serviceName, context.root.apiName);
}
public async updateDataInternal(context: AzureTreeItem<IApiTreeRoot>, payload: ApiManagementModels.ApiCreateOrUpdateParameter): Promise<ApiManagementModels.ApiContract> {
return await context.root.client.api.createOrUpdate(context.root.resourceGroupName, context.root.serviceName, context.root.apiName, payload);
}
}

Просмотреть файл

@ -0,0 +1,58 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { window } from "vscode";
import { AzureTreeItem, parseError } from "vscode-azureextensionui";
import { showSavePromptConfigKey } from "../../../constants";
import { localize } from "../../../localize";
import { processError } from "../../../utils/errorUtil";
import { nameUtil } from "../../../utils/nameUtil";
import { IServiceTreeRoot } from "../../IServiceTreeRoot";
import { Editor } from "../Editor";
// tslint:disable:no-any
export abstract class BaseArmResourceEditor<TRoot extends IServiceTreeRoot> extends Editor<AzureTreeItem<TRoot>> {
constructor() {
super(showSavePromptConfigKey);
}
public abstract get entityType(): string;
public abstract getDataInternal(context: AzureTreeItem<TRoot>): Promise<any>;
public abstract updateDataInternal(context: AzureTreeItem<TRoot>, payload: any): Promise<any>;
public async getData(context: AzureTreeItem<TRoot>): Promise<string> {
try {
const response = await this.getDataInternal(context);
return JSON.stringify(response, null, "\t");
} catch (error) {
throw new Error(`${parseError(error).message}`);
}
}
// tslint:disable: no-unsafe-any
public async updateData(context: AzureTreeItem<TRoot>, data: string): Promise<string> {
try {
const payload = JSON.parse(data);
const response = await this.updateDataInternal(context, payload);
await context.refresh();
window.showInformationMessage(localize("updateSucceded", `Changes to ${this.entityType} were succefully uploaded to cloud.`));
return JSON.stringify(response, null, "\t");
} catch (error) {
throw new Error(processError(error, localize("updateFailed", `Changes to ${this.entityType} could not be uploaded to cloud.`)));
}
}
public async getFilename(context: AzureTreeItem<TRoot>): Promise<string> {
return `${nameUtil(context.root)}-${this.entityType.toLowerCase()}-arm.json`;
}
public async getSize(): Promise<number> {
throw new Error("Method not implemented.");
}
public async getSaveConfirmationText(): Promise<string> {
return localize("saveConfirmation", "Do you want to upload the azure resource changes to cloud?");
}
}

Просмотреть файл

@ -0,0 +1,25 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ApiManagementModels } from "azure-arm-apimanagement";
import { AzureTreeItem } from "vscode-azureextensionui";
import { IOperationTreeRoot } from "../../IOperationTreeRoot";
import { BaseArmResourceEditor } from "./BaseArmResourceEditor";
// tslint:disable-next-line:no-any
export class OperationResourceEditor extends BaseArmResourceEditor<IOperationTreeRoot> {
public entityType: string = "Operation";
constructor() {
super();
}
public async getDataInternal(context: AzureTreeItem<IOperationTreeRoot>): Promise<ApiManagementModels.OperationContract> {
return await context.root.client.apiOperation.get(context.root.resourceGroupName, context.root.serviceName, context.root.apiName, context.root.opName);
}
public async updateDataInternal(context: AzureTreeItem<IOperationTreeRoot>, payload: ApiManagementModels.OperationContract): Promise<ApiManagementModels.OperationContract> {
return await context.root.client.apiOperation.createOrUpdate(context.root.resourceGroupName, context.root.serviceName, context.root.apiName, context.root.opName, payload);
}
}

Просмотреть файл

@ -0,0 +1,138 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ApiManagementModels } from "azure-arm-apimanagement";
import { WebResource } from "ms-rest";
import * as request from 'request-promise';
import { ProgressLocation, window } from "vscode";
import { appendExtensionUserAgent } from "vscode-azureextensionui";
import { openApiAcceptHeader, openApiExport, openApiSchema, showSavePromptConfigKey, swaggerAcceptHeader, swaggerExport, swaggerSchema } from "../../../constants";
import { localize } from "../../../localize";
import { IOpenApiImportObject } from "../../../openApi/OpenApiImportObject";
import { OpenApiParser } from "../../../openApi/OpenApiParser";
import { processError } from "../../../utils/errorUtil";
import { nonNullProp } from "../../../utils/nonNull";
import { signRequest } from "../../../utils/signRequest";
import { ApiTreeItem } from "../../ApiTreeItem";
import { Editor } from "../Editor";
export class OpenApiEditor extends Editor<ApiTreeItem> {
constructor() {
super(showSavePromptConfigKey);
}
public async getData(context: ApiTreeItem): Promise<string> {
try {
// Check the supported schemas for API. If no schemas specified then assume open api 3.0
const schemas = await context.root.client.apiSchema.listByApi(context.root.resourceGroupName, context.root.serviceName, context.root.apiName);
let exportFormat: string = openApiExport;
let exportAcceptHeader: string = openApiAcceptHeader;
if (schemas.length > 0) {
const openApiSchemaSupported = schemas.find((s) => s.contentType === openApiSchema);
if (openApiSchemaSupported === undefined) {
const swaggerSchemaSupported = schemas.find((s) => s.contentType === swaggerSchema);
if (swaggerSchemaSupported !== undefined) {
exportFormat = swaggerExport;
exportAcceptHeader = swaggerAcceptHeader;
} else {
throw Error(localize("unSupportedSchema", `'${context.root.apiName}' does not support OpenAPI 2.0 or OpenAPI 3.0 schema.`));
}
}
}
const responseDocument = await this.requestOpenAPIDocument(context, exportFormat, exportAcceptHeader);
const sourceDocument = await this.processDocument(context, responseDocument);
return JSON.stringify(sourceDocument, null, "\t");
} catch (error) {
throw new Error(processError(error, localize("getOpenAPIDocumentFailed", `Failed to retriev OpenAPI document for API ${context.root.apiName}.`)));
}
}
// tslint:disable: no-unsafe-any
public async updateData(context: ApiTreeItem, data: string): Promise<string> {
let openApiDocument: IOpenApiImportObject | undefined;
try {
const documentJson = JSON.parse(data);
const openApiparser = new OpenApiParser();
openApiDocument = await openApiparser.parse(documentJson);
openApiparser.updateBackend(openApiDocument.sourceDocument, nonNullProp(context.apiContract, 'serviceUrl'));
const swaggerJson = JSON.stringify(openApiDocument.sourceDocument);
const payload: ApiManagementModels.ApiCreateOrUpdateParameter = {
format: openApiDocument.importFormat,
value: swaggerJson,
path: context.apiContract.path
};
return window.withProgress(
{
location: ProgressLocation.Notification,
title: localize("updatingAPI", `Applying changes to API '${context.root.apiName}' in API Management instance ${context.root.serviceName}...`),
cancellable: false
},
async () => context.root.client.api.createOrUpdate(context.root.resourceGroupName, context.root.serviceName, context.root.apiName, payload)
).then(async () => {
window.showInformationMessage(localize("updateOpenApiSucceded", `Changes to API '${context.apiContract.name}' were succefully uploaded to cloud.`));
await context.refresh();
return this.getData(context);
});
} catch (error) {
throw new Error(processError(error, localize("updateOpenApiFailed", `Changes to the OpenAPI document could not be uploaded to cloud.`)));
}
}
public async getFilename(context: ApiTreeItem): Promise<string> {
return `${context.root.serviceName}-${context.root.apiName}-openapi.json`;
}
public async getSize(): Promise<number> {
throw new Error("Method not implemented.");
}
public async getSaveConfirmationText(context: ApiTreeItem): Promise<string> {
return `Saving will update the API '${context.apiContract.name}'.`;
}
// tslint:disable-next-line:no-any
private async requestOpenAPIDocument(context: ApiTreeItem, exportFormat: string, exportAcceptHeader: string) : Promise<any> {
const requestOptions: WebResource = new WebResource();
requestOptions.headers = {
['Accept']: exportAcceptHeader,
['User-Agent']: appendExtensionUserAgent()
};
requestOptions.url = this.buildAPIExportUrl(context, exportFormat);
await signRequest(requestOptions, context.root.client.credentials);
// tslint:disable-next-line: await-promise
return await request(requestOptions).promise();
}
private buildAPIExportUrl(context: ApiTreeItem, exportFormat: string) : string {
let url = `${context.root.environment.resourceManagerEndpointUrl}/subscriptions/${context.root.subscriptionId}/resourceGroups/${context.root.resourceGroupName}/providers/Microsoft.ApiManagement/service/${context.root.serviceName}/apis/${context.root.apiName}`;
url = `${url}?export=true&format=${exportFormat}&api-version=2019-01-01`;
return url;
}
// tslint:disable-next-line:no-any
private async processDocument(context: ApiTreeItem, swaggerDocument: any) : Promise<any> {
const openApiparser = new OpenApiParser();
// tslint:disable-next-line: no-unsafe-any
const importDocument = await openApiparser.parse(JSON.parse(swaggerDocument));
const sourceDocument = importDocument.sourceDocument;
let basePath: string = importDocument.basePath !== undefined ? importDocument.basePath : "";
if (context.apiContract.apiVersionSet && context.apiContract.apiVersionSet.versioningScheme === "Segment") {
const versionSegment = `/${context.apiContract.apiVersion}`;
if (basePath.endsWith(versionSegment)) {
// tslint:disable-next-line: no-unsafe-any
basePath = sourceDocument.basePath.replace(versionSegment, "");
}
}
const service = await context.root.client.apiManagementService.get(context.root.resourceGroupName, context.root.serviceName);
// tslint:disable-next-line: no-unsafe-any
openApiparser.updateBasePath(sourceDocument, basePath, nonNullProp(service, "gatewayUrl"));
return sourceDocument;
}
}

Просмотреть файл

@ -0,0 +1,26 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ApiManagementModels } from "azure-arm-apimanagement";
import { AzureTreeItem } from "vscode-azureextensionui";
import { emptyPolicyXml, policyFormat } from "../../../constants";
import { IApiTreeRoot } from "../../IApiTreeRoot";
import { BasePolicyEditor } from "./BasePolicyEditor";
export class ApiPolicyEditor extends BasePolicyEditor<IApiTreeRoot> {
public async getPolicy(context: AzureTreeItem<IApiTreeRoot>): Promise<string> {
const policy = await context.root.client.apiPolicy.get(context.root.resourceGroupName, context.root.serviceName, context.root.apiName, { format: policyFormat });
return policy.value;
}
public async updatePolicy(context: AzureTreeItem<IApiTreeRoot>, policy: ApiManagementModels.PolicyContract): Promise<string> {
const policyResult = await context.root.client.apiPolicy.createOrUpdate(context.root.resourceGroupName, context.root.serviceName, context.root.apiName, policy);
return policyResult.value;
}
public getDefaultPolicy() : string {
return emptyPolicyXml;
}
}

Просмотреть файл

@ -0,0 +1,68 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ApiManagementModels } from "azure-arm-apimanagement";
import { window } from "vscode";
import { AzureTreeItem, IParsedError, parseError } from "vscode-azureextensionui";
import { policyFormat, showSavePromptConfigKey } from "../../../constants";
import { ext } from "../../../extensionVariables";
import { localize } from "../../../localize";
import { errorUtil, processError } from "../../../utils/errorUtil";
import { nameUtil } from "../../../utils/nameUtil";
import { IServiceTreeRoot } from "../../IServiceTreeRoot";
import { Editor } from "../Editor";
export abstract class BasePolicyEditor<TRoot extends IServiceTreeRoot> extends Editor<AzureTreeItem<TRoot>> {
constructor() {
super(showSavePromptConfigKey);
}
public abstract getDefaultPolicy() : string;
public abstract getPolicy(context: AzureTreeItem<TRoot>): Promise<string>;
public abstract updatePolicy(context: AzureTreeItem<TRoot>, policy: ApiManagementModels.PolicyContract): Promise<string>;
public async getData(context: AzureTreeItem<TRoot>): Promise<string> {
try {
return await this.getPolicy(context);
} catch (error) {
// tslint:disable-next-line: no-unsafe-any
ext.outputChannel.appendLine(error);
const err: IParsedError = parseError(error);
if (err.errorType.toLocaleLowerCase() === 'notfound' || err.errorType.toLowerCase() === 'resourcenotfound') {
return this.getDefaultPolicy();
} else {
let errorMessage = err.message;
if (err.errorType.toLowerCase() === 'validationerror') {
// tslint:disable-next-line: no-unsafe-any
errorMessage = errorUtil(error.response.body);
}
ext.outputChannel.appendLine(errorMessage);
ext.outputChannel.show();
throw new Error(errorMessage);
}
}
}
public async getFilename(context: AzureTreeItem<TRoot>): Promise<string> {
return `${nameUtil(context.root)}.policy.cshtml`;
}
public async updateData(context: AzureTreeItem<TRoot>, data: string): Promise<string> {
try {
await this.updatePolicy(context, <ApiManagementModels.PolicyContract>{ format: policyFormat, value: data});
window.showInformationMessage(localize("updatePolicySucceded", `Changes to policy were uploaded to cloud.`));
return await this.getPolicy(context);
} catch (error) {
throw new Error(processError(error, localize("updatePolicyFailed", `Changes to policy were not uploaded to cloud.`)));
}
}
public async getSize(): Promise<number> {
throw new Error("Method not implemented.");
}
public async getSaveConfirmationText(): Promise<string> {
return localize("saveConfirmation", "Do you want to upload changes to cloud?");
}
}

Просмотреть файл

@ -0,0 +1,26 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ApiManagementModels } from "azure-arm-apimanagement";
import { AzureTreeItem } from "vscode-azureextensionui";
import { emptyPolicyXml, policyFormat } from "../../../constants";
import { IOperationTreeRoot } from "../../IOperationTreeRoot";
import { BasePolicyEditor } from "./BasePolicyEditor";
export class OperationPolicyEditor extends BasePolicyEditor<IOperationTreeRoot> {
public async getPolicy(context: AzureTreeItem<IOperationTreeRoot>): Promise<string> {
const policy = await context.root.client.apiOperationPolicy.get(context.root.resourceGroupName, context.root.serviceName, context.root.apiName, context.root.opName, { format: policyFormat });
return policy.value;
}
public async updatePolicy(context: AzureTreeItem<IOperationTreeRoot>, policy: ApiManagementModels.PolicyContract): Promise<string> {
const policyResult = await context.root.client.apiOperationPolicy.createOrUpdate(context.root.resourceGroupName, context.root.serviceName, context.root.apiName, context.root.opName, policy);
return policyResult.value;
}
public getDefaultPolicy() : string {
return emptyPolicyXml;
}
}

Просмотреть файл

@ -0,0 +1,26 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ApiManagementModels } from "azure-arm-apimanagement";
import { AzureTreeItem } from "vscode-azureextensionui";
import { emptyGlobalPolicyXml, policyFormat } from "../../../constants";
import { IServiceTreeRoot } from "../../IServiceTreeRoot";
import { BasePolicyEditor } from "./BasePolicyEditor";
export class ServicePolicyEditor extends BasePolicyEditor<IServiceTreeRoot> {
public async getPolicy(context: AzureTreeItem<IServiceTreeRoot>): Promise<string> {
const policy = await context.root.client.policy.get(context.root.resourceGroupName, context.root.serviceName, { format: policyFormat });
return policy.value;
}
public async updatePolicy(context: AzureTreeItem<IServiceTreeRoot>, policy: ApiManagementModels.PolicyContract): Promise<string> {
const policyResult = await context.root.client.policy.createOrUpdate(context.root.resourceGroupName, context.root.serviceName, policy);
return policyResult.value;
}
public getDefaultPolicy() : string {
return emptyGlobalPolicyXml;
}
}

142
src/extension.ts Normal file
Просмотреть файл

@ -0,0 +1,142 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
import { AzureParentTreeItem, AzureTreeDataProvider, AzureTreeItem, AzureUserInput, createTelemetryReporter, IActionContext, registerCommand, registerEvent, registerUIExtensionVariables } from 'vscode-azureextensionui';
import { copySubscriptionKey } from './commands/copySubscriptionKey';
import { createService } from './commands/createService';
import { deleteNode } from './commands/deleteNode';
import { importOpenApi } from './commands/importOpenApi';
import { openInPortal } from './commands/openInPortal';
import { testOperation } from './commands/testOperation';
import { doubleClickDebounceDelay } from './constants';
import { ApiManagementProvider } from './explorer/ApiManagementProvider';
import { ApiOperationTreeItem } from './explorer/ApiOperationTreeItem';
import { ApiPolicyTreeItem } from './explorer/ApiPolicyTreeItem';
import { ApisTreeItem } from './explorer/ApisTreeItem';
import { ApiTreeItem } from './explorer/ApiTreeItem';
import { ApiResourceEditor } from './explorer/editors/arm/ApiResourceEditor';
import { OperationResourceEditor } from './explorer/editors/arm/OperationResourceEditor';
import { OpenApiEditor } from './explorer/editors/openApi/OpenApiEditor';
import { ApiPolicyEditor } from './explorer/editors/policy/ApiPolicyEditor';
import { OperationPolicyEditor } from './explorer/editors/policy/OperationPolicyEditor';
import { ServicePolicyEditor } from './explorer/editors/policy/ServicePolicyEditor';
import { OperationPolicyTreeItem } from './explorer/OperationPolicyTreeItem';
import { ServicePolicyTreeItem } from './explorer/ServicePolicyTreeItem';
import { ServiceTreeItem } from './explorer/ServiceTreeItem';
import { ext } from './extensionVariables';
// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
// tslint:disable-next-line:typedef
export function activateInternal(context: vscode.ExtensionContext) {
ext.context = context;
ext.reporter = createTelemetryReporter(context);
ext.outputChannel = vscode.window.createOutputChannel('Azure API Management');
context.subscriptions.push(ext.outputChannel);
ext.ui = new AzureUserInput(context.globalState);
registerUIExtensionVariables(ext);
const tree = new AzureTreeDataProvider(ApiManagementProvider, 'azureApiManagement.LoadMore');
ext.tree = tree;
context.subscriptions.push(tree);
context.subscriptions.push(vscode.window.registerTreeDataProvider('azureApiManagementExplorer', tree));
registerCommand('azureApiManagement.Refresh', async (node?: AzureTreeItem) => await tree.refresh(node));
registerCommand('azureApiManagement.selectSubscriptions', () => vscode.commands.executeCommand("azure-account.selectSubscriptions"));
registerCommand('azureApiManagement.LoadMore', async (node: AzureTreeItem) => await tree.loadMore(node));
registerCommand('azureApiManagement.openInPortal', async (node?: AzureTreeItem) => { await openInPortal(node); });
registerCommand('azureApiManagement.createService', createService);
registerCommand('azureApiManagement.copySubscriptionKey', copySubscriptionKey);
registerCommand('azureApiManagement.deleteService', async (node?: AzureParentTreeItem) => await deleteNode(ServiceTreeItem.contextValue, node));
registerCommand('azureApiManagement.deleteApi', async (node?: AzureTreeItem) => await deleteNode(ApiTreeItem.contextValue, node));
registerCommand('azureApiManagement.deleteOperation', async (node?: AzureTreeItem) => await deleteNode(ApiOperationTreeItem.contextValue, node));
registerCommand('azureApiManagement.testOperation', testOperation);
registerCommand('azureApiManagement.importOpenApiByFile', async (node?: ApisTreeItem) => { await importOpenApi(node, false); });
registerCommand('azureApiManagement.importOpenApiByLink', async (node?: ApisTreeItem) => { await importOpenApi(node, true); });
registerEditors(context);
}
function registerEditors(context: vscode.ExtensionContext) : void {
const apiResourceEditor: ApiResourceEditor = new ApiResourceEditor();
context.subscriptions.push(apiResourceEditor);
registerEvent('azureApiManagement.ApiResourceEditor.onDidSaveTextDocument', vscode.workspace.onDidSaveTextDocument, async function (this: IActionContext, doc: vscode.TextDocument): Promise<void> { await apiResourceEditor.onDidSaveTextDocument(this, context.globalState, doc); });
registerCommand('azureApiManagement.showArmApi', async (node?: ApiTreeItem) => {
if (!node) {
node = <ApiTreeItem>await ext.tree.showTreeItemPicker(ApiTreeItem.contextValue);
}
await apiResourceEditor.showEditor(node);
}, doubleClickDebounceDelay);
const operationResourceEditor: OperationResourceEditor = new OperationResourceEditor();
context.subscriptions.push(operationResourceEditor);
registerEvent('azureApiManagement.OperationResourceEditor.onDidSaveTextDocument', vscode.workspace.onDidSaveTextDocument, async function (this: IActionContext, doc: vscode.TextDocument): Promise<void> { await operationResourceEditor.onDidSaveTextDocument(this, context.globalState, doc); });
registerCommand('azureApiManagement.showArmApiOperation', async (node?: ApiOperationTreeItem) => {
if (!node) {
node = <ApiOperationTreeItem>await ext.tree.showTreeItemPicker(ApiOperationTreeItem.contextValue);
}
await operationResourceEditor.showEditor(node);
}, doubleClickDebounceDelay);
const apiEditor: OpenApiEditor = new OpenApiEditor();
context.subscriptions.push(apiEditor);
registerEvent('azureApiManagement.apiEditor.onDidSaveTextDocument', vscode.workspace.onDidSaveTextDocument, async function (this: IActionContext, doc: vscode.TextDocument): Promise<void> { await apiEditor.onDidSaveTextDocument(this, context.globalState, doc); });
registerCommand('azureApiManagement.showApi', async (node?: ApiTreeItem) => {
if (!node) {
node = <ApiTreeItem>await ext.tree.showTreeItemPicker(ApiTreeItem.contextValue);
}
await apiEditor.showEditor(node);
}, doubleClickDebounceDelay);
const servicePolicyEditor: ServicePolicyEditor = new ServicePolicyEditor();
context.subscriptions.push(servicePolicyEditor);
registerEvent('azureApiManagement.servicePolicyEditor.onDidSaveTextDocument', vscode.workspace.onDidSaveTextDocument, async function (this: IActionContext, doc: vscode.TextDocument): Promise<void> { await servicePolicyEditor.onDidSaveTextDocument(this, context.globalState, doc); });
registerCommand('azureApiManagement.showServicePolicy', async (node?: ServicePolicyTreeItem) => {
if (!node) {
const serviceNode = <ServiceTreeItem>await ext.tree.showTreeItemPicker(ServiceTreeItem.contextValue);
node = serviceNode.servicePolicyTreeItem;
}
await servicePolicyEditor.showEditor(node);
}, doubleClickDebounceDelay);
const apiPolicyEditor: ApiPolicyEditor = new ApiPolicyEditor();
context.subscriptions.push(apiPolicyEditor);
registerEvent('azureApiManagement.apiPolicyEditor.onDidSaveTextDocument', vscode.workspace.onDidSaveTextDocument, async function (this: IActionContext, doc: vscode.TextDocument): Promise<void> { await apiPolicyEditor.onDidSaveTextDocument(this, context.globalState, doc); });
registerCommand('azureApiManagement.showApiPolicy', async (node?: ApiPolicyTreeItem) => {
if (!node) {
const apiNode = <ApiTreeItem>await ext.tree.showTreeItemPicker(ApiTreeItem.contextValue);
node = apiNode.policyTreeItem;
}
await apiPolicyEditor.showEditor(node);
}, doubleClickDebounceDelay);
const operationPolicyEditor: OperationPolicyEditor = new OperationPolicyEditor();
context.subscriptions.push(operationPolicyEditor);
registerEvent('azureApiManagement.operationPolicyEditor.onDidSaveTextDocument', vscode.workspace.onDidSaveTextDocument, async function (this: IActionContext, doc: vscode.TextDocument): Promise<void> { await operationPolicyEditor.onDidSaveTextDocument(this, context.globalState, doc); });
registerCommand('azureApiManagement.showOperationPolicy', async (node?: OperationPolicyTreeItem) => {
if (!node) {
const operationNode = <ApiOperationTreeItem>await ext.tree.showTreeItemPicker(ApiOperationTreeItem.contextValue);
node = operationNode.policyTreeItem;
}
await operationPolicyEditor.showEditor(node);
}, doubleClickDebounceDelay);
}
// this method is called when your extension is deactivated
// tslint:disable:typedef
// tslint:disable-next-line:no-empty
export function deactivateInternal() {}

17
src/extensionVariables.ts Normal file
Просмотреть файл

@ -0,0 +1,17 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtensionContext, OutputChannel } from "vscode";
import { AzureTreeDataProvider, IAzureUserInput, ITelemetryReporter } from "vscode-azureextensionui";
/**
* Namespace for common variables used throughout the extension. They must be initialized in the activate() method of extension.ts
*/
export namespace ext {
export let context: ExtensionContext;
export let tree: AzureTreeDataProvider;
export let outputChannel: OutputChannel;
export let ui: IAzureUserInput;
export let reporter: ITelemetryReporter;
}

8
src/localize.ts Normal file
Просмотреть файл

@ -0,0 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vscode-nls';
export const localize: nls.LocalizeFunc = nls.loadMessageBundle();

Просмотреть файл

@ -0,0 +1,18 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface IOpenApiImportObject {
version: string;
info: {
title: string;
description: string;
};
host?: string;
basePath?: string;
schemes?: string[];
// tslint:disable-next-line:no-any
sourceDocument: any;
importFormat: "swagger-json" | "openapi" | "openapi+json";
}

Просмотреть файл

@ -0,0 +1,113 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as SwaggerParser from "swagger-parser";
import { Uri } from "vscode";
import { parseError } from "vscode-azureextensionui";
import { ext } from "../extensionVariables";
import { localize } from "../localize";
import { IOpenApiImportObject } from "./OpenApiImportObject";
export class OpenApiParser {
public async parse(source: string): Promise<IOpenApiImportObject> {
// tslint:disable-next-line:no-any
let parsed: any;
try {
parsed = await SwaggerParser.parse(source);
} catch (e) {
const message: string = localize("openApiParseError", "Could not parse the OpenAPI document.");
ext.outputChannel.appendLine(message);
ext.outputChannel.show();
const err = parseError(e);
ext.outputChannel.appendLine(`${err.message}`);
throw new Error(message);
}
const importObject: IOpenApiImportObject = {
// tslint:disable-next-line: no-unsafe-any
version: this.getOpenApiVersion(parsed),
sourceDocument: parsed,
// tslint:disable-next-line: no-unsafe-any
info: parsed.info,
importFormat: "swagger-json"
};
if (importObject.version === "2.0") {
const oai20 = <IOpenApi20><unknown>parsed;
importObject.schemes = oai20.schemes;
importObject.host = oai20.host;
importObject.basePath = oai20.basePath;
} else if (importObject.version.startsWith("3.")) {
importObject.importFormat = "openapi+json";
const oai30 = <IOpenApi30><unknown>parsed;
if (oai30.servers.length > 0) {
importObject.schemes = oai30.servers.map(item => item.url.substring(0, item.url.indexOf("://"))).filter((value, index, self) => self.indexOf(value) === index);
const url = Uri.parse(oai30.servers[0].url);
importObject.host = url.authority;
importObject.basePath = url.path;
}
}
return importObject;
}
public updateBasePath(source: object, basePath: string, proxyHostName: string) : void {
const version = this.getOpenApiVersion(source);
if (version === "2.0") {
const oai20 = <IOpenApi20>source;
oai20.host = proxyHostName;
if (basePath) {
oai20.basePath = basePath;
} else {
delete oai20.basePath;
}
} else if (version.startsWith("3.")) {
const oai30 = <IOpenApi30>source;
oai30.servers = oai30.servers.map(item => {
return { url: `${item.url.split("://")[0]}://${proxyHostName}${basePath}` };
});
}
}
public updateBackend(source: object, url: string) : void {
const parsedUrl = Uri.parse(url);
const version = this.getOpenApiVersion(source);
if (version === "2.0") {
const oai20 = <IOpenApi20>source;
oai20.host = parsedUrl.authority;
if (parsedUrl.path) {
oai20.basePath = parsedUrl.path;
} else {
delete oai20.basePath;
}
} else if (version.startsWith("3.")) {
const oai30 = <IOpenApi30>source;
oai30.servers = [{ url: url }];
}
}
private getOpenApiVersion(parsed: object) : string {
return (<IOpenApi30>parsed).openapi || (<IOpenApi20>parsed).swagger;
}
}
interface IOpenApi20 {
swagger: string;
host: string;
basePath: string;
schemes: string[];
}
interface IOpenApi30 {
openapi: string;
servers: { url: string; }[];
}

Просмотреть файл

@ -0,0 +1,30 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ParameterContract } from "azure-arm-apimanagement/lib/models";
export class ConsoleHeader {
public name: string;
public value: string;
public required: boolean;
constructor(contract?: ParameterContract) {
this.name = "";
this.value = "";
this.required = false;
if (contract) {
this.name = contract.name;
if (contract.defaultValue) {
this.value = contract.defaultValue;
}
if (contract.required) {
this.required = contract.required;
}
}
}
}

Просмотреть файл

@ -0,0 +1,30 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { OperationContract } from "azure-arm-apimanagement/lib/models";
import { nonNullProp } from "../utils/nonNull";
import { ConsoleParameter } from "./ConsoleParameter";
import { ConsoleRequest } from "./ConsoleRequest";
export class ConsoleOperation {
public name: string;
public method: string;
public hostName: string;
public uriTemplate: string;
public templateParameters: ConsoleParameter[];
public request: ConsoleRequest;
constructor(hostName: string, operationContract: OperationContract) {
this.hostName = hostName;
this.name = operationContract.displayName;
this.method = operationContract.method.toUpperCase();
this.uriTemplate = operationContract.urlTemplate;
this.request = new ConsoleRequest(nonNullProp(operationContract, "request"));
this.templateParameters = operationContract.templateParameters ? operationContract.templateParameters.map(parameterContract => new ConsoleParameter(parameterContract)) : [];
if (this.uriTemplate && this.uriTemplate.length > 0 && this.uriTemplate[this.uriTemplate.length - 1] === "*") {
this.templateParameters.push(new ConsoleParameter({ name: "*", values: [], type: "", required: false }));
}
}
}

Просмотреть файл

@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ParameterContract } from "azure-arm-apimanagement/lib/models";
export class ConsoleParameter {
public name: string;
public value: string;
constructor(contract?: ParameterContract) {
this.name = "";
this.value = "";
if (contract) {
this.name = contract.name;
if (contract.defaultValue) {
this.value = contract.defaultValue;
}
}
}
}

Просмотреть файл

@ -0,0 +1,50 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { RequestContract } from "azure-arm-apimanagement/lib/models";
import { ConsoleHeader } from "./ConsoleHeader";
import { ConsoleParameter } from "./ConsoleParameter";
export class ConsoleRequest {
public queryParameters: ConsoleParameter[];
public headers: ConsoleHeader[];
public description: string | undefined;
public body: string;
private requestHeaders: ConsoleHeader[];
constructor(requestModel: RequestContract) {
this.description = requestModel.description;
const representations = requestModel.representations ? requestModel.representations : [];
this.queryParameters = requestModel.queryParameters ? requestModel.queryParameters.map(parameterContract => new ConsoleParameter(parameterContract)) : [];
this.requestHeaders = requestModel.headers ? requestModel.headers.map(headerContract => new ConsoleHeader(headerContract)) : [];
this.headers = this.requestHeaders.filter(header => header.required);
this.body = "";
if (representations.length > 0) {
if (representations[0].sample) {
this.body = representations[0].sample;
}
const contentType = representations[0].contentType;
if (contentType && this.headers.find(h => h.name === "Content-Type") === undefined) {
const consoleHeader = new ConsoleHeader();
consoleHeader.name = "Content-Type";
consoleHeader.value = contentType;
this.headers.push(consoleHeader);
}
}
const keyHeader = new ConsoleHeader();
keyHeader.name = "Ocp-Apim-Subscription-Key";
keyHeader.value = "{{SusbscriptionKey}}";
this.headers.push(keyHeader);
const traceHeader = new ConsoleHeader();
traceHeader.name = "Ocp-Apim-Trace";
traceHeader.value = "true";
this.headers.push(traceHeader);
}
}

Просмотреть файл

@ -0,0 +1,103 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ApiContract, ApiRevisionContract } from "azure-arm-apimanagement/lib/models";
import { IOperationTreeRoot } from "../explorer/IOperationTreeRoot";
import { nonNullOrEmptyValue, nonNullProp } from "../utils/nonNull";
import { ConsoleOperation } from "./ConsoleOperation";
export class OperationConsole {
public async buildRequestInfo(root: IOperationTreeRoot): Promise<string> {
const results = await Promise.all([
root.client.apiManagementService.get(root.resourceGroupName, root.serviceName),
root.client.api.get(root.resourceGroupName, root.serviceName, root.apiName),
root.client.apiOperation.get(root.resourceGroupName, root.serviceName, root.apiName, root.opName),
root.client.apiRevision.listByService(root.resourceGroupName, root.serviceName, root.apiName)]);
const service = results[0];
const api = results[1];
const operation = results[2];
const hostName = nonNullProp(service, "gatewayUrl").split("/")[2];
const consoleOperation = new ConsoleOperation(hostName, operation);
let revision: ApiRevisionContract | undefined;
if (api.apiRevision) {
const revisions = results[3];
revision = revisions.find((r) => r.apiRevision === api.apiRevision);
}
const url = `${this.getRequestUrl(consoleOperation, api, revision)}`;
const method = consoleOperation.method;
let requestSummary = `${method} ${url} HTTP/1.1\n`;
consoleOperation.request.headers.forEach(header => {
if (header.name && header.value) {
requestSummary += `${header.name}: ${header.value}\n`;
}
});
requestSummary += `\n\n${consoleOperation.request.body}`;
return requestSummary;
}
private getRequestUrl(consoleOperation: ConsoleOperation, api: ApiContract, revision: ApiRevisionContract | undefined): string {
const protocol = nonNullProp(api, "protocols").indexOf("https") !== -1 ? "https" : "http";
let urlTemplate = this.requestUrl(consoleOperation, api, revision);
if (urlTemplate && urlTemplate.length > 0 && urlTemplate[urlTemplate.length - 1] === "*") {
urlTemplate = urlTemplate.replace("*", "");
}
return `${protocol}://${consoleOperation.hostName}${this.ensureLeadingSlash(urlTemplate)}`;
}
private requestUrl(consoleOperation: ConsoleOperation, api: ApiContract, apiRevision: ApiRevisionContract | undefined): string {
let versionPath = "";
let revision = "";
if (api.apiVersionSet && api.apiVersion && api.apiVersionSet.versioningScheme === "Segment") {
versionPath = `/${api.apiVersion}`;
}
let requestUrl = consoleOperation.uriTemplate;
const parameters = consoleOperation.templateParameters.concat(consoleOperation.request.queryParameters);
parameters.forEach(parameter => {
if (parameter.value) {
const parameterPlaceholder = parameter.name !== "*" ? `{${parameter.name}}` : "*";
if (requestUrl.indexOf(parameterPlaceholder) > -1) {
requestUrl = requestUrl.replace(parameterPlaceholder, encodeURI(parameter.value));
} else {
requestUrl = this.addParam(requestUrl, encodeURI(parameter.name), encodeURI(parameter.value));
}
}
});
if (api.apiVersionSet && api.apiVersionSet.versioningScheme === "Query") {
requestUrl = this.addParam(requestUrl, nonNullOrEmptyValue(api.apiVersionSet.versionQueryName), nonNullOrEmptyValue(api.apiVersion));
}
if (api.apiRevision && apiRevision && !apiRevision.isCurrent) {
revision = `;rev=${apiRevision.apiRevision}`;
}
return `${api.path}${versionPath}${revision}${requestUrl}`;
}
private addParam(uri: string, name: string, value: string): string {
const separator = uri.indexOf("?") >= 0 ? "&" : "?";
const paramString = !value || value === "" ? name : `${name}=${value}`;
return uri + separator + paramString;
}
private ensureLeadingSlash(url: string): string {
if (!url.startsWith("/")) {
url = `/${url}`;
}
return url;
}
}

28
src/utils/azure.ts Normal file
Просмотреть файл

@ -0,0 +1,28 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from "../localize";
function parseResourceId(id: string): RegExpMatchArray {
const matches: RegExpMatchArray | null = id.match(/\/subscriptions\/(.*)\/resourceGroups\/(.*)\/providers\/(.*)\/(.*)/);
if (matches === null || matches.length < 3) {
throw new Error(localize('azApiManagement.InvalidResourceId', 'Invalid Azure Resource Id'));
}
return matches;
}
export function getResourceGroupFromId(id: string): string {
return parseResourceId(id)[2];
}
export function getSubscriptionFromId(id: string): string {
return parseResourceId(id)[1];
}
export function getNameFromId(id: string): string {
return parseResourceId(id)[4];
}

36
src/utils/errorUtil.ts Normal file
Просмотреть файл

@ -0,0 +1,36 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IParsedError, parseError } from "vscode-azureextensionui";
import { ext } from "../extensionVariables";
// tslint:disable: no-unsafe-any
export function errorUtil(responseBody: string): string {
let msg: string = "";
const errors = JSON.parse(responseBody);
errors.error.details.forEach((element) => {
msg = msg.concat(`Error: "${element.code}" - "${element.message}" \n`);
});
return msg;
}
// tslint:disable-next-line:no-any
export function processError(error: any, message: string) : string {
ext.outputChannel.appendLine(error);
try {
const err : IParsedError = parseError(error);
message = err.message;
if (err.errorType.toLowerCase() === 'validationerror') {
// tslint:disable-next-line: no-unsafe-any
message = errorUtil(error.response.body);
}
} catch (e) {
ext.outputChannel.appendLine("Could not parse error.");
}
ext.outputChannel.appendLine(message);
ext.outputChannel.show();
return message;
}

36
src/utils/fsUtil.ts Normal file
Просмотреть файл

@ -0,0 +1,36 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as crypto from "crypto";
import * as fse from 'fs-extra';
import * as path from 'path';
import { extensionName, sessionFolderKey } from '../constants';
import { ext } from '../extensionVariables';
export async function createTemporaryFile(fileName: string): Promise<string> {
// The extension globalStoragePath is a wellknown for vscode and will cleanup when extension gets uninstalled.
const defaultWorkspacePath = path.join(ext.context.globalStoragePath, extensionName);
await fse.ensureDir(defaultWorkspacePath);
// Every vscode sessions will get a unique folder to works with the files
// This folder will be deleted post vscode session.
const sessionFolder = getSessionWorkingFolderName();
const filePath: string = path.join(defaultWorkspacePath, sessionFolder, fileName);
await fse.ensureFile(filePath);
return filePath;
}
export function getSessionWorkingFolderName() : string {
let sessionFolderName = ext.context.globalState.get(sessionFolderKey);
if (!sessionFolderName) {
const randomFolderNameLength: number = 12;
const buffer: Buffer = crypto.randomBytes(Math.ceil(randomFolderNameLength / 2));
sessionFolderName = buffer.toString('hex').slice(0, randomFolderNameLength);
ext.outputChannel.appendLine(`Session working folder:${sessionFolderName}`);
ext.context.globalState.update(sessionFolderKey, sessionFolderName);
}
return <string>sessionFolderName;
}

30
src/utils/nameUtil.ts Normal file
Просмотреть файл

@ -0,0 +1,30 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IApiTreeRoot } from "../explorer/IApiTreeRoot";
import { IOperationTreeRoot } from "../explorer/IOperationTreeRoot";
import { IServiceTreeRoot } from "../explorer/IServiceTreeRoot";
export function nameUtil(root: IServiceTreeRoot | IApiTreeRoot | IOperationTreeRoot): string {
let name = root.serviceName;
if (isApiRoot(root)) {
name = `${name}-${root.apiName}`;
}
if (isOperationRoot(root)) {
name = `${name}-${root.opName}`;
}
return name;
}
function isApiRoot(root: IServiceTreeRoot | IApiTreeRoot | IOperationTreeRoot): root is IApiTreeRoot {
return (<IApiTreeRoot>root).apiName !== undefined;
}
function isOperationRoot(root: IServiceTreeRoot |IApiTreeRoot | IOperationTreeRoot): root is IOperationTreeRoot {
return (<IOperationTreeRoot>root).opName !== undefined;
}

52
src/utils/nonNull.ts Normal file
Просмотреть файл

@ -0,0 +1,52 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { isNullOrUndefined } from 'util';
/**
* Retrieves a property by name from an object and checks that it's not null and not undefined. It is strongly typed
* for the property and will give a compile error if the given name is not a property of the source.
*/
export function nonNullProp<TSource, TKey extends keyof TSource>(source: TSource, name: TKey): NonNullable<TSource[TKey]> {
const value: NonNullable<TSource[TKey]> = <NonNullable<TSource[TKey]>>source[name];
return nonNullValue(value, <string>name);
}
/**
* Validates that a given value is not null and not undefined.
*/
export function nonNullValue<T>(value: T | undefined, propertyNameOrMessage?: string): T {
if (isNullOrUndefined(value)) {
throw new Error(
// tslint:disable-next-line:prefer-template
'Internal error: Expected value to be neither null nor undefined'
+ (propertyNameOrMessage ? `: ${propertyNameOrMessage}` : ''));
}
return value;
}
/**
* Validates that a given string is not null, undefined, nor empty
*/
export function nonNullOrEmptyValue(value: string | undefined, propertyNameOrMessage?: string): string {
if (!value) {
throw new Error(
// tslint:disable-next-line:prefer-template
'Internal error: Expected value to be neither null, undefined, nor empty'
+ (propertyNameOrMessage ? `: ${propertyNameOrMessage}` : ''));
}
return value;
}
/**
* Validates that a given object is not null and not undefined.
* Then retrieves a property by name from that object and checks that it's not null and not undefined. It is strongly typed
* for the property and will give a compile error if the given name is not a property of the source.
*/
export function nonNullValueAndProp<TSource, TKey extends keyof TSource>(source: TSource | undefined, name: TKey): NonNullable<TSource[TKey]> {
return nonNullProp(nonNullValue(source, <string>name), name);
}

19
src/utils/requestUtil.ts Normal file
Просмотреть файл

@ -0,0 +1,19 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { WebResource } from "ms-rest";
import * as request from 'request-promise';
import { appendExtensionUserAgent } from "vscode-azureextensionui";
export async function requestUtil(url: string) : Promise<string> {
const requestOptions: WebResource = new WebResource();
requestOptions.headers = {
['User-Agent']: appendExtensionUserAgent()
};
requestOptions.url = url;
// tslint:disable-next-line: await-promise
const response = await request(requestOptions).promise();
return <string>(response);
}

18
src/utils/signRequest.ts Normal file
Просмотреть файл

@ -0,0 +1,18 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ServiceClientCredentials, WebResource } from "ms-rest";
export async function signRequest(req: WebResource, cred: ServiceClientCredentials): Promise<void> {
await new Promise((resolve: () => void, reject: (err: Error) => void): void => {
cred.signRequest(req, (err: Error | undefined) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}

40
src/utils/treeUtils.ts Normal file
Просмотреть файл

@ -0,0 +1,40 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import { AzureParentTreeItem, AzureTreeDataProvider, AzureTreeItem } from 'vscode-azureextensionui';
import { ext } from '../extensionVariables';
import { localize } from '../localize';
export namespace treeUtils {
export interface IThemedIconPath {
light: string;
dark: string;
}
export function getIconPath(iconName: string): string {
return path.join(getResourcesPath(), `${iconName}.svg`);
}
export function getThemedIconPath(iconName: string): IThemedIconPath {
return {
light: path.join(getResourcesPath(), 'light', `${iconName}.svg`),
dark: path.join(getResourcesPath(), 'dark', `${iconName}.svg`)
};
}
function getResourcesPath(): string {
return ext.context.asAbsolutePath('resources');
}
export async function getSubscriptionNode(tree: AzureTreeDataProvider, subscriptionId: string): Promise<AzureParentTreeItem> {
const node: AzureParentTreeItem | undefined = <AzureParentTreeItem | undefined>(await tree.getChildren()).find((n: AzureTreeItem) => n.root.subscriptionId === subscriptionId);
if (node) {
return node;
} else {
throw new Error(localize('noMatchingSubscription', 'Failed to find a subscription matching id "{0}".', subscriptionId));
}
}
}

17
src/utils/vscodeUtils.ts Normal file
Просмотреть файл

@ -0,0 +1,17 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
// tslint:disable-next-line:export-name
export async function writeToEditor(editor: vscode.TextEditor, data: string): Promise<void> {
await editor.edit((editBuilder: vscode.TextEditorEdit) => {
if (editor.document.lineCount > 0) {
const lastLine: vscode.TextLine = editor.document.lineAt(editor.document.lineCount - 1);
editBuilder.delete(new vscode.Range(new vscode.Position(0, 0), new vscode.Position(lastLine.range.start.line, lastLine.range.end.character)));
}
editBuilder.insert(new vscode.Position(0, 0), data);
});
}

Просмотреть файл

@ -0,0 +1,17 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ConfigurationTarget, Uri, workspace, WorkspaceConfiguration } from "vscode";
import { extensionPrefix } from "../constants";
export async function updateGlobalSetting<T = string>(section: string, value: T, prefix: string = extensionPrefix): Promise<void> {
const projectConfiguration: WorkspaceConfiguration = workspace.getConfiguration(prefix);
await projectConfiguration.update(section, value, ConfigurationTarget.Global);
}
export function getWorkspaceSetting<T>(key: string, fsPath?: string, prefix: string = extensionPrefix): T | undefined {
const projectConfiguration: WorkspaceConfiguration = workspace.getConfiguration(prefix, fsPath ? Uri.file(fsPath) : undefined);
return projectConfiguration.get<T>(key);
}

22
test/extension.test.ts Normal file
Просмотреть файл

@ -0,0 +1,22 @@
//
// Note: This example test is leveraging the Mocha test framework.
// Please refer to their documentation on https://mochajs.org/ for help.
//
// The module 'assert' provides assertion methods from node
import * as assert from 'assert';
// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
// import * as vscode from 'vscode';
// import * as myExtension from '../extension';
// Defines a Mocha test suite to group tests of similar kind together
suite("Extension Tests", () => {
// Defines a Mocha unit test
test("Something 1", () => {
assert.equal(-1, [1, 2, 3].indexOf(5));
assert.equal(-1, [1, 2, 3].indexOf(0));
});
});

66
test/index.ts Normal file
Просмотреть файл

@ -0,0 +1,66 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//
// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING
//
// This file is providing the test runner to use when running extension tests.
// By default the test runner in use is Mocha based.
//
// You can provide your own test runner if you want to override it by exporting
// a function run(testRoot: string, clb: (error:Error) => void) that the extension
// host can call to run the tests. The test runner is expected to use console.log
// to report the results back to the caller. When the tests are finished, return
// a possible error to the callback or null if none.
import * as path from 'path';
// tslint:disable-next-line:no-require-imports no-submodule-imports
import testRunner = require('vscode/lib/testrunner');
const options: { [key: string]: string | boolean | number | object } = {
ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
useColors: true // colored output from test results
};
// You can directly control Mocha options using environment variables beginning with MOCHA_.
// For example:
// {
// "name": "Launch Tests",
// "type": "extensionHost",
// "request": "launch",
// ...
// "env": {
// "MOCHA_enableTimeouts": "0",
// "MOCHA_grep": "tests-to-run"
// }
//
// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for all available options
// Defaults
options.reporter = 'mocha-multi-reporters';
options.reporterOptions = {
reporterEnabled: 'spec, mocha-junit-reporter',
mochaJunitReporterReporterOptions: {
mochaFile: path.join(__dirname, '..', '..', 'test-results.xml')
}
};
for (const envVar of Object.keys(process.env)) {
const match: RegExpMatchArray | null = envVar.match(/^mocha_(.+)/i);
if (match) {
const [, option] = match;
// tslint:disable-next-line:strict-boolean-expressions
let value: string | number = process.env[envVar] || '';
if (typeof value === 'string' && !isNaN(parseInt(value))) {
value = parseInt(value);
}
options[option] = value;
}
}
console.warn(`Mocha options: ${JSON.stringify(options, null, 2)}`);
testRunner.configure(options);
module.exports = testRunner;

23
test/tslint.json Normal file
Просмотреть файл

@ -0,0 +1,23 @@
{
"extends": "../tslint.json",
"rules": {
"no-console": false,
"no-implicit-dependencies": [
true,
"dev"
],
"typedef": [
true,
"call-signature",
// "arrow-call-signature",
"parameter",
// "arrow-parameter",
"property-declaration",
"variable-declaration",
"member-variable-declaration"
// "object-destructuring",
// "array-destructuring"
],
"no-unexternalized-strings": false
}
}

28
tsconfig.json Normal file
Просмотреть файл

@ -0,0 +1,28 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"outDir": "out",
"lib": [
"es6"
],
"sourceMap": true,
"rootDir": ".",
"noUnusedLocals": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"strictNullChecks": true,
"noUnusedParameters": true,
"baseUrl": "./",
"paths": {
"*": [
"node_modules/vscode/*",
"*"
]
}
},
"exclude": [
"node_modules",
".vscode-test"
]
}

76
tslint.json Normal file
Просмотреть файл

@ -0,0 +1,76 @@
{
"extends": "tslint-microsoft-contrib",
"rules": {
"file-header": [
true,
{
"match": "Copyright \\d{4}",
"allow-single-line-comments": true,
"default": "Copyright (c) Microsoft Corporation. All rights reserved.\nLicensed under the MIT License. See License.txt in the project root for license information.",
"enforce-trailing-newline": true
}
],
"await-promise": [
true,
"Thenable"
],
"no-increment-decrement": false,
"strict-boolean-expressions": [
true,
"allow-string",
"allow-undefined-union",
"allow-null-union",
"allow-mix"
],
"completed-docs": [
false,
"classes"
],
"function-name": [
true,
{
"static-method-regex": "^[a-z][a-zA-Z_\\d]+$"
}
],
"import-name": false,
"max-line-length": [
false,
140
],
"missing-jsdoc": false,
"no-backbone-get-set-outside-model": false,
"no-multiline-string": false,
"no-single-line-block-comment": false,
"no-parameter-properties": false,
"no-relative-imports": false,
"no-require-imports": false,
"no-void-expression": [
false,
"ignore-arrow-function-shorthand"
],
"typedef": [
true,
"call-signature",
"parameter",
"property-declaration",
"member-variable-declaration"
],
"variable-name": [
true,
"allow-leading-underscore",
"ban-keywords"
],
"linebreak-style": false,
"newline-before-return": false,
"quotemark": [
false,
"single"
],
"radix": false
},
"linterOptions": {
"exclude": [
"src/test/**" // Enable back when tests are ready.
]
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше