From 069b7a60fb41ceb3d3a8466be91880644e37c4c6 Mon Sep 17 00:00:00 2001 From: Tanner Barlow Date: Fri, 24 May 2019 13:15:05 -0700 Subject: [PATCH 1/4] test: Unit tests for deploy and login plugins --- README.md | 2 + jest.config.js | 13 + jest.config.json | 8 - package-lock.json | 2237 ++++++++++++++++- package.json | 5 +- src/config.ts | 2 +- src/index.test.ts | 6 - src/index.ts | 6 +- src/plugins/apim/apimFunctionPlugin.ts | 2 +- src/plugins/apim/apimServicePlugin.test.ts | 19 + src/plugins/apim/apimServicePlugin.ts | 7 +- .../deploy/azureDeployFunctionPlugin.ts | 27 - src/plugins/deploy/azureDeployPlugin.test.ts | 32 + src/plugins/deploy/azureDeployPlugin.ts | 15 +- src/plugins/invoke/azureInvoke.ts | 6 +- src/plugins/login/loginPlugin.test.ts | 123 + src/plugins/login/loginPlugin.ts | 33 +- src/plugins/logs/azureLogs.ts | 2 +- src/plugins/package/azurePackage.ts | 37 +- src/plugins/package/azurePackageFunction.ts | 26 - src/plugins/remove/azureRemove.ts | 2 +- src/provider/armTemplates/azuredeploy.json | 4 + src/provider/azureProvider.ts | 6 +- src/services/apimService.ts | 32 +- src/services/baseService.ts | 22 +- src/services/functionAppService.ts | 165 +- src/services/loginService.ts | 27 + src/services/resourceService.ts | 8 +- src/shared/binding.test.ts | 11 + src/shared/bindings.ts | 8 +- src/shared/getAdminKey.ts | 3 - src/test/mockFactory.ts | 106 + src/test/utils.ts | 3 + tsconfig.json | 10 +- 34 files changed, 2663 insertions(+), 352 deletions(-) create mode 100644 jest.config.js delete mode 100644 jest.config.json delete mode 100644 src/index.test.ts create mode 100644 src/plugins/apim/apimServicePlugin.test.ts delete mode 100644 src/plugins/deploy/azureDeployFunctionPlugin.ts create mode 100644 src/plugins/deploy/azureDeployPlugin.test.ts create mode 100644 src/plugins/login/loginPlugin.test.ts delete mode 100644 src/plugins/package/azurePackageFunction.ts create mode 100644 src/services/loginService.ts create mode 100644 src/shared/binding.test.ts delete mode 100644 src/shared/getAdminKey.ts create mode 100644 src/test/mockFactory.ts create mode 100644 src/test/utils.ts diff --git a/README.md b/README.md index 5b1f3a76..310a693b 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ This plugin enables Azure Functions support within the Serverless Framework. +[![Coverage Status](https://coveralls.io/repos/github/serverless/serverless-azure-functions/badge.svg)](https://coveralls.io/github/serverless/serverless-azure-functions) + ## Getting started ### Pre-requisites diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 00000000..1d854c50 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,13 @@ +module.exports = { + preset: 'ts-jest', + transform: { + ".(ts|tsx)": "ts-jest" + }, + testRegex: "((\\.|/)(test|spec))\\.ts$", + moduleFileExtensions: ["ts", "tsx", "js"], + testPathIgnorePatterns: [ + "/lib/", + "/node_modules/" + ], + collectCoverage: true +}; \ No newline at end of file diff --git a/jest.config.json b/jest.config.json deleted file mode 100644 index 2e85d517..00000000 --- a/jest.config.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "transform": { - "^.+\\.ts$": "ts-jest" - }, - "testRegex": "((\\.|/)(test|spec))\\.ts$", - "testPathIgnorePatterns": ["/lib/", "/node_modules/"], - "collectCoverage": true -} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 3b93fcb0..8959dd31 100644 --- a/package-lock.json +++ b/package-lock.json @@ -648,6 +648,12 @@ "integrity": "sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==", "dev": true }, + "@types/lodash": { + "version": "4.14.130", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.130.tgz", + "integrity": "sha512-H++wk0tbneBsRVfLkgAAd0IIpmpVr2Bj4T0HncoOsQf3/xrJexRYQK2Tqo0Ej3pFslM8GkMgdis9bu6xIb1ycw==", + "dev": true + }, "@types/node": { "version": "8.10.48", "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.48.tgz", @@ -850,6 +856,15 @@ "xpath.js": "~1.1.0" } }, + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, "ajv": { "version": "6.10.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", @@ -871,6 +886,21 @@ "repeat-string": "^1.5.2" } }, + "ansi": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz", + "integrity": "sha1-DELU+xcWDVqa8eSEus4cZpIsGyE=", + "dev": true + }, + "ansi-align": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", + "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", + "dev": true, + "requires": { + "string-width": "^2.0.0" + } + }, "ansi-escapes": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", @@ -900,6 +930,23 @@ "normalize-path": "^2.1.1" } }, + "archiver": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-1.3.0.tgz", + "integrity": "sha1-TyGU1tj5nfP1MeaIHxTxXVX6ryI=", + "dev": true, + "requires": { + "archiver-utils": "^1.3.0", + "async": "^2.0.0", + "buffer-crc32": "^0.2.1", + "glob": "^7.0.0", + "lodash": "^4.8.0", + "readable-stream": "^2.0.0", + "tar-stream": "^1.5.0", + "walkdir": "^0.0.11", + "zip-stream": "^1.1.0" + } + }, "archiver-utils": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-1.3.0.tgz", @@ -936,6 +983,16 @@ } } }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -966,6 +1023,21 @@ "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "dev": true }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", @@ -1068,6 +1140,70 @@ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, + "aws-sdk": { + "version": "2.461.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.461.0.tgz", + "integrity": "sha512-nqRqlOaM92P6BTx/huq8FuowWNPiRRcpEKHvAQ2XTWTQUADx9HIP9KtbEzLpauxE4Er2reM0UYz9Kbtyke/3EQ==", + "dev": true, + "requires": { + "buffer": "4.9.1", + "events": "1.1.1", + "ieee754": "1.1.8", + "jmespath": "0.15.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "uuid": "3.3.2", + "xml2js": "0.4.19" + }, + "dependencies": { + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "dev": true + }, + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", + "dev": true + }, + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=", + "dev": true + }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + } + } + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -1241,8 +1377,7 @@ "base64-js": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", - "optional": true + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" }, "bcrypt-pbkdf": { "version": "1.0.2", @@ -1268,7 +1403,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", - "optional": true, "requires": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" @@ -1277,8 +1411,7 @@ "bluebird": { "version": "3.5.4", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz", - "integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==", - "optional": true + "integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==" }, "bn.js": { "version": "4.11.8", @@ -1293,6 +1426,29 @@ "hoek": "2.x.x" } }, + "boxen": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", + "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", + "dev": true, + "requires": { + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + } + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1448,7 +1604,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", - "optional": true, "requires": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" @@ -1458,7 +1613,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "optional": true, "requires": { "buffer-alloc-unsafe": "^1.1.0", "buffer-fill": "^1.0.0" @@ -1467,8 +1621,7 @@ "buffer-alloc-unsafe": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "optional": true + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" }, "buffer-crc32": { "version": "0.2.13", @@ -1483,8 +1636,7 @@ "buffer-fill": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", - "optional": true + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" }, "buffer-from": { "version": "1.1.1", @@ -1525,6 +1677,12 @@ "unset-value": "^1.0.0" } }, + "cachedir": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.2.0.tgz", + "integrity": "sha512-VvxA0xhNqIIfg0V9AmJkDg91DaJwryutH5rVEZAhcNi4iJFj9f+QxmAjgK1LT9I8OgToX27fypX6/MeCXVbBjQ==", + "dev": true + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1546,11 +1704,29 @@ "rsvp": "^4.8.4" } }, + "capture-stack-trace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", + "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", + "dev": true + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, + "caw": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/caw/-/caw-2.0.1.tgz", + "integrity": "sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==", + "dev": true, + "requires": { + "get-proxy": "^2.0.0", + "isurl": "^1.0.0-alpha5", + "tunnel-agent": "^0.6.0", + "url-to-options": "^1.0.1" + } + }, "center-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", @@ -1641,6 +1817,12 @@ } } }, + "cli-boxes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", + "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", + "dev": true + }, "cli-cursor": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", @@ -1724,19 +1906,83 @@ "version": "2.20.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", - "dev": true, - "optional": true + "dev": true }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, + "compress-commons": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-1.2.2.tgz", + "integrity": "sha1-UkqfEJA/OoEzibAiXSfEi7dRiQ8=", + "dev": true, + "requires": { + "buffer-crc32": "^0.2.1", + "crc32-stream": "^2.0.0", + "normalize-path": "^2.0.0", + "readable-stream": "^2.0.0" + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "config-chain": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", + "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", + "dev": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "configstore": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", + "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", + "dev": true, + "requires": { + "dot-prop": "^4.1.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" + }, + "dependencies": { + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, "console-browserify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", @@ -1761,6 +2007,18 @@ "safe-buffer": "~5.1.1" } }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "dev": true + }, + "cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", + "dev": true + }, "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", @@ -1819,11 +2077,20 @@ "version": "3.8.0", "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", - "optional": true, "requires": { "buffer": "^5.1.0" } }, + "crc32-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-2.0.0.tgz", + "integrity": "sha1-483TtN8xaN10494/u8t7KX/pCPQ=", + "dev": true, + "requires": { + "crc": "^3.4.4", + "readable-stream": "^2.0.0" + } + }, "create-ecdh": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", @@ -1834,6 +2101,15 @@ "elliptic": "^6.0.0" } }, + "create-error-class": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", + "dev": true, + "requires": { + "capture-stack-trace": "^1.0.0" + } + }, "create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", @@ -1863,7 +2139,6 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "optional": true, "requires": { "lru-cache": "^4.0.1", "shebang-command": "^1.2.0", @@ -1874,7 +2149,6 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "optional": true, "requires": { "pseudomap": "^1.0.2", "yallist": "^2.1.2" @@ -1909,6 +2183,12 @@ "randomfill": "^1.0.3" } }, + "crypto-random-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", + "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", + "dev": true + }, "cssom": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz", @@ -1993,6 +2273,120 @@ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, + "decompress": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.0.tgz", + "integrity": "sha1-eu3YVCflqS2s/lVnSnxQXpbQH50=", + "dev": true, + "requires": { + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" + }, + "dependencies": { + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + } + } + }, + "decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "dev": true, + "requires": { + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" + } + }, + "decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "dev": true, + "requires": { + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" + }, + "dependencies": { + "file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==", + "dev": true + } + } + }, + "decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "dev": true, + "requires": { + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" + } + }, + "decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", + "dev": true, + "requires": { + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" + }, + "dependencies": { + "file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=", + "dev": true + }, + "get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + } + } + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -2054,6 +2448,12 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, "des.js": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", @@ -2117,6 +2517,36 @@ "webidl-conversions": "^4.0.2" } }, + "dot-prop": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", + "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "dev": true, + "requires": { + "is-obj": "^1.0.0" + } + }, + "download": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/download/-/download-5.0.3.tgz", + "integrity": "sha1-Y1N/l3+ZJmow64oqL70fILgAD3o=", + "dev": true, + "requires": { + "caw": "^2.0.0", + "decompress": "^4.0.0", + "filenamify": "^2.0.0", + "get-stream": "^3.0.0", + "got": "^6.3.0", + "mkdirp": "^0.5.1", + "pify": "^2.3.0" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, "ebnf-parser": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/ebnf-parser/-/ebnf-parser-0.1.10.tgz", @@ -2165,6 +2595,15 @@ "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", "optional": true }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "dev": true, + "requires": { + "iconv-lite": "~0.4.13" + } + }, "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", @@ -2260,6 +2699,21 @@ "event-emitter": "~0.3.5" } }, + "es6-promise": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.6.tgz", + "integrity": "sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q==", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, "es6-set": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", @@ -2612,7 +3066,6 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "optional": true, "requires": { "cross-spawn": "^5.0.1", "get-stream": "^3.0.0", @@ -2629,6 +3082,12 @@ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", "dev": true }, + "exit-hook": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", + "dev": true + }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -2699,10 +3158,32 @@ } } }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "external-editor": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-1.1.1.tgz", + "integrity": "sha1-Etew24UPf/fnCBuvQAVwAGDEYAs=", + "dev": true, + "requires": { + "extend": "^3.0.0", + "spawn-sync": "^1.0.15", + "tmp": "^0.0.29" + }, + "dependencies": { + "tmp": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.29.tgz", + "integrity": "sha1-8lEl/w3Z2jzLDC3Tce4SiLuRKMA=", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.1" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "requires": { "array-unique": "^0.3.2", "define-property": "^1.0.0", @@ -2792,6 +3273,15 @@ "bser": "^2.0.0" } }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -2810,6 +3300,35 @@ "flat-cache": "^2.0.1" } }, + "file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=", + "dev": true + }, + "filename-reserved-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=", + "dev": true + }, + "filenamify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-2.1.0.tgz", + "integrity": "sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA==", + "dev": true, + "requires": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.0", + "trim-repeated": "^1.0.0" + } + }, + "filesize": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz", + "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", + "dev": true + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -2900,6 +3419,12 @@ "mime-types": "^2.1.12" } }, + "formidable": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", + "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==", + "dev": true + }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -2911,8 +3436,20 @@ "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "optional": true + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "fs-extra": { + "version": "0.26.7", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz", + "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } }, "fs.realpath": { "version": "1.0.0", @@ -3393,16 +3930,43 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "gauge": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz", + "integrity": "sha1-6c7FSD09TuDvRLYKfZnkk14TbZM=", + "dev": true, + "requires": { + "ansi": "^0.3.0", + "has-unicode": "^2.0.0", + "lodash.pad": "^4.1.0", + "lodash.padend": "^4.1.0", + "lodash.padstart": "^4.1.0" + } + }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" }, + "get-proxy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz", + "integrity": "sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw==", + "dev": true, + "requires": { + "npm-conf": "^1.1.0" + } + }, + "get-stdin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", + "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", + "dev": true + }, "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "optional": true + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" }, "get-value": { "version": "2.0.6", @@ -3452,17 +4016,73 @@ } } }, + "global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "dev": true, + "requires": { + "ini": "^1.3.4" + } + }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "got": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", + "dev": true, + "requires": { + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" + } + }, "graceful-fs": { "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", + "dev": true + }, + "graphlib": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.7.tgz", + "integrity": "sha512-TyI9jIy2J4j0qgPmOOrHTCtpPqJGN/aurBwc6ZT+bRii+di1I+Wv3obRhVrmBEXet+qkMaEX67dXrwsd3QQM6w==", + "dev": true, + "requires": { + "lodash": "^4.17.5" + } + }, "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -3531,17 +4151,47 @@ "function-bind": "^1.1.1" } }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, + "has-symbol-support-x": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", + "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", + "dev": true + }, "has-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", "dev": true }, + "has-to-string-tag-x": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", + "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "dev": true, + "requires": { + "has-symbol-support-x": "^1.4.1" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -3655,6 +4305,33 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "optional": true }, + "https-proxy-agent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", + "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", + "dev": true, + "requires": { + "agent-base": "^4.1.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -3667,8 +4344,7 @@ "ieee754": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "optional": true + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, "ignore": { "version": "4.0.6", @@ -3676,6 +4352,12 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", + "dev": true + }, "import-fresh": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz", @@ -3686,6 +4368,12 @@ "resolve-from": "^4.0.0" } }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true + }, "import-local": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", @@ -3722,6 +4410,128 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "inquirer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-1.2.3.tgz", + "integrity": "sha1-TexvMvN+97sLLtPx0aXD9UUHSRg=", + "dev": true, + "requires": { + "ansi-escapes": "^1.1.0", + "chalk": "^1.0.0", + "cli-cursor": "^1.0.1", + "cli-width": "^2.0.0", + "external-editor": "^1.1.0", + "figures": "^1.3.5", + "lodash": "^4.3.0", + "mute-stream": "0.0.6", + "pinkie-promise": "^2.0.0", + "run-async": "^2.2.0", + "rx": "^4.1.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-escapes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "mute-stream": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.6.tgz", + "integrity": "sha1-SJYrGeFp/R38JAs/HnMXYnu8R9s=", + "dev": true + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, "interpret": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", @@ -3825,6 +4635,12 @@ } } }, + "is-docker": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-1.1.0.tgz", + "integrity": "sha1-8EN01O7lMQ6ajhE78UlUEeRhdqE=", + "dev": true + }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -3855,6 +4671,28 @@ "is-extglob": "^2.1.1" } }, + "is-installed-globally": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", + "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", + "dev": true, + "requires": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + } + }, + "is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=", + "dev": true + }, + "is-npm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", + "dev": true + }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", @@ -3863,6 +4701,27 @@ "kind-of": "^3.0.2" } }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "is-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", + "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", + "dev": true + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -3877,6 +4736,12 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, + "is-redirect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", + "dev": true + }, "is-regex": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", @@ -3886,6 +4751,12 @@ "has": "^1.0.1" } }, + "is-retry-allowed": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", + "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", + "dev": true + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -4031,6 +4902,16 @@ "handlebars": "^4.1.2" } }, + "isurl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "dev": true, + "requires": { + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" + } + }, "jest": { "version": "24.8.0", "resolved": "https://registry.npmjs.org/jest/-/jest-24.8.0.tgz", @@ -4313,24 +5194,556 @@ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" +<<<<<<< HEAD + "ms": "^2.1.1" } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true } } }, - "jest-config": { - "version": "24.8.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.8.0.tgz", - "integrity": "sha512-Czl3Nn2uEzVGsOeaewGWoDPD8GStxCpAe0zOYs2x2l0fZAgPbCr3uwUkgNKV3LwE13VXythM946cd5rdGkkBZw==", + "istanbul-reports": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", + "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", "dev": true, "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^24.8.0", - "@jest/types": "^24.8.0", - "babel-jest": "^24.8.0", - "chalk": "^2.0.1", - "glob": "^7.1.1", + "handlebars": "^4.1.2" + } + }, + "isurl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "dev": true, + "requires": { + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" + } + }, + "jest": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-24.8.0.tgz", + "integrity": "sha512-o0HM90RKFRNWmAWvlyV8i5jGZ97pFwkeVoGvPW1EtLTgJc2+jcuqcbbqcSZLE/3f2S5pt0y2ZBETuhpWNl1Reg==", + "dev": true, + "requires": { + "import-local": "^2.0.0", + "jest-cli": "^24.8.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "jest-cli": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.8.0.tgz", + "integrity": "sha512-+p6J00jSMPQ116ZLlHJJvdf8wbjNbZdeSX9ptfHX06/MSNaXmKihQzx5vQcw0q2G6JsdVkUIdWbOWtSnaYs3yA==", + "dev": true, + "requires": { + "@jest/core": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "import-local": "^2.0.0", + "is-ci": "^2.0.0", + "jest-config": "^24.8.0", + "jest-util": "^24.8.0", + "jest-validate": "^24.8.0", + "prompts": "^2.0.1", + "realpath-native": "^1.1.0", + "yargs": "^12.0.2" + } + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "jest-changed-files": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.8.0.tgz", + "integrity": "sha512-qgANC1Yrivsq+UrLXsvJefBKVoCsKB0Hv+mBb6NMjjZ90wwxCDmU3hsCXBya30cH+LnPYjwgcU65i6yJ5Nfuug==", + "dev": true, + "requires": { + "@jest/types": "^24.8.0", + "execa": "^1.0.0", + "throat": "^4.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } +======= + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } +>>>>>>> e91e4817920bfeaf600126ab9b98a8cbaeddf686 + } + } + }, + "jest-cli": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.8.0.tgz", + "integrity": "sha512-+p6J00jSMPQ116ZLlHJJvdf8wbjNbZdeSX9ptfHX06/MSNaXmKihQzx5vQcw0q2G6JsdVkUIdWbOWtSnaYs3yA==", + "dev": true, + "requires": { + "@jest/core": "^24.8.0", + "@jest/test-result": "^24.8.0", + "@jest/types": "^24.8.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "import-local": "^2.0.0", + "is-ci": "^2.0.0", + "jest-config": "^24.8.0", + "jest-util": "^24.8.0", + "jest-validate": "^24.8.0", + "prompts": "^2.0.1", + "realpath-native": "^1.1.0", + "yargs": "^12.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "jest-config": { + "version": "24.8.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.8.0.tgz", + "integrity": "sha512-Czl3Nn2uEzVGsOeaewGWoDPD8GStxCpAe0zOYs2x2l0fZAgPbCr3uwUkgNKV3LwE13VXythM946cd5rdGkkBZw==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.8.0", + "@jest/types": "^24.8.0", + "babel-jest": "^24.8.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", "jest-environment-jsdom": "^24.8.0", "jest-environment-node": "^24.8.0", "jest-get-type": "^24.8.0", @@ -4953,6 +6366,12 @@ "nomnom": "1.5.2" } }, + "jmespath": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5084,6 +6503,12 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, + "json-cycle": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/json-cycle/-/json-cycle-1.3.0.tgz", + "integrity": "sha512-FD/SedD78LCdSvJaOUQAXseT8oQBb5z6IVYaQaCrVUlu9zOAr1BDdKyVYQaSD/GDsAMrXpKcOyBD4LIl8nfjHw==", + "dev": true + }, "json-loader": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", @@ -5096,6 +6521,38 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "dev": true }, + "json-refs": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/json-refs/-/json-refs-2.1.7.tgz", + "integrity": "sha1-uesB/in16j6Sh48VrqEK04taz4k=", + "dev": true, + "requires": { + "commander": "^2.9.0", + "graphlib": "^2.1.1", + "js-yaml": "^3.8.3", + "native-promise-only": "^0.8.1", + "path-loader": "^1.0.2", + "slash": "^1.0.0", + "uri-js": "^3.0.2" + }, + "dependencies": { + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "uri-js": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-3.0.2.tgz", + "integrity": "sha1-+QuFhQf4HepNz7s8TD2/orVX+qo=", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + } + } + }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -5131,6 +6588,15 @@ "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", "optional": true }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, "jsonify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", @@ -5165,6 +6631,18 @@ "verror": "1.10.0" } }, + "jszip": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.2.1.tgz", + "integrity": "sha512-iCMBbo4eE5rb1VCpm5qXOAaUiRKRUKiItn8ah2YQQx9qymmSAY98eyQfioChEYcVQLh0zxJ3wS4A0mh90AVPvw==", + "dev": true, + "requires": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "set-immediate-shim": "~1.0.1" + } + }, "jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", @@ -5184,6 +6662,12 @@ "safe-buffer": "^5.0.1" } }, + "jwt-decode": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz", + "integrity": "sha1-fYa9VmefWM5qhHBKZX3TkruoGnk=", + "dev": true + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -5192,12 +6676,30 @@ "is-buffer": "^1.1.5" } }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.9" + } + }, "kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true }, + "latest-version": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", + "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", + "dev": true, + "requires": { + "package-json": "^4.0.0" + } + }, "lazy-cache": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", @@ -5245,6 +6747,15 @@ "resolved": "https://registry.npmjs.org/lex-parser/-/lex-parser-0.1.4.tgz", "integrity": "sha1-ZMTwJfF/1Tv7RXY/rrFvAVp0dVA=" }, + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "requires": { + "immediate": "~3.0.5" + } + }, "load-json-file": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", @@ -5300,6 +6811,30 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, + "lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=", + "dev": true + }, + "lodash.pad": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz", + "integrity": "sha1-QzCUmoM6fI2iLMIPaibE1Z3runA=", + "dev": true + }, + "lodash.padend": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4=", + "dev": true + }, + "lodash.padstart": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", + "integrity": "sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs=", + "dev": true + }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -5312,6 +6847,12 @@ "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", "dev": true }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, "log-driver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", @@ -5332,6 +6873,18 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, + "lsmod": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz", + "integrity": "sha1-mgD3bco26yP6BTUK/htYXUKZ5ks=", + "dev": true + }, "make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -5423,6 +6976,12 @@ "readable-stream": "^2.0.1" } }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -5460,6 +7019,12 @@ "brorand": "^1.0.1" } }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, "mime-db": { "version": "1.40.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", @@ -5535,6 +7100,12 @@ } } }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", + "dev": true + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -5577,6 +7148,12 @@ } } }, + "native-promise-only": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz", + "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=", + "dev": true + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -5599,6 +7176,16 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "dev": true, + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -5706,9 +7293,48 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "requires": { - "remove-trailing-separator": "^1.0.1" + "remove-trailing-separator": "^1.0.1" + } + }, + "npm-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "dev": true, + "requires": { + "config-chain": "^1.1.11", + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, +<<<<<<< HEAD + "npm-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "dev": true, + "requires": { + "config-chain": "^1.1.11", + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } } }, +======= +>>>>>>> e91e4817920bfeaf600126ab9b98a8cbaeddf686 "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -5717,6 +7343,17 @@ "path-key": "^2.0.0" } }, + "npmlog": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-2.0.4.tgz", + "integrity": "sha1-mLUlMPJRTKkNCexbIsiEZyI3VpI=", + "dev": true, + "requires": { + "ansi": "~0.3.1", + "are-we-there-yet": "~1.1.2", + "gauge": "~1.2.5" + } + }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", @@ -5759,6 +7396,12 @@ } } }, + "object-hash": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz", + "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==", + "dev": true + }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -5870,6 +7513,12 @@ "mem": "^1.1.0" } }, + "os-shim": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz", + "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=", + "dev": true + }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", @@ -5932,11 +7581,22 @@ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "optional": true }, + "package-json": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", + "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", + "dev": true, + "requires": { + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" + } + }, "pako": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", - "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", - "optional": true + "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==" }, "parent-module": { "version": "1.0.1", @@ -6019,6 +7679,16 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" }, + "path-loader": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/path-loader/-/path-loader-1.0.10.tgz", + "integrity": "sha512-CMP0v6S6z8PHeJ6NFVyVJm6WyJjIwFvyz2b0n2/4bKdS/0uZa/9sKUlYZzubrn3zuDRU0zIuEDX9DZYQ2ZI8TA==", + "dev": true, + "requires": { + "native-promise-only": "^0.8.1", + "superagent": "^3.8.3" + } + }, "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", @@ -6045,6 +7715,12 @@ "sha.js": "^2.4.8" } }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", + "dev": true + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -6056,6 +7732,21 @@ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, "pirates": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", @@ -6135,6 +7826,12 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, "pretty-format": { "version": "24.8.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.8.0.tgz", @@ -6172,6 +7869,12 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "promise-queue": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/promise-queue/-/promise-queue-2.2.5.tgz", + "integrity": "sha1-L29ffA9tCBCelnZZx5uIqe1ek7Q=", + "dev": true + }, "prompts": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.1.0.tgz", @@ -6182,6 +7885,12 @@ "sisteransi": "^1.0.0" } }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "dev": true + }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -6190,8 +7899,7 @@ "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "optional": true + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, "psl": { "version": "1.1.31", @@ -6212,6 +7920,16 @@ "safe-buffer": "^5.1.2" } }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -6226,8 +7944,7 @@ "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "optional": true + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" }, "querystring-es3": { "version": "0.2.1", @@ -6253,6 +7970,39 @@ "safe-buffer": "^5.1.0" } }, + "raven": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/raven/-/raven-1.2.1.tgz", + "integrity": "sha1-lJwTTbAooZC3u/j3kKrlQbfAIL0=", + "dev": true, + "requires": { + "cookie": "0.3.1", + "json-stringify-safe": "5.0.1", + "lsmod": "1.0.0", + "stack-trace": "0.0.9", + "uuid": "3.0.0" + }, + "dependencies": { + "uuid": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz", + "integrity": "sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg=", + "dev": true + } + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, "react-is": { "version": "16.8.6", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", @@ -6329,6 +8079,25 @@ "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", "dev": true }, + "registry-auth-token": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", + "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", + "dev": true, + "requires": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", + "dev": true, + "requires": { + "rc": "^1.0.1" + } + }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -6344,6 +8113,12 @@ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, + "replaceall": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/replaceall/-/replaceall-0.1.6.tgz", + "integrity": "sha1-gdgax663LX9cSUKt8ml6MiBojY4=", + "dev": true + }, "request": { "version": "2.81.0", "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", @@ -6585,6 +8360,12 @@ "is-promise": "^2.1.0" } }, + "rx": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", + "integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=", + "dev": true + }, "rxjs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", @@ -6683,11 +8464,114 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, + "seek-bzip": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", + "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", + "dev": true, + "requires": { + "commander": "~2.8.1" + }, + "dependencies": { + "commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "dev": true, + "requires": { + "graceful-readlink": ">= 1.0.0" + } + } + } + }, "semver": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" }, + "semver-diff": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", + "dev": true, + "requires": { + "semver": "^5.0.3" + } + }, + "semver-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-1.0.0.tgz", + "integrity": "sha1-kqSWkGX5xwxpR1PVUkj8aPj2Usk=", + "dev": true + }, + "serverless": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/serverless/-/serverless-1.43.0.tgz", + "integrity": "sha512-3fQM7vwlUkJZbKpKFFKfDJYNkD1c5UxiAA+JWHY2JiD4Rkn5RVX89ToeR5+rzsT9+ewr2WJ9TqTVM+pd9WjOvA==", + "dev": true, + "requires": { + "archiver": "^1.1.0", + "async": "^1.5.2", + "aws-sdk": "^2.430.0", + "bluebird": "^3.5.0", + "cachedir": "^2.2.0", + "chalk": "^2.0.0", + "ci-info": "^1.1.1", + "download": "^5.0.2", + "fast-levenshtein": "^2.0.6", + "filesize": "^3.3.0", + "fs-extra": "^0.26.7", + "get-stdin": "^5.0.1", + "globby": "^6.1.0", + "graceful-fs": "^4.1.11", + "https-proxy-agent": "^2.2.1", + "is-docker": "^1.1.0", + "js-yaml": "^3.13.0", + "json-cycle": "^1.3.0", + "json-refs": "^2.1.5", + "jszip": "^3.2.1", + "jwt-decode": "^2.2.0", + "lodash": "^4.13.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "moment": "^2.13.0", + "nanomatch": "^1.2.13", + "node-fetch": "^1.6.0", + "object-hash": "^1.2.0", + "promise-queue": "^2.2.3", + "raven": "^1.2.1", + "rc": "^1.1.6", + "replaceall": "^0.1.6", + "semver": "^5.7.0", + "semver-regex": "^1.0.0", + "tabtab": "^2.2.2", + "untildify": "^3.0.3", + "update-notifier": "^2.2.0", + "uuid": "^2.0.2", + "write-file-atomic": "^2.1.0", + "yaml-ast-parser": "0.0.34" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "dev": true + }, + "uuid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", + "dev": true + } + } + }, "serverless-webpack": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/serverless-webpack/-/serverless-webpack-4.4.0.tgz", @@ -6794,6 +8678,12 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", + "dev": true + }, "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", @@ -7027,6 +8917,16 @@ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" }, + "spawn-sync": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz", + "integrity": "sha1-sAeZVX63+wyDdsKdROih6mfldHY=", + "dev": true, + "requires": { + "concat-stream": "^1.4.7", + "os-shim": "^0.1.2" + } + }, "spdx-correct": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", @@ -7085,6 +8985,12 @@ "tweetnacl": "~0.14.0" } }, + "stack-trace": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz", + "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU=", + "dev": true + }, "stack-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", @@ -7254,6 +9160,15 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" }, + "strip-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "dev": true, + "requires": { + "is-natural-number": "^4.0.1" + } + }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -7264,6 +9179,50 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, + "strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, + "superagent": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", + "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", + "dev": true, + "requires": { + "component-emitter": "^1.2.0", + "cookiejar": "^2.1.0", + "debug": "^3.1.0", + "extend": "^3.0.0", + "form-data": "^2.3.1", + "formidable": "^1.2.0", + "methods": "^1.1.1", + "mime": "^1.4.1", + "qs": "^6.5.1", + "readable-stream": "^2.3.5" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -7319,6 +9278,22 @@ } } }, + "tabtab": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tabtab/-/tabtab-2.2.2.tgz", + "integrity": "sha1-egR/FDsBC0y9MfhX6ClhUSy/ThQ=", + "dev": true, + "requires": { + "debug": "^2.2.0", + "inquirer": "^1.0.2", + "lodash.difference": "^4.5.0", + "lodash.uniq": "^4.5.0", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "npmlog": "^2.0.3", + "object-assign": "^4.1.0" + } + }, "tapable": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.9.tgz", @@ -7328,7 +9303,6 @@ "version": "1.6.2", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", - "optional": true, "requires": { "bl": "^1.0.0", "buffer-alloc": "^1.2.0", @@ -7339,6 +9313,15 @@ "xtend": "^4.0.0" } }, + "term-size": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", + "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", + "dev": true, + "requires": { + "execa": "^0.7.0" + } + }, "test-exclude": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", @@ -7478,6 +9461,12 @@ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", + "dev": true + }, "timers-browserify": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", @@ -7511,8 +9500,7 @@ "to-buffer": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", - "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==", - "optional": true + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" }, "to-fast-properties": { "version": "2.0.0", @@ -7573,6 +9561,15 @@ "punycode": "^2.1.0" } }, + "trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, "trim-right": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", @@ -7713,6 +9710,12 @@ "prelude-ls": "~1.1.2" } }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, "typescript": { "version": "3.4.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.5.tgz", @@ -7775,6 +9778,16 @@ } } }, + "unbzip2-stream": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz", + "integrity": "sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg==", + "dev": true, + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, "underscore": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", @@ -7812,6 +9825,15 @@ } } }, + "unique-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", + "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "dev": true, + "requires": { + "crypto-random-string": "^1.0.0" + } + }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -7854,12 +9876,59 @@ } } }, + "untildify": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-3.0.3.tgz", + "integrity": "sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA==", + "dev": true + }, + "unzip-response": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", + "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", + "dev": true + }, "upath": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==", "optional": true }, + "update-notifier": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", + "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", + "dev": true, + "requires": { + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" + }, + "dependencies": { + "ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "dev": true + }, + "is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "dev": true, + "requires": { + "ci-info": "^1.5.0" + } + } + } + }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", @@ -7891,6 +9960,21 @@ } } }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "dev": true, + "requires": { + "prepend-http": "^1.0.1" + } + }, + "url-to-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=", + "dev": true + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -7971,6 +10055,12 @@ "browser-process-hrtime": "^0.1.2" } }, + "walkdir": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.0.11.tgz", + "integrity": "sha1-oW0CXrkxvQO1LzCMrtD0D86+lTI=", + "dev": true + }, "walker": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", @@ -8111,6 +10201,15 @@ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, + "widest-line": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", + "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", + "dev": true, + "requires": { + "string-width": "^2.1.1" + } + }, "window-size": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", @@ -8185,6 +10284,12 @@ "async-limiter": "~1.0.0" } }, + "xdg-basedir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", + "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", + "dev": true + }, "xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", @@ -8218,8 +10323,7 @@ "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "optional": true + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" }, "y18n": { "version": "3.2.1", @@ -8229,8 +10333,13 @@ "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "optional": true + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + }, + "yaml-ast-parser": { + "version": "0.0.34", + "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.34.tgz", + "integrity": "sha1-0A88+ddztyQUCa6SpnQNHbGfSeY=", + "dev": true }, "yargs": { "version": "8.0.2", @@ -8311,11 +10420,33 @@ } } }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "yn": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", "optional": true + }, + "zip-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-1.2.0.tgz", + "integrity": "sha1-qLxF9MG0lpnGuQGYuqyqzbzUugQ=", + "dev": true, + "requires": { + "archiver-utils": "^1.3.0", + "compress-commons": "^1.2.0", + "lodash": "^4.8.0", + "readable-stream": "^2.0.0" + } } } } diff --git a/package.json b/package.json index debd41f7..30eb685d 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "lint": "eslint src/**/*.ts", "lintfix": "eslint src/**/*.ts --fix", "pretest": "npm run lint", - "test": "jest --config jest.config.json", + "test": "jest --config jest.config.js", "prebuild": "rm -rf lib/ && npm test", "build": "tsc" }, @@ -40,6 +40,7 @@ }, "devDependencies": { "@types/jest": "^24.0.13", + "@types/lodash": "^4.14.130", "@types/open": "^6.1.0", "@types/request": "^2.48.1", "@types/serverless": "^1.18.2", @@ -48,6 +49,8 @@ "coveralls": "^3.0.3", "eslint": "^5.16.0", "jest": "^24.8.0", + "jest-cli": "^24.8.0", + "serverless": "^1.43.0", "ts-jest": "^24.0.2", "typescript": "^3.4.5" }, diff --git a/src/config.ts b/src/config.ts index ba5cf2c6..2d8a8bf5 100644 --- a/src/config.ts +++ b/src/config.ts @@ -13,7 +13,7 @@ export const constants = { scmCommandApiPath: '/api/command', scmDomain: '.scm.azurewebsites.net', scmVfsPath: '/api/vfs/site/wwwroot/', - scmZipApiPath: '/api/zip/site/wwwroot/' + scmZipDeployApiPath: '/api/zipdeploy' }; export default constants; \ No newline at end of file diff --git a/src/index.test.ts b/src/index.test.ts deleted file mode 100644 index 8b3dc442..00000000 --- a/src/index.test.ts +++ /dev/null @@ -1,6 +0,0 @@ - -describe("Index test", () => { - it("should run a test", () => { - expect(true).toBe(true); - }); -}); \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index e262c764..538f2c1c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,15 +4,13 @@ This way only one plugin needs to be added to the service in order to get access whole provider implementation. */ -import * as Serverless from 'serverless'; +import Serverless from 'serverless'; import AzureProvider from './provider/azureProvider'; import { AzureInvoke } from './plugins/invoke/azureInvoke'; import { AzureLogs } from './plugins/logs/azureLogs'; import { AzureRemove } from './plugins/remove/azureRemove'; import { AzurePackage } from './plugins/package/azurePackage'; -import { AzurePackageFunction } from './plugins/package/azurePackageFunction'; import { AzureDeployPlugin } from './plugins/deploy/azureDeployPlugin'; -import { AzureDeployFunctionPlugin } from './plugins/deploy/azureDeployFunctionPlugin'; import { AzureLoginPlugin } from './plugins/login/loginPlugin'; import { AzureApimServicePlugin } from './plugins/apim/apimServicePlugin'; import { AzureApimFunctionPlugin } from './plugins/apim/apimFunctionPlugin'; @@ -23,14 +21,12 @@ export class AzureIndex { // To be refactored this.serverless.pluginManager.addPlugin(AzurePackage); - this.serverless.pluginManager.addPlugin(AzurePackageFunction); this.serverless.pluginManager.addPlugin(AzureInvoke); this.serverless.pluginManager.addPlugin(AzureLogs); this.serverless.pluginManager.addPlugin(AzureRemove); // Refactored this.serverless.pluginManager.addPlugin(AzureLoginPlugin); this.serverless.pluginManager.addPlugin(AzureDeployPlugin); - this.serverless.pluginManager.addPlugin(AzureDeployFunctionPlugin); this.serverless.pluginManager.addPlugin(AzureApimServicePlugin); this.serverless.pluginManager.addPlugin(AzureApimFunctionPlugin); } diff --git a/src/plugins/apim/apimFunctionPlugin.ts b/src/plugins/apim/apimFunctionPlugin.ts index c96acba2..c4dcd6f0 100644 --- a/src/plugins/apim/apimFunctionPlugin.ts +++ b/src/plugins/apim/apimFunctionPlugin.ts @@ -1,4 +1,4 @@ -import * as Serverless from 'serverless'; +import Serverless from 'serverless'; import { ApimService } from '../../services/apimService'; export class AzureApimFunctionPlugin { diff --git a/src/plugins/apim/apimServicePlugin.test.ts b/src/plugins/apim/apimServicePlugin.test.ts new file mode 100644 index 00000000..a4075947 --- /dev/null +++ b/src/plugins/apim/apimServicePlugin.test.ts @@ -0,0 +1,19 @@ +import Serverless from 'serverless'; +import { AzureApimServicePlugin } from './apimServicePlugin'; + +describe('APIM Service Plugin', () => { + it('is defined', () => { + expect(AzureApimServicePlugin).toBeDefined(); + }); + + it('can be instantiated', () => { + const serverless = new Serverless(); + const options: Serverless.Options = { + stage: '', + region: '', + } + const plugin = new AzureApimServicePlugin(serverless, options); + + expect(plugin).not.toBeNull(); + }); +}); \ No newline at end of file diff --git a/src/plugins/apim/apimServicePlugin.ts b/src/plugins/apim/apimServicePlugin.ts index 7cecb977..a5255f65 100644 --- a/src/plugins/apim/apimServicePlugin.ts +++ b/src/plugins/apim/apimServicePlugin.ts @@ -1,4 +1,4 @@ -import * as Serverless from 'serverless'; +import Serverless from 'serverless'; import { ApimService } from '../../services/apimService'; export class AzureApimServicePlugin { @@ -11,6 +11,11 @@ export class AzureApimServicePlugin { } private async deploy() { + const apimConfig = this.serverless.service.provider['apim']; + if (!apimConfig) { + return Promise.resolve(); + } + this.serverless.cli.log('Starting APIM service deployment'); const apimService = new ApimService(this.serverless, this.options); diff --git a/src/plugins/deploy/azureDeployFunctionPlugin.ts b/src/plugins/deploy/azureDeployFunctionPlugin.ts deleted file mode 100644 index 6886f22a..00000000 --- a/src/plugins/deploy/azureDeployFunctionPlugin.ts +++ /dev/null @@ -1,27 +0,0 @@ -import * as Serverless from 'serverless'; -import { FunctionAppService } from '../../services/functionAppService'; - -export class AzureDeployFunctionPlugin { - public hooks: { [eventName: string]: Promise }; - - constructor(private serverless: Serverless, private options: Serverless.Options) { - this.hooks = { - 'deploy:function:packageFunction': this.beforeDeploy.bind(this), - 'deploy:function:deploy': this.deploy.bind(this) - }; - } - - private async beforeDeploy() { - // Spawn 'package:function' to create the single-function zip artifact - this.serverless.pluginManager.spawn('package:function'); - } - - private async deploy() { - const functionAppService = new FunctionAppService(this.serverless, this.options); - const functionApp = await functionAppService.get(); - - await functionAppService.deleteFunction(this.options.function); - await functionAppService.uploadFunction(functionApp, this.options.function); - await functionAppService.syncTriggers(functionApp); - } -} diff --git a/src/plugins/deploy/azureDeployPlugin.test.ts b/src/plugins/deploy/azureDeployPlugin.test.ts new file mode 100644 index 00000000..a14cc593 --- /dev/null +++ b/src/plugins/deploy/azureDeployPlugin.test.ts @@ -0,0 +1,32 @@ +import { MockFactory } from "../../test/mockFactory"; +import { invokeHook } from "../../test/utils"; +import { AzureDeployPlugin } from "./azureDeployPlugin"; + +jest.mock("../../services/resourceService"); +jest.mock("../../services/functionAppService"); +import { FunctionAppService } from "../../services/functionAppService"; +import { ResourceService } from "../../services/resourceService"; + +describe('Deploy plugin', () => { + + it('calls deploy hook', async () => { + const deployResourceGroup = jest.fn(); + const functionAppStub = "Function App Stub"; + const deploy = jest.fn(() => Promise.resolve(functionAppStub)); + const uploadFunctions = jest.fn(); + + ResourceService.prototype.deployResourceGroup = deployResourceGroup + FunctionAppService.prototype.deploy = deploy + FunctionAppService.prototype.uploadFunctions = uploadFunctions + + const sls = MockFactory.createTestServerless(); + const options = MockFactory.createTestServerlessOptions(); + const plugin = new AzureDeployPlugin(sls, options); + + await invokeHook(plugin, 'deploy:deploy'); + + expect(deployResourceGroup).toBeCalled(); + expect(deploy).toBeCalled(); + expect(uploadFunctions).toBeCalledWith(functionAppStub); + }); +}); \ No newline at end of file diff --git a/src/plugins/deploy/azureDeployPlugin.ts b/src/plugins/deploy/azureDeployPlugin.ts index 56925a43..56d5803a 100644 --- a/src/plugins/deploy/azureDeployPlugin.ts +++ b/src/plugins/deploy/azureDeployPlugin.ts @@ -1,4 +1,4 @@ -import * as Serverless from 'serverless'; +import Serverless from 'serverless'; import { ResourceService } from '../../services/resourceService'; import { FunctionAppService } from '../../services/functionAppService'; @@ -7,28 +7,17 @@ export class AzureDeployPlugin { constructor(private serverless: Serverless, private options: Serverless.Options) { this.hooks = { - 'before:deploy:deploy': this.beforeDeploy.bind(this), 'deploy:deploy': this.deploy.bind(this) }; } - private async beforeDeploy() { - const functionAppService = new FunctionAppService(this.serverless, this.options); - const functionApp = await functionAppService.get(); - - if (functionApp) { - await functionAppService.cleanUp(functionApp); - } - } - private async deploy() { const resourceService = new ResourceService(this.serverless, this.options); await resourceService.deployResourceGroup(); const functionAppService = new FunctionAppService(this.serverless, this.options); - const functionApp = await functionAppService.deploy(); + const functionApp = await functionAppService.deploy(); await functionAppService.uploadFunctions(functionApp); - await functionAppService.syncTriggers(functionApp); } } diff --git a/src/plugins/invoke/azureInvoke.ts b/src/plugins/invoke/azureInvoke.ts index 3cb44766..a672e345 100644 --- a/src/plugins/invoke/azureInvoke.ts +++ b/src/plugins/invoke/azureInvoke.ts @@ -1,12 +1,10 @@ -import * as Serverless from 'serverless'; -import { getAdminKey } from '../../shared/getAdminKey'; +import Serverless from 'serverless'; import { join, isAbsolute } from 'path'; import AzureProvider from '../../provider/azureProvider'; export class AzureInvoke { public hooks: { [eventName: string]: Promise }; private provider: AzureProvider; - private getAdminKey = getAdminKey; constructor(private serverless: Serverless, private options: Serverless.Options) { this.provider = (this.serverless.getProvider('azure') as any) as AzureProvider; @@ -24,7 +22,7 @@ export class AzureInvoke { } this.hooks = { - 'before:invoke:invoke': this.getAdminKey.bind(this), + 'before:invoke:invoke': this.provider.getAdminKey.bind(this), 'invoke:invoke': this.invoke.bind(this) }; } diff --git a/src/plugins/login/loginPlugin.test.ts b/src/plugins/login/loginPlugin.test.ts new file mode 100644 index 00000000..86f9eea4 --- /dev/null +++ b/src/plugins/login/loginPlugin.test.ts @@ -0,0 +1,123 @@ +import { MockFactory } from "../../test/mockFactory"; +import { invokeHook } from "../../test/utils"; +import { AzureLoginPlugin } from "./loginPlugin"; +import { AzureLoginService } from "../../services/loginService"; + +describe('Login Plugin', () => { + + const authResponse = MockFactory.createTestAuthResponse(); + + it('returns if azure credentials are set', async () => { + const interactiveLogin = jest.fn(() => Promise.resolve(authResponse)); + const servicePrincipalLogin = jest.fn(() => Promise.resolve(authResponse)); + + AzureLoginService.interactiveLogin = interactiveLogin; + AzureLoginService.servicePrincipalLogin = servicePrincipalLogin; + + const sls = MockFactory.createTestServerless(); + sls.variables['azureCredentials'] = 'credentials'; + const options = MockFactory.createTestServerlessOptions(); + const plugin = new AzureLoginPlugin(sls, options); + + await invokeHook(plugin, 'before:package:initialize'); + + expect(interactiveLogin).not.toBeCalled(); + expect(servicePrincipalLogin).not.toBeCalled(); + }); + + it('calls login if azure credentials are not set', async () => { + const interactiveLogin = jest.fn(() => Promise.resolve(authResponse)); + const servicePrincipalLogin = jest.fn(() => Promise.resolve(authResponse)); + + AzureLoginService.interactiveLogin = interactiveLogin; + AzureLoginService.servicePrincipalLogin = servicePrincipalLogin; + + const sls = MockFactory.createTestServerless(); + const options = MockFactory.createTestServerlessOptions(); + const plugin = new AzureLoginPlugin(sls, options); + + await invokeHook(plugin, 'before:package:initialize'); + + expect(interactiveLogin).toBeCalled(); + expect(servicePrincipalLogin).not.toBeCalled(); + }); + + it('calls service principal login if environment variables are set', async () => { + + process.env.azureSubId = 'azureSubId'; + process.env.azureServicePrincipalClientId = 'azureServicePrincipalClientId'; + process.env.azureServicePrincipalPassword = 'azureServicePrincipalPassword'; + process.env.azureServicePrincipalTenantId = 'azureServicePrincipalTenantId'; + + const interactiveLogin = jest.fn(() => Promise.resolve(authResponse)); + const servicePrincipalLogin = jest.fn(() => Promise.resolve(authResponse)); + + AzureLoginService.interactiveLogin = interactiveLogin; + AzureLoginService.servicePrincipalLogin = servicePrincipalLogin; + + const sls = MockFactory.createTestServerless(); + const options = MockFactory.createTestServerlessOptions(); + const plugin = new AzureLoginPlugin(sls, options); + await invokeHook(plugin, 'before:package:initialize'); + expect(servicePrincipalLogin).toBeCalledWith( + 'azureServicePrincipalClientId', + 'azureServicePrincipalPassword', + 'azureServicePrincipalTenantId' + ) + expect(interactiveLogin).not.toBeCalled(); + + expect(sls.variables['azureCredentials']).toEqual(authResponse.credentials); + expect(sls.variables['subscriptionId']).toEqual('azureSubId'); + }); + + it('calls interactive login if environment variables are not set', async () => { + delete process.env.azureSubId; + delete process.env.azureServicePrincipalClientId; + delete process.env.azureServicePrincipalPassword; + delete process.env.azureServicePrincipalTenantId; + + const interactiveLogin = jest.fn(() => Promise.resolve(authResponse)); + const servicePrincipalLogin = jest.fn(() => Promise.resolve(authResponse)); + + AzureLoginService.interactiveLogin = interactiveLogin; + AzureLoginService.servicePrincipalLogin = servicePrincipalLogin; + + const sls = MockFactory.createTestServerless(); + const options = MockFactory.createTestServerlessOptions(); + const plugin = new AzureLoginPlugin(sls, options); + await invokeHook(plugin, 'before:package:initialize'); + expect(servicePrincipalLogin).not.toBeCalled(); + expect(interactiveLogin).toBeCalled(); + + expect(sls.variables['azureCredentials']).toEqual(authResponse.credentials); + expect(sls.variables['subscriptionId']).toEqual('azureSubId'); + }); + + it('logs an error from authentication', async () => { + process.env.azureSubId = 'azureSubId'; + process.env.azureServicePrincipalClientId = 'azureServicePrincipalClientId'; + process.env.azureServicePrincipalPassword = 'azureServicePrincipalPassword'; + process.env.azureServicePrincipalTenantId = 'azureServicePrincipalTenantId'; + + const interactiveLogin = jest.fn(() => Promise.resolve(authResponse)); + const errorMessage = 'This is my error message'; + const servicePrincipalLogin = jest.fn(() => { + throw new Error(errorMessage); + }); + + AzureLoginService.interactiveLogin = interactiveLogin; + AzureLoginService.servicePrincipalLogin = servicePrincipalLogin; + + const sls = MockFactory.createTestServerless(); + const options = MockFactory.createTestServerlessOptions(); + const plugin = new AzureLoginPlugin(sls, options); + await invokeHook(plugin, 'before:package:initialize'); + expect(servicePrincipalLogin).toBeCalledWith( + 'azureServicePrincipalClientId', + 'azureServicePrincipalPassword', + 'azureServicePrincipalTenantId' + ) + expect(interactiveLogin).not.toBeCalled(); + expect(sls.cli.log).lastCalledWith(`Error: ${errorMessage}`) + }); +}) \ No newline at end of file diff --git a/src/plugins/login/loginPlugin.ts b/src/plugins/login/loginPlugin.ts index 0d645468..5b22cc7f 100644 --- a/src/plugins/login/loginPlugin.ts +++ b/src/plugins/login/loginPlugin.ts @@ -1,7 +1,7 @@ -import * as open from 'open'; -import { interactiveLoginWithAuthResponse, loginWithServicePrincipalSecretWithAuthResponse } from '@azure/ms-rest-nodeauth'; -import * as Serverless from 'serverless'; +import open from 'open'; +import Serverless from 'serverless'; import AzureProvider from '../../provider/azureProvider'; +import { AzureLoginService } from '../../services/loginService' export class AzureLoginPlugin { private provider: AzureProvider; @@ -23,32 +23,17 @@ export class AzureLoginPlugin { this.serverless.cli.log('Logging into Azure'); - let authResult = null; - - const subscriptionId = process.env.azureSubId; - const clientId = process.env.azureServicePrincipalClientId; - const secret = process.env.azureServicePrincipalPassword; - const tenantId = process.env.azureServicePrincipalTenantId; - try { - if (subscriptionId && clientId && secret && tenantId) { - authResult = await loginWithServicePrincipalSecretWithAuthResponse(clientId, secret, tenantId); - } - else { - await open('https://microsoft.com/devicelogin'); - authResult = await interactiveLoginWithAuthResponse(); - } - - // TODO: This is temporary until the azure provider goes away - this.provider.credentials = authResult.credentials; - - this.serverless.variables['azureAccessToken'] = authResult.credentials.tokenCache._entries[0].accessToken; + const authResult = await AzureLoginService.login(); this.serverless.variables['azureCredentials'] = authResult.credentials; - this.serverless.variables['subscriptionId'] = authResult.subscriptionId || subscriptionId; + console.log(this.serverless.variables['azureCredentials']) + // Use environment variable for sub ID or use the first subscription in the list (service principal can + // have access to more than one subscription) + this.serverless.variables['subscriptionId'] = process.env.azureSubId || authResult.subscriptions[0].id; } catch (e) { this.serverless.cli.log('Error logging into azure'); - this.serverless.cli.log(e); + this.serverless.cli.log(`${e}`); } } } \ No newline at end of file diff --git a/src/plugins/logs/azureLogs.ts b/src/plugins/logs/azureLogs.ts index 8634fdec..86c522ec 100644 --- a/src/plugins/logs/azureLogs.ts +++ b/src/plugins/logs/azureLogs.ts @@ -1,4 +1,4 @@ -import * as Serverless from 'serverless'; +import Serverless from 'serverless'; import AzureProvider from '../../provider/azureProvider'; export class AzureLogs { diff --git a/src/plugins/package/azurePackage.ts b/src/plugins/package/azurePackage.ts index 6b39a112..7d44762d 100644 --- a/src/plugins/package/azurePackage.ts +++ b/src/plugins/package/azurePackage.ts @@ -1,7 +1,5 @@ -import { existsSync, renameSync, statSync } from 'fs'; -import { join } from "path"; -import * as Serverless from 'serverless'; +import Serverless from 'serverless'; import AzureProvider from '../../provider/azureProvider'; import { createEventsBindings } from '../../shared/bindings'; import { getFunctionMetaData } from '../../shared/utils'; @@ -13,7 +11,6 @@ export class AzurePackage { constructor(private serverless: Serverless, private options: Serverless.Options) { this.hooks = { 'package:setupProviderConfiguration': this.setupProviderConfiguration.bind(this), - 'before:webpack:package:packageModules': this.webpackFunctionJson.bind(this) }; } @@ -29,36 +26,4 @@ export class AzurePackage { return Promise.all(createEventsPromises); } - - private async webpackFunctionJson(): Promise { - if (existsSync('.webpack')) { - const webpackJsonPromises = this.serverless.service.getAllFunctions() - .map((functionName) => this.moveJsonFile(functionName)); - - return Promise.all(webpackJsonPromises); - } - - return Promise.resolve(); - } - - private async moveJsonFile(functionName): Promise { - const dirPath = join(this.serverless.config.servicePath, '.webpack', functionName); - const jsonFileName = `${functionName}-function.json`; - const jsonFileSrcPath = join(this.serverless.config.servicePath, jsonFileName); - const jsonFileDestPath = join(dirPath, jsonFileName); - - return new Promise((resolve) => { - if (existsSync(dirPath) && statSync(dirPath).isDirectory()) { - if (existsSync(jsonFileSrcPath)) { - this.serverless.cli.log(`Moving ${jsonFileName} to .webpack directory.`); - renameSync(jsonFileSrcPath, jsonFileDestPath); - } - else { - this.serverless.cli.log(`Warning: No generated ${functionName}-function.json file was found. It will not be included in the package.`); - } - } - - resolve(); - }); - } } diff --git a/src/plugins/package/azurePackageFunction.ts b/src/plugins/package/azurePackageFunction.ts deleted file mode 100644 index e0b09260..00000000 --- a/src/plugins/package/azurePackageFunction.ts +++ /dev/null @@ -1,26 +0,0 @@ - -import * as Serverless from 'serverless'; -import { createEventsBindings } from '../../shared/bindings'; -import { getFunctionMetaData } from '../../shared/utils'; - -export class AzurePackageFunction { - public hooks: { [eventName: string]: Promise }; - - constructor(private serverless: Serverless, private options: Serverless.Options) { - this.hooks = { - 'before:deploy:function:packageFunction': this.packageFunction.bind(this) - }; - } - - private async packageFunction() { - this.serverless.cli.log('Building Azure Events Hooks'); - await this.compileEventsForFunction(); - } - - private async compileEventsForFunction(): Promise { - const functionName = this.options.function; - const metaData = getFunctionMetaData(functionName, this.serverless); - - return createEventsBindings(this.serverless.config.servicePath, functionName, metaData); - } -} diff --git a/src/plugins/remove/azureRemove.ts b/src/plugins/remove/azureRemove.ts index 40cc2665..f9bf78d6 100644 --- a/src/plugins/remove/azureRemove.ts +++ b/src/plugins/remove/azureRemove.ts @@ -1,4 +1,4 @@ -import * as Serverless from 'serverless'; +import Serverless from 'serverless'; import { ResourceService } from '../../services/resourceService'; export class AzureRemove { diff --git a/src/provider/armTemplates/azuredeploy.json b/src/provider/armTemplates/azuredeploy.json index 023ebf55..32df123f 100644 --- a/src/provider/armTemplates/azuredeploy.json +++ b/src/provider/armTemplates/azuredeploy.json @@ -103,6 +103,10 @@ { "name": "WEBSITE_NODE_DEFAULT_VERSION", "value": "8.11.1" + }, + { + "name": "WEBSITE_RUN_FROM_PACKAGE", + "value": "1" } ] } diff --git a/src/provider/azureProvider.ts b/src/provider/azureProvider.ts index 1db46850..a3d752a3 100644 --- a/src/provider/azureProvider.ts +++ b/src/provider/azureProvider.ts @@ -1,7 +1,7 @@ -import * as fs from 'fs'; +import fs from 'fs'; import { join } from 'path'; -import * as request from 'request'; -import * as Serverless from 'serverless'; +import request from 'request'; +import Serverless from 'serverless'; import config from '../config'; let functionAppName; diff --git a/src/services/apimService.ts b/src/services/apimService.ts index ceda0a91..1e0bc364 100644 --- a/src/services/apimService.ts +++ b/src/services/apimService.ts @@ -1,4 +1,4 @@ -import * as Serverless from 'serverless'; +import Serverless from 'serverless'; import { ApiManagementClient } from '@azure/arm-apimanagement'; import { FunctionAppService } from './functionAppService'; import { BaseService } from './baseService'; @@ -22,11 +22,7 @@ export class ApimService extends BaseService { /** * Deploys the APIM top level api */ - async deployApi() { - if (!this.config) { - return; - } - + public async deployApi() { const functionApp = await this.functionAppService.get(); await this.ensureApi(); @@ -37,12 +33,8 @@ export class ApimService extends BaseService { /** * Deploys all the functions of the serverless service to APIM */ - async deployFunctions() { - if (!this.config) { - return; - } - - this.serverless.cli.log('Starting to deploy API Operations'); + public async deployFunctions() { + this.serverless.cli.log('-> Deploying API Operations'); const deployApiTasks = this.serverless.service .getAllFunctions() @@ -55,7 +47,7 @@ export class ApimService extends BaseService { * Deploys the specified serverless function to APIM * @param options */ - async deployFunction(options) { + public async deployFunction(options) { const functionConfig = this.serverless.service['functions'][options.function]; if (!functionConfig.apim) { @@ -75,7 +67,9 @@ export class ApimService extends BaseService { /** * Deploys the APIM API referenced by the serverless service */ - async ensureApi() { + private async ensureApi() { + this.serverless.cli.log('-> Deploying API') + try { await this.apimClient.api.createOrUpdate(this.resourceGroup, this.config.resourceId, this.config.name, { isCurrent: true, @@ -96,7 +90,8 @@ export class ApimService extends BaseService { * Deploys the APIM Backend referenced by the serverless service * @param functionAppUrl The host name for the deployed function app */ - async ensureBackend(functionApp) { + private async ensureBackend(functionApp) { + this.serverless.cli.log('-> Deploying API Backend') try { const functionAppResourceId = `https://management.azure.com${functionApp.id}`; @@ -122,8 +117,8 @@ export class ApimService extends BaseService { * @param serverless The serverless framework * @param options The plugin options */ - async deployOperation(options) { - this.serverless.cli.log(`-> Deploying API operation ${options.function}`); + private async deployOperation(options) { + this.serverless.cli.log(`--> Deploying API operation ${options.function}`); try { const client = new ApiManagementClient(this.credentials, this.subscriptionId); @@ -167,7 +162,8 @@ export class ApimService extends BaseService { * Gets the master key for the function app and stores a reference in the APIM instance * @param functionAppUrl The host name for the Azure function app */ - async ensureFunctionAppKeys(functionApp) { + private async ensureFunctionAppKeys(functionApp) { + this.serverless.cli.log('-> Deploying API keys') try { const masterKey = await this.functionAppService.getMasterKey(functionApp); const keyName = `${this.serviceName}-key`; diff --git a/src/services/baseService.ts b/src/services/baseService.ts index 5c517f0b..cb7d9761 100644 --- a/src/services/baseService.ts +++ b/src/services/baseService.ts @@ -1,5 +1,7 @@ -import * as Serverless from 'serverless'; +import Serverless from 'serverless'; import axios from 'axios'; +import request from 'request' +import fs from 'fs'; export abstract class BaseService { protected baseUrl: string; @@ -63,4 +65,22 @@ export abstract class BaseService { }, interval); }); } + + /** + * Uploads the specified file via HTTP request + * @param requestOptions The HTTP request options + * @param filePath The local file path + */ + protected sendFile(requestOptions, filePath) { + return new Promise((resolve, reject) => { + fs.createReadStream(filePath) + .pipe(request(requestOptions, (err, response) => { + if (err) { + this.serverless.cli.log(JSON.stringify(err, null, 4)); + return reject(err); + } + resolve(response); + })); + }); + } } \ No newline at end of file diff --git a/src/services/functionAppService.ts b/src/services/functionAppService.ts index 5f38c2d1..f40e6204 100644 --- a/src/services/functionAppService.ts +++ b/src/services/functionAppService.ts @@ -1,13 +1,13 @@ +import fs from 'fs'; +import path from 'path'; import { WebSiteManagementClient } from '@azure/arm-appservice'; import { ResourceManagementClient } from '@azure/arm-resources'; import { Deployment } from '@azure/arm-resources/esm/models'; -import * as fs from 'fs'; import jsonpath from 'jsonpath'; -import * as _ from 'lodash'; -import * as path from 'path'; -import * as Serverless from 'serverless'; +import _ from 'lodash'; +import Serverless from 'serverless'; import { BaseService } from './baseService'; -import * as request from 'request'; +import { constants } from '../config'; export class FunctionAppService extends BaseService { private resourceClient: ResourceManagementClient; @@ -20,7 +20,7 @@ export class FunctionAppService extends BaseService { this.webClient = new WebSiteManagementClient(this.credentials, this.subscriptionId); } - async get() { + public async get() { const response: any = await this.webClient.webApps.get(this.resourceGroup, this.serviceName); if (response.error && (response.error.code === 'ResourceNotFound' || response.error.code === 'ResourceGroupNotFound')) { return null; @@ -29,26 +29,34 @@ export class FunctionAppService extends BaseService { return response; } - async getMasterKey(functionApp) { + public async getMasterKey(functionApp) { functionApp = functionApp || await this.get(); - const adminToken = await this._getAuthKey(functionApp); + const adminToken = await this.getAuthKey(functionApp); + const keyUrl = `https://${functionApp.defaultHostName}/admin/host/systemkeys/_master`; - return await this._getMasterKey(functionApp.defaultHostName, adminToken); + const response = await this.sendApiRequest('GET', keyUrl, { + json: true, + headers: { + 'Authorization': `Bearer ${adminToken}` + } + }); + + return response.data.value; } - async deleteFunction(functionName) { + public async deleteFunction(functionName) { this.serverless.cli.log(`-> Deleting function: ${functionName}`); return await this.webClient.webApps.deleteFunction(this.resourceGroup, this.serviceName, functionName); } - async syncTriggers(functionApp) { + public async syncTriggers(functionApp) { this.serverless.cli.log('Syncing function triggers'); const syncTriggersUrl = `${this.baseUrl}${functionApp.id}/syncfunctiontriggers?api-version=2016-08-01`; await this.sendApiRequest('POST', syncTriggersUrl); } - async cleanUp(functionApp) { + public async cleanUp(functionApp) { this.serverless.cli.log('Cleaning up existing functions'); const deleteTasks = []; @@ -65,55 +73,57 @@ export class FunctionAppService extends BaseService { return await Promise.all(deleteTasks); } - async listFunctions(functionApp) { + public async listFunctions(functionApp) { const getTokenUrl = `${this.baseUrl}${functionApp.id}/functions?api-version=2016-08-01`; const response = await this.sendApiRequest('GET', getTokenUrl); return response.data.value || []; } - async uploadFunctions(functionApp) { - this.serverless.cli.log('Creating azure functions'); - - // Perform additional operations per function - const serviceFunctions = this.serverless.service.getAllFunctions(); - const uploadTasks = serviceFunctions.map((functionName) => this.uploadFunction(functionApp, functionName)); - - return await Promise.all(uploadTasks); + public async uploadFunctions(functionApp) { + await this.zipDeploy(functionApp); } - async uploadFunction(functionApp, functionName) { - this.serverless.cli.log(`-> Creating function: ${functionName}`); - - const scmDomain = functionApp.enabledHostNames[0]; + private async zipDeploy(functionApp) { + const functionAppName = functionApp.name; + this.serverless.cli.log(`Deploying zip file to function app: ${functionAppName}`); // Upload function artifact if it exists, otherwise the full service is handled in 'uploadFunctions' method - const functionZipFile = this.serverless.service['functions'][functionName].package.artifact || this.serverless.service['artifact']; - if (functionZipFile) { - this.serverless.cli.log(`-> Uploading function package: ${functionName}`); - - const requestOptions = { - method: 'PUT', - uri: `https://${scmDomain}/api/zip/site/wwwroot/${functionName}/`, - json: true, - headers: { - Authorization: `Bearer ${this.credentials.tokenCache._entries[0].accessToken}`, - Accept: '*/*' - } - }; + const functionZipFile = this.serverless.service['artifact']; + if (!functionZipFile) { + throw new Error('No zip file found for function app'); + } - await this._sendFile(requestOptions, functionZipFile); - this.serverless.cli.log(`-> Function package uploaded successfully: ${functionName}`); + this.serverless.cli.log(`-> Uploading ${functionZipFile}`); - // Rename function json - const fromPath = path.join(functionName, `${functionName}-function.json`); - const toPath = path.join(functionName, 'function.json'); - const command = `mv ${fromPath} ${toPath}`; - await this._runKuduCommand(functionApp, command); + const uploadUrl = `https://${functionAppName}${constants.scmDomain}${constants.scmZipDeployApiPath}`; + this.serverless.cli.log(`-> Upload url: ${uploadUrl}`); + + // https://github.com/projectkudu/kudu/wiki/Deploying-from-a-zip-file-or-url + const requestOptions = { + method: 'POST', + uri: uploadUrl, + json: true, + headers: { + Authorization: `Bearer ${this.credentials.tokenCache._entries[0].accessToken}`, + Accept: '*/*', + ContentType: 'application/octet-stream', + } + }; + + try { + await this.sendFile(requestOptions, functionZipFile); + this.serverless.cli.log('-> Function package uploaded successfully'); + } catch (e) { + throw new Error(`Error uploading zip file:\n --> ${e}`); } } - async deploy() { + /** + * create all necessary resources as defined in src/provider/armTemplates + * resource-group, storage account, app service plan, and app service at the minimum + */ + public async deploy() { this.serverless.cli.log(`Creating function app: ${this.serviceName}`); let parameters: any = { functionAppName: { value: this.serviceName } }; @@ -181,48 +191,7 @@ export class FunctionAppService extends BaseService { return await this.get(); } - /** - * Uploads the specified file via HTTP request - * @param requestOptions The HTTP request options - * @param filePath The local file path - */ - _sendFile(requestOptions, filePath) { - return new Promise((resolve, reject) => { - fs.createReadStream(filePath) - .pipe(request(requestOptions, (err, response) => { - if (err) { - this.serverless.cli.log(JSON.stringify(err, null, 4)); - return reject(err); - } - resolve(response); - })); - }); - } - - _wait(timeout) { - return new Promise((resolve) => setTimeout(resolve, timeout)); - } - - _waitForCondition(predicate, interval = 2000) { - return new Promise((resolve, reject) => { - let retries = 0; - const id = setInterval(async () => { - if (retries >= 20) { - clearInterval(id); - return reject('Failed conditional check 20 times'); - } - - retries++; - const result = await predicate(); - if (result) { - clearInterval(id); - resolve(result); - } - }, interval); - }); - } - - async _runKuduCommand(functionApp, command) { + private async runKuduCommand(functionApp, command) { this.serverless.cli.log(`-> Running Kudu command ${command}...`); const scmDomain = functionApp.enabledHostNames[0]; @@ -248,28 +217,10 @@ export class FunctionAppService extends BaseService { /** * Gets a short lived admin token used to retrieve function keys */ - async _getAuthKey(functionApp) { + private async getAuthKey(functionApp) { const adminTokenUrl = `${this.baseUrl}${functionApp.id}/functions/admin/token?api-version=2016-08-01`; const response = await this.sendApiRequest('GET', adminTokenUrl); return response.data.replace(/"/g, ''); } - - /** - * Gets the master key for the specified function app - * @param functionAppUrl The function app url - * @param authToken The JWT access token used for authorization - */ - async _getMasterKey(functionAppUrl, authToken) { - const keyUrl = `https://${functionAppUrl}/admin/host/systemkeys/_master`; - - const response = await this.sendApiRequest('GET', keyUrl, { - json: true, - headers: { - 'Authorization': `Bearer ${authToken}` - } - }); - - return response.data.value; - } } diff --git a/src/services/loginService.ts b/src/services/loginService.ts new file mode 100644 index 00000000..92453f3f --- /dev/null +++ b/src/services/loginService.ts @@ -0,0 +1,27 @@ +import { interactiveLoginWithAuthResponse, loginWithServicePrincipalSecretWithAuthResponse } from '@azure/ms-rest-nodeauth'; +import { getEnvVariable } from '../shared/utils'; + +export class AzureLoginService { + + public static async login() { + const subscriptionId = process.env.azureSubId; + const clientId = process.env.azureServicePrincipalClientId; + const secret = process.env.azureServicePrincipalPassword; + const tenantId = process.env.azureServicePrincipalTenantId; + + if (subscriptionId && clientId && secret && tenantId) { + return await AzureLoginService.servicePrincipalLogin(clientId, secret, tenantId); + } else { + return await AzureLoginService.interactiveLogin(); + } + } + + public static async interactiveLogin() { + await open('https://microsoft.com/devicelogin'); + return await interactiveLoginWithAuthResponse(); + } + + public static async servicePrincipalLogin(clientId: string, secret: string, tenantId: string) { + return loginWithServicePrincipalSecretWithAuthResponse(clientId, secret, tenantId); + } +} \ No newline at end of file diff --git a/src/services/resourceService.ts b/src/services/resourceService.ts index c2f0436d..fa53d3f3 100644 --- a/src/services/resourceService.ts +++ b/src/services/resourceService.ts @@ -1,4 +1,4 @@ -import * as Serverless from 'serverless'; +import Serverless from 'serverless'; import { ResourceManagementClient } from '@azure/arm-resources'; import { BaseService } from './baseService'; @@ -11,7 +11,7 @@ export class ResourceService extends BaseService { this.resourceClient = new ResourceManagementClient(this.credentials, this.subscriptionId); } - async deployResourceGroup() { + public async deployResourceGroup() { this.serverless.cli.log(`Creating resource group: ${this.resourceGroup}`); const groupParameters = { @@ -21,12 +21,12 @@ export class ResourceService extends BaseService { return await this.resourceClient.resourceGroups.createOrUpdate(this.resourceGroup, groupParameters); } - async deleteDeployment() { + public async deleteDeployment() { this.serverless.cli.log(`Deleting deployment: ${this.deploymentName}`); return await this.resourceClient.deployments.deleteMethod(this.resourceGroup, this.deploymentName); } - async deleteResourceGroup() { + public async deleteResourceGroup() { this.serverless.cli.log(`Deleting resource group: ${this.resourceGroup}`); return await this.resourceClient.resourceGroups.deleteMethod(this.resourceGroup); } diff --git a/src/shared/binding.test.ts b/src/shared/binding.test.ts new file mode 100644 index 00000000..ea2c5758 --- /dev/null +++ b/src/shared/binding.test.ts @@ -0,0 +1,11 @@ +import { MockFactory } from '../test/mockFactory'; +import { getBindingsMetaData, createEventsBindings } from './bindings' + +describe('Bindings', () => { + it('should get bindings metadata from serverless', () => { + const sls = MockFactory.createTestServerless(); + expect(sls).not.toBeNull(); + const bindingsMetadata = getBindingsMetaData(sls); + expect(sls.cli.log).toBeCalledWith('Parsing Azure Functions Bindings.json...'); + }); +}); \ No newline at end of file diff --git a/src/shared/bindings.ts b/src/shared/bindings.ts index c288f8f9..a4c70c61 100644 --- a/src/shared/bindings.ts +++ b/src/shared/bindings.ts @@ -1,7 +1,7 @@ import { writeFileSync } from 'fs'; import { join } from 'path'; +import Serverless from 'serverless'; import { FunctionMetadata } from './utils'; - const bindingsJson = require('./bindings.json'); const constants = { @@ -12,7 +12,7 @@ const constants = { type: 'type' }; -export function getBindingsMetaData(serverless) { +export function getBindingsMetaData(serverless: Serverless) { const bindingDisplayNames = []; const bindingTypes = []; const bindingSettings = []; @@ -34,14 +34,12 @@ export function getBindingsMetaData(serverless) { bindingSettingsNames[bindingsIndex] = settingsNames; } - const parsedBindings = { + return { bindingDisplayNames: bindingDisplayNames, bindingTypes: bindingTypes, bindingSettings: bindingSettings, bindingSettingsNames: bindingSettingsNames }; - - return parsedBindings; } export async function createEventsBindings(servicePath: string, functionName: string, functionMetadata: FunctionMetadata): Promise { diff --git a/src/shared/getAdminKey.ts b/src/shared/getAdminKey.ts deleted file mode 100644 index 41568a79..00000000 --- a/src/shared/getAdminKey.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function getAdminKey() { - return this.provider.getAdminKey(); -} \ No newline at end of file diff --git a/src/test/mockFactory.ts b/src/test/mockFactory.ts new file mode 100644 index 00000000..ece8da37 --- /dev/null +++ b/src/test/mockFactory.ts @@ -0,0 +1,106 @@ +import Serverless from 'serverless'; +import Service from 'serverless/classes/Service'; +import Utils = require('serverless/classes/Utils'); +import PluginManager = require('serverless/classes/PluginManager'); +import { AuthResponse, TokenCredentialsBase, LinkedSubscription } from '@azure/ms-rest-nodeauth'; +import { TokenCredentials } from '@azure/ms-rest-js'; +import { AuthenticationContext } from 'adal-node'; + +export class MockFactory { + public static createTestServerless(config?: any): Serverless { + const sls = new Serverless(config); + sls.service = MockFactory.createTestService(); + sls.utils = MockFactory.createTestUtils(); + sls.cli = MockFactory.createTestCli(); + sls.pluginManager = MockFactory.createTestPluginManager(); + sls.variables = {}; + return sls; + } + + public static createTestServerlessOptions(): Serverless.Options { + return { + extraServicePath: null, + function: null, + noDeploy: null, + region: null, + stage: null, + watch: null + } + } + + public static createTestAuthResponse(): AuthResponse { + return { + credentials: 'credentials' as any as TokenCredentialsBase, + subscriptions: [ + { + id: 'azureSubId', + } + ] as any as LinkedSubscription[] + } + } + + private static createTestService(): Service { + return { + getAllFunctions: jest.fn(), + getFunction: jest.fn(), + getAllEventsInFunction: jest.fn(), + getAllFunctionsNames: jest.fn(), + getEventInFunction: jest.fn(), + getServiceName: jest.fn(), + load: jest.fn(), + mergeResourceArrays: jest.fn(), + setFunctionNames: jest.fn(), + update: jest.fn(), + validate: jest.fn(), + custom: null, + provider: null + }; + } + + private static createTestUtils(): Utils { + return { + appendFileSync: jest.fn(), + copyDirContentsSync: jest.fn(), + dirExistsSync: jest.fn(), + fileExistsSync: jest.fn(), + findServicePath: jest.fn(), + generateShortId: jest.fn(), + getVersion: jest.fn(), + logStat: jest.fn(), + readFile: jest.fn(), + readFileSync: jest.fn(), + walkDirSync: jest.fn(), + writeFile: jest.fn(), + writeFileDir: jest.fn(), + writeFileSync: jest.fn() + } + } + + private static createTestCli(){ + return { + log: jest.fn() + } + } + + private static createTestPluginManager(): PluginManager { + return { + addPlugin: jest.fn(), + cliCommands: null, + cliOptions: null, + commands: null, + deprecatedEvents: null, + hooks: null, + loadAllPlugins: jest.fn(), + loadCommand: jest.fn(), + loadCommands: jest.fn(), + loadCorePlugins: jest.fn(), + loadPlugins: jest.fn(), + loadServicePlugins: jest.fn(), + plugins: null, + serverless: null, + setCliCommands: jest.fn(), + setCliOptions: jest.fn(), + spawn: jest.fn() + } + } +} \ No newline at end of file diff --git a/src/test/utils.ts b/src/test/utils.ts new file mode 100644 index 00000000..3210ab3e --- /dev/null +++ b/src/test/utils.ts @@ -0,0 +1,3 @@ +export async function invokeHook(plugin: { hooks: { [eventName: string]: Promise }}, hook: string) { + return await (plugin.hooks[hook] as any)(); +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index ef135d13..d4d9522e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,11 +6,15 @@ "outDir": "./lib", "strict": false, "resolveJsonModule": true, - "types": ["jest", "node"] + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "types": [ + "jest", + "node" + ] }, "include": [ "src", - "src/shared/bindings.json", - "src/provider/armTemplates/azuredeploy.json" + "src/**/*.json" ] } \ No newline at end of file From bbd9e83f298ebe91bbe549d92ef54c0bf0f26c96 Mon Sep 17 00:00:00 2001 From: Tanner Barlow Date: Fri, 24 May 2019 13:19:46 -0700 Subject: [PATCH 2/4] Lint fixes --- src/plugins/login/loginPlugin.ts | 3 +-- src/services/loginService.ts | 1 - src/shared/binding.test.ts | 4 ++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/plugins/login/loginPlugin.ts b/src/plugins/login/loginPlugin.ts index 5b22cc7f..d8047cd6 100644 --- a/src/plugins/login/loginPlugin.ts +++ b/src/plugins/login/loginPlugin.ts @@ -1,7 +1,6 @@ -import open from 'open'; import Serverless from 'serverless'; import AzureProvider from '../../provider/azureProvider'; -import { AzureLoginService } from '../../services/loginService' +import { AzureLoginService } from '../../services/loginService'; export class AzureLoginPlugin { private provider: AzureProvider; diff --git a/src/services/loginService.ts b/src/services/loginService.ts index 92453f3f..1faa24b4 100644 --- a/src/services/loginService.ts +++ b/src/services/loginService.ts @@ -1,5 +1,4 @@ import { interactiveLoginWithAuthResponse, loginWithServicePrincipalSecretWithAuthResponse } from '@azure/ms-rest-nodeauth'; -import { getEnvVariable } from '../shared/utils'; export class AzureLoginService { diff --git a/src/shared/binding.test.ts b/src/shared/binding.test.ts index ea2c5758..ab522d62 100644 --- a/src/shared/binding.test.ts +++ b/src/shared/binding.test.ts @@ -1,11 +1,11 @@ import { MockFactory } from '../test/mockFactory'; -import { getBindingsMetaData, createEventsBindings } from './bindings' +import { getBindingsMetaData } from './bindings'; describe('Bindings', () => { it('should get bindings metadata from serverless', () => { const sls = MockFactory.createTestServerless(); expect(sls).not.toBeNull(); - const bindingsMetadata = getBindingsMetaData(sls); + getBindingsMetaData(sls); expect(sls.cli.log).toBeCalledWith('Parsing Azure Functions Bindings.json...'); }); }); \ No newline at end of file From c90e30d129559526f267230851801ab50c75ecb1 Mon Sep 17 00:00:00 2001 From: Tanner Barlow Date: Fri, 24 May 2019 14:44:57 -0700 Subject: [PATCH 3/4] test: Remove, package and APIM tests (#150) --- src/plugins/apim/apimFunctionPlugin.test.ts | 25 ++ src/plugins/apim/apimServicePlugin.test.ts | 43 +++ src/plugins/deploy/azureDeployPlugin.test.ts | 3 +- src/plugins/package/azurePackage.test.ts | 31 ++ src/plugins/package/azurePackage.ts | 8 +- src/plugins/remove/azureRemove.test.ts | 26 ++ src/shared/binding.test.ts | 4 +- src/shared/bindings.ts | 154 +++++++--- src/shared/constants.ts | 24 ++ src/shared/utils.ts | 298 ++++++------------- src/test/mockFactory.ts | 4 +- 11 files changed, 373 insertions(+), 247 deletions(-) create mode 100644 src/plugins/apim/apimFunctionPlugin.test.ts create mode 100644 src/plugins/package/azurePackage.test.ts create mode 100644 src/plugins/remove/azureRemove.test.ts create mode 100644 src/shared/constants.ts diff --git a/src/plugins/apim/apimFunctionPlugin.test.ts b/src/plugins/apim/apimFunctionPlugin.test.ts new file mode 100644 index 00000000..d7a02afd --- /dev/null +++ b/src/plugins/apim/apimFunctionPlugin.test.ts @@ -0,0 +1,25 @@ +import { MockFactory } from "../../test/mockFactory"; +import { invokeHook } from "../../test/utils"; +import { AzureApimFunctionPlugin } from './apimFunctionPlugin'; + +jest.mock('../../services/apimService'); +import { ApimService } from '../../services/apimService'; + +describe('APIM Function Plugin', () => { + it('calls deploy function', async () => { + const deployFunction = jest.fn(); + + ApimService.prototype.deployFunction = deployFunction; + + const sls = MockFactory.createTestServerless(); + sls.service.provider['apim'] = 'apim config' + const options = MockFactory.createTestServerlessOptions(); + const plugin = new AzureApimFunctionPlugin(sls, options); + + await invokeHook(plugin, 'after:deploy:function:deploy'); + + expect(sls.cli.log).toBeCalledWith('Starting APIM function deployment') + expect(deployFunction).toBeCalled(); + expect(sls.cli.log).lastCalledWith('Finished APIM function deployment') + }); +}); \ No newline at end of file diff --git a/src/plugins/apim/apimServicePlugin.test.ts b/src/plugins/apim/apimServicePlugin.test.ts index a4075947..c17a705d 100644 --- a/src/plugins/apim/apimServicePlugin.test.ts +++ b/src/plugins/apim/apimServicePlugin.test.ts @@ -1,6 +1,11 @@ import Serverless from 'serverless'; +import { MockFactory } from "../../test/mockFactory"; +import { invokeHook } from "../../test/utils"; import { AzureApimServicePlugin } from './apimServicePlugin'; +jest.mock('../../services/apimService'); +import { ApimService } from '../../services/apimService'; + describe('APIM Service Plugin', () => { it('is defined', () => { expect(AzureApimServicePlugin).toBeDefined(); @@ -16,4 +21,42 @@ describe('APIM Service Plugin', () => { expect(plugin).not.toBeNull(); }); + + it('calls deploy API and deploy functions', async () => { + const deployApi = jest.fn(); + const deployFunctions = jest.fn(); + + ApimService.prototype.deployApi = deployApi; + ApimService.prototype.deployFunctions = deployFunctions; + + const sls = MockFactory.createTestServerless(); + sls.service.provider['apim'] = 'apim config' + const options = MockFactory.createTestServerlessOptions(); + const plugin = new AzureApimServicePlugin(sls, options); + + await invokeHook(plugin, 'after:deploy:deploy'); + + expect(sls.cli.log).toBeCalledWith('Starting APIM service deployment') + expect(deployApi).toBeCalled(); + expect(deployFunctions).toBeCalled(); + expect(sls.cli.log).lastCalledWith('Finished APIM service deployment') + }); + + it('does not call deploy API or deploy functions when "apim" not included in config', async () => { + const deployApi = jest.fn(); + const deployFunctions = jest.fn(); + + ApimService.prototype.deployApi = deployApi; + ApimService.prototype.deployFunctions = deployFunctions; + + const sls = MockFactory.createTestServerless(); + const options = MockFactory.createTestServerlessOptions(); + const plugin = new AzureApimServicePlugin(sls, options); + + await invokeHook(plugin, 'after:deploy:deploy'); + + expect(sls.cli.log).not.toBeCalled() + expect(deployApi).not.toBeCalled(); + expect(deployFunctions).not.toBeCalled(); + }); }); \ No newline at end of file diff --git a/src/plugins/deploy/azureDeployPlugin.test.ts b/src/plugins/deploy/azureDeployPlugin.test.ts index a14cc593..05d3d2f5 100644 --- a/src/plugins/deploy/azureDeployPlugin.test.ts +++ b/src/plugins/deploy/azureDeployPlugin.test.ts @@ -2,9 +2,10 @@ import { MockFactory } from "../../test/mockFactory"; import { invokeHook } from "../../test/utils"; import { AzureDeployPlugin } from "./azureDeployPlugin"; -jest.mock("../../services/resourceService"); jest.mock("../../services/functionAppService"); import { FunctionAppService } from "../../services/functionAppService"; + +jest.mock("../../services/resourceService"); import { ResourceService } from "../../services/resourceService"; describe('Deploy plugin', () => { diff --git a/src/plugins/package/azurePackage.test.ts b/src/plugins/package/azurePackage.test.ts new file mode 100644 index 00000000..1c18d650 --- /dev/null +++ b/src/plugins/package/azurePackage.test.ts @@ -0,0 +1,31 @@ +import { MockFactory } from "../../test/mockFactory"; +import { invokeHook } from "../../test/utils"; +import { AzurePackage } from "./azurePackage" + +jest.mock('../../shared/bindings'); +import { BindingUtils } from '../../shared/bindings'; +jest.mock('../../shared/utils'); +import { Utils, FunctionMetadata } from '../../shared/utils'; + +describe('Azure Package Plugin', () => { + it('sets up provider configuration', async () => { + const metadata = 'metadata'; + const functionName = 'function1'; + + const getFunctionMetaDataFn = jest.fn(() => metadata as any as FunctionMetadata); + const createEventsBindingsFn = jest.fn(); + + Utils.getFunctionMetaData = getFunctionMetaDataFn + BindingUtils.createEventsBindings = createEventsBindingsFn + + const sls = MockFactory.createTestServerless(); + const options = MockFactory.createTestServerlessOptions(); + const plugin = new AzurePackage(sls, options); + + await invokeHook(plugin, 'package:setupProviderConfiguration'); + + expect(sls.cli.log).toBeCalledWith('Building Azure Events Hooks'); + expect(getFunctionMetaDataFn).toBeCalledWith(functionName, sls); + expect(createEventsBindingsFn).toBeCalledWith(sls.config.servicePath, functionName, metadata); + }); +}); \ No newline at end of file diff --git a/src/plugins/package/azurePackage.ts b/src/plugins/package/azurePackage.ts index 7d44762d..1eb4446f 100644 --- a/src/plugins/package/azurePackage.ts +++ b/src/plugins/package/azurePackage.ts @@ -1,8 +1,8 @@ import Serverless from 'serverless'; import AzureProvider from '../../provider/azureProvider'; -import { createEventsBindings } from '../../shared/bindings'; -import { getFunctionMetaData } from '../../shared/utils'; +import { BindingUtils } from '../../shared/bindings'; +import { Utils } from '../../shared/utils'; export class AzurePackage { provider: AzureProvider @@ -19,9 +19,9 @@ export class AzurePackage { const createEventsPromises = this.serverless.service.getAllFunctions() .map((functionName) => { - const metaData = getFunctionMetaData(functionName, this.serverless); + const metaData = Utils.getFunctionMetaData(functionName, this.serverless); - return createEventsBindings(this.serverless.config.servicePath, functionName, metaData); + return BindingUtils.createEventsBindings(this.serverless.config.servicePath, functionName, metaData); }); return Promise.all(createEventsPromises); diff --git a/src/plugins/remove/azureRemove.test.ts b/src/plugins/remove/azureRemove.test.ts new file mode 100644 index 00000000..223531f8 --- /dev/null +++ b/src/plugins/remove/azureRemove.test.ts @@ -0,0 +1,26 @@ +import { MockFactory } from "../../test/mockFactory"; +import { invokeHook } from "../../test/utils"; +import { AzureRemove } from "./azureRemove"; + +jest.mock("../../services/resourceService"); +import { ResourceService } from "../../services/resourceService"; + +describe('Remove Plugin', () => { + it('calls remove hook', async () => { + const deleteDeployment = jest.fn(); + const deleteResourceGroup = jest.fn(); + + ResourceService.prototype.deleteDeployment = deleteDeployment; + ResourceService.prototype.deleteResourceGroup = deleteResourceGroup; + + const sls = MockFactory.createTestServerless(); + const options = MockFactory.createTestServerlessOptions(); + const plugin = new AzureRemove(sls, options); + + await invokeHook(plugin, 'remove:remove'); + + expect(deleteDeployment).toBeCalled(); + expect(deleteResourceGroup).toBeCalled(); + expect(sls.cli.log).toBeCalledWith('Service successfully removed'); + }); +}); \ No newline at end of file diff --git a/src/shared/binding.test.ts b/src/shared/binding.test.ts index ab522d62..8ea864a1 100644 --- a/src/shared/binding.test.ts +++ b/src/shared/binding.test.ts @@ -1,11 +1,11 @@ import { MockFactory } from '../test/mockFactory'; -import { getBindingsMetaData } from './bindings'; +import { BindingUtils } from './bindings'; describe('Bindings', () => { it('should get bindings metadata from serverless', () => { const sls = MockFactory.createTestServerless(); expect(sls).not.toBeNull(); - getBindingsMetaData(sls); + BindingUtils.getBindingsMetaData(sls); expect(sls.cli.log).toBeCalledWith('Parsing Azure Functions Bindings.json...'); }); }); \ No newline at end of file diff --git a/src/shared/bindings.ts b/src/shared/bindings.ts index a4c70c61..c108072f 100644 --- a/src/shared/bindings.ts +++ b/src/shared/bindings.ts @@ -2,50 +2,128 @@ import { writeFileSync } from 'fs'; import { join } from 'path'; import Serverless from 'serverless'; import { FunctionMetadata } from './utils'; -const bindingsJson = require('./bindings.json'); - -const constants = { - bindings: 'bindings', - settings: 'settings', - name: 'name', - displayName: 'displayName', - type: 'type' -}; +import { constants } from './constants'; -export function getBindingsMetaData(serverless: Serverless) { - const bindingDisplayNames = []; - const bindingTypes = []; - const bindingSettings = []; - const bindingSettingsNames = []; - - serverless.cli.log('Parsing Azure Functions Bindings.json...'); +const bindingsJson = require('./bindings.json'); - for (let bindingsIndex = 0; bindingsIndex < bindingsJson[constants.bindings].length; bindingsIndex++) { - const settingsNames = []; +export class BindingUtils { + public static getBindingsMetaData(serverless: Serverless) { + const bindingDisplayNames = []; + const bindingTypes = []; + const bindingSettings = []; + const bindingSettingsNames = []; + + serverless.cli.log('Parsing Azure Functions Bindings.json...'); + + for (let bindingsIndex = 0; bindingsIndex < bindingsJson[constants.bindings].length; bindingsIndex++) { + const settingsNames = []; + + bindingTypes.push(bindingsJson[constants.bindings][bindingsIndex][constants.type]); + bindingDisplayNames.push(bindingsJson[constants.bindings][bindingsIndex][constants.displayName].toLowerCase()); + bindingSettings[bindingsIndex] = bindingsJson[constants.bindings][bindingsIndex][constants.settings]; + + for (let bindingSettingsIndex = 0; bindingSettingsIndex < bindingSettings[bindingsIndex].length; bindingSettingsIndex++) { + settingsNames.push(bindingSettings[bindingsIndex][bindingSettingsIndex][constants.name]); + } + + bindingSettingsNames[bindingsIndex] = settingsNames; + } + + return { + bindingDisplayNames: bindingDisplayNames, + bindingTypes: bindingTypes, + bindingSettings: bindingSettings, + bindingSettingsNames: bindingSettingsNames + }; + } - bindingTypes.push(bindingsJson[constants.bindings][bindingsIndex][constants.type]); - bindingDisplayNames.push(bindingsJson[constants.bindings][bindingsIndex][constants.displayName].toLowerCase()); - bindingSettings[bindingsIndex] = bindingsJson[constants.bindings][bindingsIndex][constants.settings]; + public static createEventsBindings(servicePath: string, functionName: string, functionMetadata: FunctionMetadata): Promise { + const functionJSON = functionMetadata.params.functionsJson; + functionJSON.entryPoint = functionMetadata.entryPoint; + functionJSON.scriptFile = functionMetadata.handlerPath; + writeFileSync(join(servicePath, functionName, 'function.json'), JSON.stringify(functionJSON, null, 4)); + return Promise.resolve(); + } - for (let bindingSettingsIndex = 0; bindingSettingsIndex < bindingSettings[bindingsIndex].length; bindingSettingsIndex++) { - settingsNames.push(bindingSettings[bindingsIndex][bindingSettingsIndex][constants.name]); + public static getBindingUserSettingsMetaData(azureSettings, bindingType, bindingTypeIndex, bindingDisplayNames) { + let bindingDisplayNamesIndex = bindingTypeIndex; + const bindingUserSettings = {}; + + if (azureSettings) { + const directionIndex = Object.keys(azureSettings).indexOf(constants.direction); + + if (directionIndex >= 0) { + const key = Object.keys(azureSettings)[directionIndex]; + const displayName = `$${bindingType}${azureSettings[key]}_displayName`; + + bindingDisplayNamesIndex = bindingDisplayNames.indexOf(displayName.toLowerCase()); + bindingUserSettings[constants.direction] = azureSettings[key]; + } } + const bindingUserSettingsMetaData = { + index: bindingDisplayNamesIndex, + userSettings: bindingUserSettings + }; + + return bindingUserSettingsMetaData; + } - bindingSettingsNames[bindingsIndex] = settingsNames; + public static getHttpOutBinding(bindingUserSettings) { + const binding = {}; + + binding[constants.type] = 'http'; + binding[constants.direction] = constants.outDirection; + binding[constants.name] = '$return'; + if (bindingUserSettings[constants.webHookType]) { + binding[constants.name] = 'res'; + } + + return binding; } - return { - bindingDisplayNames: bindingDisplayNames, - bindingTypes: bindingTypes, - bindingSettings: bindingSettings, - bindingSettingsNames: bindingSettingsNames - }; + public static getBinding(bindingType, bindingSettings, bindingUserSettings) { + const binding = {}; + + binding[constants.type] = bindingType; + if (bindingUserSettings && bindingUserSettings[constants.direction]) { + binding[constants.direction] = bindingUserSettings[constants.direction]; + } else if (bindingType.includes(constants.trigger)) { + binding[constants.direction] = constants.inDirection; + } else { + binding[constants.direction] = constants.outDirection; + } + + for (let bindingSettingsIndex = 0; bindingSettingsIndex < bindingSettings.length; bindingSettingsIndex++) { + const name = bindingSettings[bindingSettingsIndex][constants.name]; + + if (bindingUserSettings && bindingUserSettings[name] !== undefined && bindingUserSettings[name] !== null) { + binding[name] = bindingUserSettings[name]; + continue; + } + const value = bindingSettings[bindingSettingsIndex][constants.value]; + const required = bindingSettings[bindingSettingsIndex][constants.required]; + const resource = bindingSettings[bindingSettingsIndex][constants.resource]; + + if (required) { + const defaultValue = bindingSettings[bindingSettingsIndex][constants.defaultValue]; + + if (defaultValue) { + binding[name] = defaultValue; + } else if (name === constants.connection && resource.toLowerCase() === constants.storage) { + binding[name] = 'AzureWebJobsStorage'; + } else { + throw new Error(`Required property ${name} is missing for binding:${bindingType}`); + } + } + + if (value === constants.enum && name !== constants.webHookType) { + const enumValues = bindingSettings[bindingSettingsIndex][constants.enum]; + + binding[name] = enumValues[0][constants.value]; + } + } + + return binding; + } + } - -export async function createEventsBindings(servicePath: string, functionName: string, functionMetadata: FunctionMetadata): Promise { - const functionJSON = functionMetadata.params.functionsJson; - functionJSON.entryPoint = functionMetadata.entryPoint; - functionJSON.scriptFile = functionMetadata.handlerPath; - writeFileSync(join(servicePath, functionName, 'function.json'), JSON.stringify(functionJSON, null, 4)); - return Promise.resolve(); -} \ No newline at end of file diff --git a/src/shared/constants.ts b/src/shared/constants.ts new file mode 100644 index 00000000..a9dd3512 --- /dev/null +++ b/src/shared/constants.ts @@ -0,0 +1,24 @@ +export const constants = { + bindings: 'bindings', + settings: 'settings', + name: 'name', + displayName: 'displayName', + type: 'type', + direction: 'direction', + trigger: 'Trigger', + inDirection: 'in', + outDirection: 'out', + value: 'value', + resource: 'resource', + required: 'required', + storage: 'storage', + connection: 'connection', + enum: 'enum', + defaultValue: 'defaultValue', + webHookType: 'webHookType', + httpTrigger: 'httpTrigger', + queue: 'queue', + queueName: 'queueName', + xAzureSettings: 'x-azure-settings', + entryPoint: 'entryPoint' +} \ No newline at end of file diff --git a/src/shared/utils.ts b/src/shared/utils.ts index 05cd0351..55234686 100644 --- a/src/shared/utils.ts +++ b/src/shared/utils.ts @@ -1,28 +1,5 @@ -import { getBindingsMetaData } from "./bindings"; - -const constants = { - type: 'type', - direction: 'direction', - trigger: 'Trigger', - inDirection: 'in', - outDirection: 'out', - settings: 'settings', - name: 'name', - value: 'value', - resource: 'resource', - required: 'required', - storage: 'storage', - connection: 'connection', - enum: 'enum', - defaultValue: 'defaultValue', - webHookType: 'webHookType', - httpTrigger: 'httpTrigger', - queue: 'queue', - queueName: 'queueName', - displayName: 'displayName', - xAzureSettings: 'x-azure-settings', - entryPoint: 'entryPoint' -}; +import { BindingUtils } from "./bindings"; +import { constants } from "./constants"; export interface FunctionMetadata { entryPoint: any; @@ -30,186 +7,107 @@ export interface FunctionMetadata { params: any; } -export function getFunctionMetaData(functionName, serverless): FunctionMetadata { - const bindings = []; - let bindingSettingsNames = []; - let bindingSettings = []; - let bindingUserSettings = {}; - let bindingType; - const functionsJson = { disabled: false, bindings: [] }; - const functionObject = serverless.service.getFunction(functionName); - const handler = functionObject.handler; - const events = functionObject.events; - const params: any = { - functionJson: null - }; - - const parsedBindings = getBindingsMetaData(serverless); - - const bindingTypes = parsedBindings.bindingTypes; - const bindingDisplayNames = parsedBindings.bindingDisplayNames; - - for (let eventsIndex = 0; eventsIndex < events.length; eventsIndex++) { - bindingType = Object.keys(functionObject.events[eventsIndex])[0]; - - if (eventsIndex === 0) { - bindingType += constants.trigger; - } - - const index = bindingTypes.indexOf(bindingType); - - if (index < 0) { - throw new Error(`Binding ${bindingType} not supported`); - } - - serverless.cli.log(`Building binding for function: ${functionName} event: ${bindingType}`); - - bindingUserSettings = {}; - const azureSettings = events[eventsIndex][constants.xAzureSettings]; - let bindingTypeIndex = bindingTypes.indexOf(bindingType); - const bindingUserSettingsMetaData = this.getBindingUserSettingsMetaData(azureSettings, bindingType, bindingTypeIndex, bindingDisplayNames); - - bindingTypeIndex = bindingUserSettingsMetaData.index; - bindingUserSettings = bindingUserSettingsMetaData.userSettings; - - if (bindingType.includes(constants.queue) && functionObject.events[eventsIndex].queue) { - bindingUserSettings[constants.queueName] = functionObject.events[eventsIndex].queue; - } - - if (bindingTypeIndex < 0) { - throw new Error('Binding not supported'); - } - - bindingSettings = parsedBindings.bindingSettings[bindingTypeIndex]; - bindingSettingsNames = parsedBindings.bindingSettingsNames[bindingTypeIndex]; - - if (azureSettings) { - for (let azureSettingKeyIndex = 0; azureSettingKeyIndex < Object.keys(azureSettings).length; azureSettingKeyIndex++) { - const key = Object.keys(azureSettings)[azureSettingKeyIndex]; - - if (bindingSettingsNames.indexOf(key) >= 0) { - bindingUserSettings[key] = azureSettings[key]; +export class Utils { + public static getFunctionMetaData(functionName: string, serverless): FunctionMetadata { + const bindings = []; + let bindingSettingsNames = []; + let bindingSettings = []; + let bindingUserSettings = {}; + let bindingType; + const functionsJson = { disabled: false, bindings: [] }; + const functionObject = serverless.service.getFunction(functionName); + const handler = functionObject.handler; + const events = functionObject.events; + const params: any = { + functionJson: null + }; + + const parsedBindings = BindingUtils.getBindingsMetaData(serverless); + + const bindingTypes = parsedBindings.bindingTypes; + const bindingDisplayNames = parsedBindings.bindingDisplayNames; + + for (let eventsIndex = 0; eventsIndex < events.length; eventsIndex++) { + bindingType = Object.keys(functionObject.events[eventsIndex])[0]; + + if (eventsIndex === 0) { + bindingType += constants.trigger; + } + + const index = bindingTypes.indexOf(bindingType); + + if (index < 0) { + throw new Error(`Binding ${bindingType} not supported`); + } + + serverless.cli.log(`Building binding for function: ${functionName} event: ${bindingType}`); + + bindingUserSettings = {}; + const azureSettings = events[eventsIndex][constants.xAzureSettings]; + let bindingTypeIndex = bindingTypes.indexOf(bindingType); + const bindingUserSettingsMetaData = BindingUtils.getBindingUserSettingsMetaData(azureSettings, bindingType, bindingTypeIndex, bindingDisplayNames); + + bindingTypeIndex = bindingUserSettingsMetaData.index; + bindingUserSettings = bindingUserSettingsMetaData.userSettings; + + if (bindingType.includes(constants.queue) && functionObject.events[eventsIndex].queue) { + bindingUserSettings[constants.queueName] = functionObject.events[eventsIndex].queue; + } + + if (bindingTypeIndex < 0) { + throw new Error('Binding not supported'); + } + + bindingSettings = parsedBindings.bindingSettings[bindingTypeIndex]; + bindingSettingsNames = parsedBindings.bindingSettingsNames[bindingTypeIndex]; + + if (azureSettings) { + for (let azureSettingKeyIndex = 0; azureSettingKeyIndex < Object.keys(azureSettings).length; azureSettingKeyIndex++) { + const key = Object.keys(azureSettings)[azureSettingKeyIndex]; + + if (bindingSettingsNames.indexOf(key) >= 0) { + bindingUserSettings[key] = azureSettings[key]; + } } } + + bindings.push(BindingUtils.getBinding(bindingType, bindingSettings, bindingUserSettings)); } - - bindings.push(this.getBinding(bindingType, bindingSettings, bindingUserSettings, serverless)); - } - - if (bindingType === constants.httpTrigger) { - bindings.push(this.getHttpOutBinding(bindingUserSettings)); - } - - functionsJson.bindings = bindings; - params.functionsJson = functionsJson; - - const entryPointAndHandlerPath = this.getEntryPointAndHandlerPath(handler); - if (functionObject.scriptFile) { - entryPointAndHandlerPath.handlerPath = functionObject.scriptFile; - } - const metaData = { - entryPoint: entryPointAndHandlerPath[constants.entryPoint], - handlerPath: entryPointAndHandlerPath.handlerPath, - params: params - }; - - return metaData; -}; - -export function getBindingUserSettingsMetaData(azureSettings, bindingType, bindingTypeIndex, bindingDisplayNames) { - let bindingDisplayNamesIndex = bindingTypeIndex; - const bindingUserSettings = {}; - - if (azureSettings) { - const directionIndex = Object.keys(azureSettings).indexOf(constants.direction); - - if (directionIndex >= 0) { - const key = Object.keys(azureSettings)[directionIndex]; - const displayName = `$${bindingType}${azureSettings[key]}_displayName`; - - bindingDisplayNamesIndex = bindingDisplayNames.indexOf(displayName.toLowerCase()); - bindingUserSettings[constants.direction] = azureSettings[key]; + + if (bindingType === constants.httpTrigger) { + bindings.push(BindingUtils.getHttpOutBinding(bindingUserSettings)); } - } - const bindingUserSettingsMetaData = { - index: bindingDisplayNamesIndex, - userSettings: bindingUserSettings - }; - - return bindingUserSettingsMetaData; -}; - -export function getEntryPointAndHandlerPath(handler) { - let handlerPath = 'handler.js'; - let entryPoint = handler; - const handlerSplit = handler.split('.'); - - if (handlerSplit.length > 1) { - entryPoint = handlerSplit[handlerSplit.length - 1]; - handlerPath = `${handler.substring(0, handler.lastIndexOf('.'))}.js`; - } - const metaData = { - entryPoint: entryPoint, - handlerPath: handlerPath - }; - - return metaData; -}; - -export function getHttpOutBinding(bindingUserSettings) { - const binding = {}; - - binding[constants.type] = 'http'; - binding[constants.direction] = constants.outDirection; - binding[constants.name] = '$return'; - if (bindingUserSettings[constants.webHookType]) { - binding[constants.name] = 'res'; - } - - return binding; -} - -export function getBinding(bindingType, bindingSettings, bindingUserSettings) { - const binding = {}; - - binding[constants.type] = bindingType; - if (bindingUserSettings && bindingUserSettings[constants.direction]) { - binding[constants.direction] = bindingUserSettings[constants.direction]; - } else if (bindingType.includes(constants.trigger)) { - binding[constants.direction] = constants.inDirection; - } else { - binding[constants.direction] = constants.outDirection; - } - - for (let bindingSettingsIndex = 0; bindingSettingsIndex < bindingSettings.length; bindingSettingsIndex++) { - const name = bindingSettings[bindingSettingsIndex][constants.name]; - - if (bindingUserSettings && bindingUserSettings[name] !== undefined && bindingUserSettings[name] !== null) { - binding[name] = bindingUserSettings[name]; - continue; - } - const value = bindingSettings[bindingSettingsIndex][constants.value]; - const required = bindingSettings[bindingSettingsIndex][constants.required]; - const resource = bindingSettings[bindingSettingsIndex][constants.resource]; - - if (required) { - const defaultValue = bindingSettings[bindingSettingsIndex][constants.defaultValue]; - - if (defaultValue) { - binding[name] = defaultValue; - } else if (name === constants.connection && resource.toLowerCase() === constants.storage) { - binding[name] = 'AzureWebJobsStorage'; - } else { - throw new Error(`Required property ${name} is missing for binding:${bindingType}`); - } + + functionsJson.bindings = bindings; + params.functionsJson = functionsJson; + + const entryPointAndHandlerPath = Utils.getEntryPointAndHandlerPath(handler); + if (functionObject.scriptFile) { + entryPointAndHandlerPath.handlerPath = functionObject.scriptFile; } + const metaData = { + entryPoint: entryPointAndHandlerPath[constants.entryPoint], + handlerPath: entryPointAndHandlerPath.handlerPath, + params: params + }; + + return metaData; + } - if (value === constants.enum && name !== constants.webHookType) { - const enumValues = bindingSettings[bindingSettingsIndex][constants.enum]; - - binding[name] = enumValues[0][constants.value]; + public static getEntryPointAndHandlerPath(handler) { + let handlerPath = 'handler.js'; + let entryPoint = handler; + const handlerSplit = handler.split('.'); + + if (handlerSplit.length > 1) { + entryPoint = handlerSplit[handlerSplit.length - 1]; + handlerPath = `${handler.substring(0, handler.lastIndexOf('.'))}.js`; } + const metaData = { + entryPoint: entryPoint, + handlerPath: handlerPath + }; + + return metaData; } - - return binding; -}; +} diff --git a/src/test/mockFactory.ts b/src/test/mockFactory.ts index e1e7e6b0..da96e62f 100644 --- a/src/test/mockFactory.ts +++ b/src/test/mockFactory.ts @@ -39,7 +39,7 @@ export class MockFactory { private static createTestService(): Service { return { - getAllFunctions: jest.fn(), + getAllFunctions: jest.fn(() => ['function1']), getFunction: jest.fn(), getAllEventsInFunction: jest.fn(), getAllFunctionsNames: jest.fn(), @@ -51,7 +51,7 @@ export class MockFactory { update: jest.fn(), validate: jest.fn(), custom: null, - provider: null + provider: {} as any, }; } From 33c5a46e2bcaba887a01579be794cfa3aed10fc2 Mon Sep 17 00:00:00 2001 From: Tanner Barlow Date: Fri, 24 May 2019 14:47:40 -0700 Subject: [PATCH 4/4] Remove console log from login plugin --- src/plugins/login/loginPlugin.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/login/loginPlugin.ts b/src/plugins/login/loginPlugin.ts index d8047cd6..75072d35 100644 --- a/src/plugins/login/loginPlugin.ts +++ b/src/plugins/login/loginPlugin.ts @@ -25,7 +25,6 @@ export class AzureLoginPlugin { try { const authResult = await AzureLoginService.login(); this.serverless.variables['azureCredentials'] = authResult.credentials; - console.log(this.serverless.variables['azureCredentials']) // Use environment variable for sub ID or use the first subscription in the list (service principal can // have access to more than one subscription) this.serverless.variables['subscriptionId'] = process.env.azureSubId || authResult.subscriptions[0].id;