diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 229e3c4..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "env": { - "node": true, - "commonjs": true, - "es6": true, - "mocha": true - }, - "extends": "eslint:recommended", - "parserOptions": { - "ecmaVersion": 2018 - }, - "rules": { - "eol-last":"error", - "no-console": "off", - "indent": [ - "error", - 4, - { - "SwitchCase": 1 - } - ], - "linebreak-style": [ - "warn", - "windows" - ], - "quotes": [ - "error", - "double" - ], - "semi": [ - "error", - "always" - ], - "max-len": [ - "error", - { - "code": 140 - } - ] - } -} \ No newline at end of file diff --git a/azure-kusto-data/index.js b/azure-kusto-data/index.js deleted file mode 100644 index 77962f4..0000000 --- a/azure-kusto-data/index.js +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -const Client = require("./source/client"); -const ClientRequestProperties = require("./source/clientRequestProperties"); -const KustoConnectionStringBuilder = require("./source/connectionBuilder"); -module.exports = { - Client, - KustoConnectionStringBuilder, - ClientRequestProperties -}; diff --git a/azure-kusto-data/index.ts b/azure-kusto-data/index.ts new file mode 100644 index 0000000..ae1ee36 --- /dev/null +++ b/azure-kusto-data/index.ts @@ -0,0 +1,13 @@ +/* tslint:disable:no-var-requires */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import KustoClient from "./source/client"; +import ClientRequestProperties from "./source/clientRequestProperties"; +import KustoConnectionStringBuilder from "./source/connectionBuilder"; + +export { + KustoClient as Client, + ClientRequestProperties, + KustoConnectionStringBuilder +} \ No newline at end of file diff --git a/azure-kusto-data/package-lock.json b/azure-kusto-data/package-lock.json index e4c11ca..68a4e78 100644 --- a/azure-kusto-data/package-lock.json +++ b/azure-kusto-data/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-kusto-data", - "version": "1.0.3", + "version": "2.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -120,16 +120,16 @@ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "dev": true }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "@types/mocha": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.0.tgz", + "integrity": "sha512-/Sge3BymXo4lKc31C8OINJgXLaw+7vL1/L1pGiBNpGrBiT8FQiaFpSYV0uhTaG4y78vcMBTMFsWaHDvuD+xGzQ==", "dev": true }, "@types/node": { - "version": "8.10.59", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.59.tgz", - "integrity": "sha512-8RkBivJrDCyPpBXhVZcjh7cQxVBSmRk9QM7hOketZzp6Tg79c0N8kkpAIito9bnJ3HCVCHVYz+KHTEbfQNfeVQ==" + "version": "14.14.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.13.tgz", + "integrity": "sha512-vbxr0VZ8exFMMAjCW8rJwaya0dMCDyYW2ZRdTyjtrCvJoENMpdUHOT/eTzvgyA5ZnqRZ/sI0NwqAxNHKYokLJQ==" }, "@types/node-fetch": { "version": "2.5.4", @@ -147,6 +147,12 @@ "@types/node": "*" } }, + "@types/uuid": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==", + "dev": true + }, "abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -155,18 +161,6 @@ "event-target-shim": "^5.0.0" } }, - "acorn": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz", - "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==", - "dev": true - }, - "acorn-jsx": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", - "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", - "dev": true - }, "adal-node": { "version": "0.1.28", "resolved": "https://registry.npmjs.org/adal-node/-/adal-node-0.1.28.tgz", @@ -181,12 +175,19 @@ "uuid": "^3.1.0", "xmldom": ">= 0.1.x", "xpath.js": "~1.1.0" + }, + "dependencies": { + "@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==" + } } }, "ajv": { - "version": "6.12.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", - "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -200,29 +201,6 @@ "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", "dev": true }, - "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", - "dev": true, - "requires": { - "type-fest": "^0.11.0" - }, - "dependencies": { - "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", - "dev": true - } - } - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -242,6 +220,12 @@ "picomatch": "^2.0.4" } }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -270,12 +254,6 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true - }, "async": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/async/-/async-3.1.0.tgz", @@ -292,9 +270,9 @@ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", - "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, "axios": { "version": "0.19.2", @@ -354,10 +332,16 @@ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "dev": true }, "camelcase": { @@ -382,12 +366,6 @@ "supports-color": "^5.3.0" } }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, "chokidar": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", @@ -404,21 +382,6 @@ "readdirp": "~3.2.0" } }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-width": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", - "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", - "dev": true - }, "cliui": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", @@ -478,6 +441,12 @@ "delayed-stream": "~1.0.0" } }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -489,26 +458,11 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, - "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" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true }, "dashdash": { "version": "1.14.1", @@ -523,27 +477,12 @@ "resolved": "https://registry.npmjs.org/date-utils/-/date-utils-1.2.21.tgz", "integrity": "sha1-YfsWzcEnSzyayq/+n8ad+HIKK2Q=" }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -564,15 +503,6 @@ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -590,12 +520,6 @@ "safe-buffer": "^5.0.1" } }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, "es-abstract": { "version": "1.17.6", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", @@ -632,131 +556,12 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, - "eslint": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", - "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.10.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^1.4.3", - "eslint-visitor-keys": "^1.1.0", - "espree": "^6.1.2", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^7.0.0", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.14", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.3", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^6.1.2", - "strip-ansi": "^5.2.0", - "strip-json-comments": "^3.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - } - }, - "eslint-scope": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", - "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "eslint-visitor-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", - "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", - "dev": true - }, - "espree": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", - "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", - "dev": true, - "requires": { - "acorn": "^7.1.1", - "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.1.0" - } - }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, - "esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", - "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, "event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -767,56 +572,21 @@ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", - "dev": true, - "requires": { - "flat-cache": "^2.0.1" - } - }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -844,23 +614,6 @@ "is-buffer": "~2.0.3" } }, - "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", - "dev": true, - "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" - } - }, - "flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "dev": true - }, "follow-redirects": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", @@ -918,12 +671,6 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -961,15 +708,6 @@ "is-glob": "^4.0.1" } }, - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -982,11 +720,11 @@ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", "requires": { - "ajv": "^6.5.5", + "ajv": "^6.12.3", "har-schema": "^2.0.0" } }, @@ -1027,37 +765,6 @@ "sshpk": "^1.7.0" } }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1074,88 +781,6 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "inquirer": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", - "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^3.0.0", - "cli-cursor": "^3.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.15", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.5.3", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "ip-regex": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", @@ -1182,6 +807,15 @@ "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", "dev": true }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "is-date-object": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", @@ -1194,12 +828,6 @@ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", "dev": true }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", @@ -1286,12 +914,6 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -1333,16 +955,6 @@ "safe-buffer": "^5.0.1" } }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -1374,6 +986,12 @@ "integrity": "sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg==", "dev": true }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "mime-db": { "version": "1.42.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", @@ -1387,12 +1005,6 @@ "mime-db": "1.42.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 - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -1500,30 +1112,6 @@ "resolved": "https://registry.npmjs.org/moment/-/moment-2.25.3.tgz", "integrity": "sha512-PuYv0PHxZvzc15Sp8ybUCoQ+xpyPWvjOuK72a5ovzp2LI32rJXOiIfyoFoYvG3s6EwwrdkMyWuRiEHSZRLJNdg==" }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, "nise": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.2.tgz", @@ -1614,35 +1202,6 @@ "wrappy": "1" } }, - "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -1667,15 +1226,6 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -1688,10 +1238,10 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, "path-to-regexp": { @@ -1714,18 +1264,6 @@ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, "psl": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", @@ -1750,12 +1288,6 @@ "picomatch": "^2.0.4" } }, - "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", - "dev": true - }, "request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", @@ -1795,44 +1327,14 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", "dev": true, "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true - }, - "rxjs": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", - "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" } }, "safe-buffer": { @@ -1850,39 +1352,12 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, "sinon": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.5.0.tgz", @@ -1898,23 +1373,20 @@ "supports-color": "^5.5.0" } }, - "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "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 + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - } + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, "sprintf-js": { @@ -1939,28 +1411,6 @@ "tweetnacl": "~0.14.0" } }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, "string.prototype.trimend": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", @@ -1998,12 +1448,6 @@ } } }, - "strip-json-comments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz", - "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==", - "dev": true - }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -2013,64 +1457,6 @@ "has-flag": "^3.0.0" } }, - "table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", - "dev": true, - "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - }, - "dependencies": { - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2089,11 +1475,83 @@ "punycode": "^2.1.1" } }, + "ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + } + } + }, "tslib": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" }, + "tslint": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", + "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^4.0.1", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.3", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.13.0", + "tsutils": "^2.29.0" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, "tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", @@ -2112,25 +1570,16 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "typescript": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", + "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", "dev": true }, "underscore": { @@ -2139,9 +1588,9 @@ "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" }, "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", "requires": { "punycode": "^2.1.0" } @@ -2151,12 +1600,6 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, - "v8-compile-cache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", - "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", - "dev": true - }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -2224,12 +1667,6 @@ } } }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, "wrap-ansi": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", @@ -2272,15 +1709,6 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, "xml2js": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", @@ -2374,6 +1802,12 @@ "lodash": "^4.17.15", "yargs": "^13.3.0" } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true } } } diff --git a/azure-kusto-data/package.json b/azure-kusto-data/package.json index e1e7d55..5f31ead 100644 --- a/azure-kusto-data/package.json +++ b/azure-kusto-data/package.json @@ -1,8 +1,9 @@ { "name": "azure-kusto-data", - "version": "1.0.3", + "version": "2.0.0", "description": "Azure Data Explorer Query SDK", "main": "index.js", + "types": "index.d.ts", "engines": { "node": ">= 8.0.0" }, @@ -17,9 +18,11 @@ "kusto" ], "scripts": { - "example": "node example.js", - "test": "mocha", - "lint": "eslint source --quiet" + "build": "tsc -b", + "prepublish": "npm run build ", + "example": "npm run build && node example.js", + "lint": "npm run build && tslint --project tsconfig.json --quiet", + "test": "npm run build && mocha --require ts-node/register" }, "author": "", "license": "ISC", @@ -31,8 +34,13 @@ "uuid": "^3.4.0" }, "devDependencies": { - "eslint": "^6.8.0", "mocha": "^7.1.2", - "sinon": "^7.2.3" + "sinon": "^7.2.3", + "tslint": "^6.1.3", + "typescript": "^4.1.3", + "ts-node": "^9.1.1", + "@types/mocha": "^8.2.0", + "@types/node": "^14.14.13", + "@types/uuid": "^8.3.0" } } diff --git a/azure-kusto-data/source/azLoginIdentityClient.js b/azure-kusto-data/source/azLoginIdentityClient.js deleted file mode 100644 index fcc09ba..0000000 --- a/azure-kusto-data/source/azLoginIdentityClient.js +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -const {AzureCliCredentials} = require("@azure/ms-rest-nodeauth"); - -module.exports = function acquireToken(connectionString, callback) { - - AzureCliCredentials.create({ resource: connectionString }).then((res)=>{ - - const tokenData = res.tokenInfo; - return callback(null, { tokenType: tokenData.tokenType, accessToken: tokenData.accessToken }); - - }).catch(err=>callback(err)); -}; diff --git a/azure-kusto-data/source/azLoginIdentityClient.ts b/azure-kusto-data/source/azLoginIdentityClient.ts new file mode 100644 index 0000000..ef49ec5 --- /dev/null +++ b/azure-kusto-data/source/azLoginIdentityClient.ts @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import {AzureCliCredentials} from "@azure/ms-rest-nodeauth"; +import {TokenResponse} from "adal-node"; + +export default function acquireToken(connectionString: string, callback: (err: Error | null, data?: { tokenType: string; accessToken: string }) => T) { + + AzureCliCredentials.create({resource: connectionString}).then((res) => { + + const tokenData = res.tokenInfo; + return callback(null, {tokenType: tokenData.tokenType, accessToken: tokenData.accessToken}); + + }).catch(err => callback(err)); +} diff --git a/azure-kusto-data/source/client.js b/azure-kusto-data/source/client.ts similarity index 54% rename from azure-kusto-data/source/client.js rename to azure-kusto-data/source/client.ts index 0b582f2..a470d54 100644 --- a/azure-kusto-data/source/client.js +++ b/azure-kusto-data/source/client.ts @@ -1,30 +1,36 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -const moment = require("moment"); -const uuidv4 = require("uuid/v4"); -const AadHelper = require("./security"); -const { KustoResponseDataSetV1, KustoResponseDataSetV2 } = require("./response"); -const ConnectionStringBuilder = require("./connectionBuilder"); -const ClientRequestProperties = require("./clientRequestProperties"); -const pkg = require("../package.json"); -const axios = require("axios"); +import moment from "moment"; +import uuid from "uuid"; +import AadHelper from "./security"; +import {KustoResponseDataSet, KustoResponseDataSetV1, KustoResponseDataSetV2} from "./response"; +import ConnectionStringBuilder from "./connectionBuilder"; +import ClientRequestProperties from "./clientRequestProperties"; +import pkg from "../package.json"; +import axios from "axios"; const COMMAND_TIMEOUT_IN_MILLISECS = moment.duration(10.5, "minutes").asMilliseconds(); const QUERY_TIMEOUT_IN_MILLISECS = moment.duration(4.5, "minutes").asMilliseconds(); const CLIENT_SERVER_DELTA_IN_MILLISECS = moment.duration(0.5, "minutes").asMilliseconds(); const MGMT_PREFIX = "."; -const ExecutionType = Object.freeze({ - Mgmt: 0, - Query: 1, - Ingest: 2 -}); +enum ExecutionType { + Mgmt = 0, + Query = 1, + Ingest = 2 +} -module.exports = class KustoClient { - constructor(kcsb) { +export class KustoClient { + connectionString: ConnectionStringBuilder; + cluster: string; + endpoints: { mgmt: string; query: string; ingest: string; }; + aadHelper: AadHelper; + headers: { [name: string]: string }; + + constructor(kcsb: string | ConnectionStringBuilder) { this.connectionString = typeof (kcsb) === "string" ? new ConnectionStringBuilder(kcsb) : kcsb; - this.cluster = this.connectionString.dataSource; + this.cluster = (this.connectionString.dataSource as string); this.endpoints = { mgmt: `${this.cluster}/v1/rest/mgmt`, query: `${this.cluster}/v2/rest/query`, @@ -38,7 +44,7 @@ module.exports = class KustoClient { }; } - async execute(db, query, properties) { + async execute(db: string, query: string, properties?: ClientRequestProperties) { query = query.trim(); if (query.startsWith(MGMT_PREFIX)) { return this.executeMgmt(db, query, properties); @@ -47,15 +53,15 @@ module.exports = class KustoClient { return this.executeQuery(db, query, properties); } - async executeQuery(db, query, properties) { + async executeQuery(db: string, query: string, properties?: ClientRequestProperties) { return this._execute(this.endpoints.query, ExecutionType.Query, db, query, null, properties); } - async executeMgmt(db, query, properties) { + async executeMgmt(db: string, query: string, properties?: ClientRequestProperties) { return this._execute(this.endpoints.mgmt, ExecutionType.Mgmt, db, query, null, properties); } - async executeStreamingIngest(db, table, stream, streamFormat, mappingName) { + async executeStreamingIngest(db: string, table: string, stream: any, streamFormat: any, mappingName: string | null): Promise { let endpoint = `${this.endpoints.ingest}/${db}/${table}?streamFormat=${streamFormat}`; if (mappingName != null) { endpoint += `&mappingName=${mappingName}`; @@ -64,68 +70,77 @@ module.exports = class KustoClient { return this._execute(endpoint, ExecutionType.Ingest, db, null, stream, null); } - async _execute(endpoint, executionType, db, query, stream, properties) { - const headers = {}; + async _execute( + endpoint: string, + executionType: ExecutionType, + db: string, + query: string | null, + stream: string | null, + properties?: ClientRequestProperties | null): Promise { + const headers: { [header: string]: string } = {}; Object.assign(headers, this.headers); - let payload; + let payload: { db: string, csl: string, properties?: any }; let clientRequestPrefix = ""; let clientRequestId; - let timeout = this._getClientTimeout(executionType, properties); - + const timeout = this._getClientTimeout(executionType, properties); + let payloadStr = ""; if (query != null) { payload = { "db": db, "csl": query }; - if (properties != null && properties instanceof ClientRequestProperties) { + if (properties != null) { payload.properties = properties.toJson(); clientRequestId = properties.clientRequestId; } - payload = JSON.stringify(payload); + payloadStr = JSON.stringify(payload); headers["Content-Type"] = "application/json; charset=utf-8"; clientRequestPrefix = "KNC.execute;"; } else if (stream != null) { - payload = stream; + payloadStr = stream; clientRequestPrefix = "KNC.executeStreamingIngest;"; headers["Content-Encoding"] = "gzip"; headers["Content-Type"] = "multipart/form-data"; } - headers["x-ms-client-request-id"] = clientRequestId || clientRequestPrefix + `${uuidv4()}`; + headers["x-ms-client-request-id"] = clientRequestId || clientRequestPrefix + `${uuid.v4()}`; - headers["Authorization"] = await this.aadHelper._getAuthHeader(); + headers.Authorization = await this.aadHelper._getAuthHeader(); - return this._doRequest(endpoint, executionType, headers, payload, timeout, properties); + return this._doRequest(endpoint, executionType, headers, payloadStr, timeout, properties); } - async _doRequest(endpoint, executionType, headers, payload, timeout, properties) { - let axiosConfig = { - headers: headers, + async _doRequest(endpoint: string, + executionType: ExecutionType, + headers: { [header: string]: string; }, + payload: string, + timeout: number, + properties?: ClientRequestProperties | null): Promise { + const axiosConfig = { + headers, gzip: true, - timeout: timeout + timeout }; let axiosResponse; try { axiosResponse = await axios.post(endpoint, payload, axiosConfig); - } - catch (error) { + } catch (error) { if (error.response) { throw error.response.data.error; } throw error; } - return this._parseResponse(axiosResponse.data, executionType, properties); + return this._parseResponse(axiosResponse.data, executionType, properties, axiosResponse.status); } - _parseResponse(response, executionType, properties) { - - const { raw } = properties || {}; + _parseResponse(response: any, executionType: ExecutionType, properties?: ClientRequestProperties | null, status?: number) : KustoResponseDataSet { + const {raw} = properties || {}; if (raw === true || executionType == ExecutionType.Ingest) { return response; } @@ -138,16 +153,16 @@ module.exports = class KustoClient { kustoResponse = new KustoResponseDataSetV1(response); } } catch (ex) { - throw `Failed to parse response ({${response.status}}) with the following error [${ex}].`; + throw new Error(`Failed to parse response ({${status}}) with the following error [${ex}].`); } if (kustoResponse.getErrorsCount() > 0) { - throw `Kusto request had errors. ${kustoResponse.getExceptions()}`; + throw new Error(`Kusto request had errors. ${kustoResponse.getExceptions()}`); } return kustoResponse; } - _getClientTimeout(executionType, properties) { - if (properties != null && properties instanceof ClientRequestProperties) { + _getClientTimeout(executionType: ExecutionType, properties?: ClientRequestProperties | null): number { + if (properties != null) { const clientTimeout = properties.getClientTimeout(); if (clientTimeout) { return clientTimeout; @@ -159,7 +174,8 @@ module.exports = class KustoClient { } } - const defaultTimeout = executionType == ExecutionType.Query ? QUERY_TIMEOUT_IN_MILLISECS : COMMAND_TIMEOUT_IN_MILLISECS; - return defaultTimeout; + return executionType == ExecutionType.Query ? QUERY_TIMEOUT_IN_MILLISECS : COMMAND_TIMEOUT_IN_MILLISECS; } -}; +} + +export default KustoClient; diff --git a/azure-kusto-data/source/clientRequestProperties.js b/azure-kusto-data/source/clientRequestProperties.ts similarity index 53% rename from azure-kusto-data/source/clientRequestProperties.js rename to azure-kusto-data/source/clientRequestProperties.ts index d7376d0..6822e45 100644 --- a/azure-kusto-data/source/clientRequestProperties.js +++ b/azure-kusto-data/source/clientRequestProperties.ts @@ -1,29 +1,35 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -module.exports = class ClientRequestProperties { - constructor(options, parameters, clientRequestId) { +export class ClientRequestProperties { + private _options: { [option: string]: any }; + private _parameters: { [option: string]: any }; + private _clientTimeOut?: number; + public clientRequestId: string | null; + public raw?: boolean; + + constructor(options?: {}, parameters?: {}, clientRequestId?: null) { this._options = options || {}; this._parameters = parameters || {}; this.clientRequestId = clientRequestId || null; } - setOption(name, value) { + setOption(name: string, value: any) { this._options[name] = value; } - getOption(name, defaultValue) { + getOption(name: string, defaultValue?: any) { if (!this._options || this._options[name] === undefined) return defaultValue; return this._options[name]; } - setParameter(name, value) { + setParameter(name: string, value: any) { this._parameters[name] = value; } - getParameter(name, defaultValue) { + getParameter(name: string, defaultValue?: any) { if (!this._parameters || this._parameters[name] === undefined) { return defaultValue; } @@ -35,7 +41,7 @@ module.exports = class ClientRequestProperties { this._parameters = {}; } - setTimeout(timeoutMillis) { + setTimeout(timeoutMillis: number) { this.setOption("servertimeout", timeoutMillis); } @@ -43,7 +49,7 @@ module.exports = class ClientRequestProperties { return this.getOption("servertimeout"); } - setClientTimeout(timeoutMillis) { + setClientTimeout(timeoutMillis: number) { this._clientTimeOut = timeoutMillis; } @@ -56,7 +62,7 @@ module.exports = class ClientRequestProperties { } toJson() { - let json = {}; + const json: { Options?: { [option: string]: any }, Parameters?: { [option: string]: any } } = {}; if (Object.keys(this._options).length !== 0) { json.Options = this._options; @@ -76,16 +82,18 @@ module.exports = class ClientRequestProperties { return JSON.stringify(this.toJson()); } - _msToTimespan(duration) { - var milliseconds = parseInt((duration % 1000) / 100) - , seconds = parseInt((duration / 1000) % 60) - , minutes = parseInt((duration / (1000 * 60)) % 60) - , hours = parseInt((duration / (1000 * 60 * 60)) % 24); + _msToTimespan(duration: number): string { + const milliseconds = Math.floor((duration % 1000) / 100); + const seconds = Math.floor((duration / 1000) % 60); + const minutes = Math.floor((duration / (1000 * 60)) % 60); + const hours = Math.floor((duration / (1000 * 60 * 60)) % 24); - hours = (hours < 10) ? "0" + hours : hours; - minutes = (minutes < 10) ? "0" + minutes : minutes; - seconds = (seconds < 10) ? "0" + seconds : seconds; + const hoursStr = (hours < 10) ? "0" + hours : String(hours); + const minutesStr = (minutes < 10) ? "0" + minutes : String(minutes); + const secondsStr = (seconds < 10) ? "0" + seconds : String(seconds); - return hours + ":" + minutes + ":" + seconds + "." + milliseconds; + return hoursStr + ":" + minutesStr + ":" + secondsStr + "." + milliseconds; } -}; +} + +export default ClientRequestProperties; \ No newline at end of file diff --git a/azure-kusto-data/source/connectionBuilder.js b/azure-kusto-data/source/connectionBuilder.ts similarity index 72% rename from azure-kusto-data/source/connectionBuilder.js rename to azure-kusto-data/source/connectionBuilder.ts index 84fd637..a5d601d 100644 --- a/azure-kusto-data/source/connectionBuilder.js +++ b/azure-kusto-data/source/connectionBuilder.ts @@ -1,7 +1,15 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -const KeywordMapping = Object.freeze({ +import {UserCodeInfo} from "adal-node"; + +interface MappingType { + propName: string, + mappedTo: string, + validNames: string[] +} + +const KeywordMapping: { [name: string]: MappingType } = Object.freeze({ dataSource: { propName: "dataSource", mappedTo: "Data Source", @@ -44,11 +52,11 @@ const KeywordMapping = Object.freeze({ } }); -const getPropName = (key) => { - let _key = key.trim().toLowerCase(); +const getPropName = (key: string): string => { + const _key = key.trim().toLowerCase(); - for (let keyword of Object.keys(KeywordMapping)) { - let k = KeywordMapping[keyword]; + for (const keyword of Object.keys(KeywordMapping)) { + const k = KeywordMapping[keyword]; if (k.validNames.indexOf(_key) >= 0) { return k.propName; } @@ -56,8 +64,19 @@ const getPropName = (key) => { throw new Error(key); }; -module.exports = class KustoConnectionStringBuilder { - constructor(connectionString) { +export class KustoConnectionStringBuilder { + [prop: string]: string | boolean | ((info: UserCodeInfo) => void) | undefined; + dataSource?: string; + aadUserId?: string; + password?: string; + applicationClientId?: string; + applicationKey?: string; + applicationCertificate?: string; + applicationCertificateThumbprint?: string; + authorityId?: string; + AuthorizationCallback?: (info: UserCodeInfo) => void; + + constructor(connectionString: string) { if (!connectionString || connectionString.trim().length === 0) throw new Error("Missing connection string"); if (connectionString.endsWith("/") || connectionString.endsWith("\\")) { @@ -70,14 +89,14 @@ module.exports = class KustoConnectionStringBuilder { this[KeywordMapping.authorityId.propName] = "common"; - let params = connectionString.split(";"); - for (let i = 0; i < params.length; i++) { - let kvp = params[i].split("="); + const params = connectionString.split(";"); + for (const item of params) { + const kvp = item.split("="); this[getPropName(kvp[0])] = kvp[1].trim(); } } - static withAadUserPasswordAuthentication(connectionString, userId, password, authorityId) { + static withAadUserPasswordAuthentication(connectionString: string, userId: string, password: string, authorityId?: string) { if (!userId || userId.trim().length == 0) throw new Error("Invalid user"); if (!password || password.trim().length == 0) throw new Error("Invalid password"); @@ -89,7 +108,7 @@ module.exports = class KustoConnectionStringBuilder { return kcsb; } - static withAadApplicationKeyAuthentication(connectionString, aadAppId, appKey, authorityId) { + static withAadApplicationKeyAuthentication(connectionString: string, aadAppId: string, appKey: string, authorityId?: string) { if (!aadAppId || aadAppId.trim().length == 0) throw new Error("Invalid app id"); if (!appKey || appKey.trim().length == 0) throw new Error("Invalid app key"); @@ -101,7 +120,7 @@ module.exports = class KustoConnectionStringBuilder { return kcsb; } - static withAadApplicationCertificateAuthentication(connectionString, aadAppId, certificate, thumbprint, authorityId) { + static withAadApplicationCertificateAuthentication(connectionString: string, aadAppId: string, certificate: string, thumbprint: string, authorityId: string) { if (!aadAppId || aadAppId.trim().length == 0) throw new Error("Invalid app id"); if (!certificate || certificate.trim().length == 0) throw new Error("Invalid certificate"); if (!thumbprint || thumbprint.trim().length == 0) throw new Error("Invalid thumbprint"); @@ -116,7 +135,7 @@ module.exports = class KustoConnectionStringBuilder { } - static withAadDeviceAuthentication(connectionString, authorityId, authCallback) { + static withAadDeviceAuthentication(connectionString: string, authorityId: string, authCallback?: (info: UserCodeInfo) => void) { const kcsb = new KustoConnectionStringBuilder(connectionString); kcsb[KeywordMapping.authorityId.propName] = authorityId; kcsb.AuthorizationCallback = authCallback; @@ -124,8 +143,8 @@ module.exports = class KustoConnectionStringBuilder { return kcsb; } - // Notice: you can leave `msiEndpoint` and `clientId` - static withAadManagedIdentities(connectionString, msiEndpoint, clientId) { + // Notice: you can leave `msiEndpoint` and `clientId` + static withAadManagedIdentities(connectionString: string, msiEndpoint?: string, clientId?: string) { const kcsb = new KustoConnectionStringBuilder(connectionString); kcsb.msiEndpoint = msiEndpoint; @@ -144,7 +163,7 @@ module.exports = class KustoConnectionStringBuilder { return kcsb; } - static withAzLoginIdentity(connectionString) { + static withAzLoginIdentity(connectionString: string) { const kcsb = new KustoConnectionStringBuilder(connectionString); kcsb.azLoginIdentity = true; @@ -152,11 +171,13 @@ module.exports = class KustoConnectionStringBuilder { return kcsb; } - static withAccessToken(connectionString, accessToken) { + static withAccessToken(connectionString: string, accessToken?: string) { const kcsb = new KustoConnectionStringBuilder(connectionString); kcsb.accessToken = accessToken; return kcsb; } -}; +} + +export default KustoConnectionStringBuilder; \ No newline at end of file diff --git a/azure-kusto-data/source/managedIdentitiesClient.js b/azure-kusto-data/source/managedIdentitiesClient.ts similarity index 61% rename from azure-kusto-data/source/managedIdentitiesClient.js rename to azure-kusto-data/source/managedIdentitiesClient.ts index 10800c7..822eba5 100644 --- a/azure-kusto-data/source/managedIdentitiesClient.js +++ b/azure-kusto-data/source/managedIdentitiesClient.ts @@ -1,19 +1,21 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -const request = require("request"); + +// @ts-ignore +import request from "request"; const MSI_API_VERSION = "2018-02-01"; const MSI_FUNCTION_API_VERSION = "2017-09-01"; -module.exports = function acquireToken(resource, msiEndpoint, msiClientId, msiSecret, callback) { +export default function acquireToken(resource: string, msiEndpoint: string, msiClientId: string, msiSecret: string, callback: (error: string | null, token?: { tokenType: string; accessToken: string }) => T) { let msiUri = `${msiEndpoint}/?resource=${resource}&api-version=${msiSecret ? MSI_FUNCTION_API_VERSION : MSI_API_VERSION}`; if (msiClientId) { msiUri += `&client_id=${msiClientId}`; } - const headers = {}; + const headers: any = {}; if (msiSecret) { headers.Secret = msiSecret; @@ -23,7 +25,7 @@ module.exports = function acquireToken(resource, msiEndpoint, msiClientId, msiSe method: "GET", url: msiUri, headers - }, (error, response, body) => { + }, (error: string | null, response: {statusCode: number, json: string, body: string}, body: any) => { if (error) return callback(error); if (response.statusCode < 200 || response.statusCode >= 400) { @@ -31,6 +33,6 @@ module.exports = function acquireToken(resource, msiEndpoint, msiClientId, msiSe } const tokenData = JSON.parse(body); - return callback(null, { tokenType: tokenData.token_type, accessToken: tokenData.access_token }); + return callback(null, {tokenType: tokenData.token_type, accessToken: tokenData.access_token}); }); }; diff --git a/azure-kusto-data/source/models.js b/azure-kusto-data/source/models.js deleted file mode 100644 index 83055c3..0000000 --- a/azure-kusto-data/source/models.js +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -const moment = require("moment"); - -const WellKnownDataSet = { - PrimaryResult: "PrimaryResult", - QueryCompletionInformation: "QueryCompletionInformation", - TableOfContents: "TableOfContents", - QueryProperties: "QueryProperties" -}; - -module.exports.WellKnownDataSet = WellKnownDataSet; - -const ValueParser = { - datetime: moment, - timespan: moment.duration, - DateTime: moment, - TimeSpan: moment.duration, -}; - -class KustoResultRow { - constructor(columns, row) { - this.columns = columns.sort((a, b) => a.ordinal - b.ordinal); - this.raw = row; - - for (let col of this.columns) { - let parse = ValueParser[col.type]; - - this[col.name] = parse ? parse(row[col.ordinal]) : row[col.ordinal]; - } - } - - * values() { - for (let item in this.rows) { - yield item; - } - } - - getValueAt(index) { - return this[this.columns[index].name]; - } - - toJson() { - let obj = {}; - - for (let col of this.columns) { - obj[col.name] = this[col.name]; - } - - return obj; - } - - toString() { - return JSON.stringify(this.toJson()); - } -} - -module.exports.KustoResultRow = KustoResultRow; - -class KustoResultColumn { - constructor(columnObj, ordinal) { - this.name = columnObj.ColumnName; - // TODO: should validate type? should coarse value to type? - this.type = columnObj.ColumnType || columnObj.DateType; - this.ordinal = ordinal; - } -} - -module.exports.KustoResultColumn = KustoResultColumn; - -module.exports.KustoResultTable = class KustoResultTable { - constructor(tableObj) { - this.name = tableObj.TableName; - if (tableObj.TableId !== undefined) { - this.id = tableObj.TableId; - } - - if (tableObj.TableKind) { - this.kind = tableObj.TableKind; - } - - this.columns = tableObj.Columns.map((item, index) => new KustoResultColumn(item, index)); - this._rows = tableObj.Rows; - - if (this._rows && this._rows.length > 0) { - for (let i = 0; i new KustoResultRow(this.columns, this._rows[i])}); - } - } - - } - - * rows() { - for (let row of this._rows) { - yield new KustoResultRow(this.columns, row); - } - } - - toJson() { - let table = {}; - - table.name = this.name; - table.data = []; - for (let row of this.rows()) { - table.data.push(row.toJson()); - } - - return table; - } - - toString() { - return JSON.stringify(this.toJson()); - } -}; diff --git a/azure-kusto-data/source/models.ts b/azure-kusto-data/source/models.ts new file mode 100644 index 0000000..77a8422 --- /dev/null +++ b/azure-kusto-data/source/models.ts @@ -0,0 +1,142 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import moment from "moment"; + +export enum WellKnownDataSet { + PrimaryResult = "PrimaryResult", + QueryCompletionInformation = "QueryCompletionInformation", + TableOfContents = "TableOfContents", + QueryProperties = "QueryProperties" +}; + +const ValueParser: { [fromString: string]: (typeof moment | typeof moment.duration) } = { + datetime: moment, + timespan: moment.duration, + DateTime: moment, + TimeSpan: moment.duration, +} + +export interface Table { + TableKind?: string; + TableName: string; + TableId?: number; + Columns: Column[]; + Rows: any[][]; +} + +interface Column { + ColumnName: string, + ColumnType?: string, + DateType?: string +} + + +export class KustoResultRow { + columns: KustoResultColumn[]; + raw: any; + + [column: string]: any; + + constructor(columns: KustoResultColumn[], row: { [ord: number]: any }) { + this.columns = columns.sort((a, b) => a.ordinal - b.ordinal); + this.raw = row; + + for (const col of this.columns) { + const parse = ValueParser[col.type as string]; + + this[col.name as string] = parse ? parse(row[col.ordinal]) : row[col.ordinal]; + } + } + + * values() { + // tslint:disable-next-line:forin + for (const item in this.rows) { + yield item; + } + } + + getValueAt(index: number) { + return this[this.columns[index].name as string]; + } + + toJson() { + const obj: any = {}; + + for (const col of this.columns) { + obj[col.name as string] = this[col.name as string]; + } + + return obj; + } + + toString() { + return JSON.stringify(this.toJson()); + } +} + +export class KustoResultColumn { + name: string | null + type: string | null; + ordinal: number; + + constructor(columnObj: { ColumnName?: string, ColumnType?: string, DateType?: string }, ordinal: number) { + this.name = columnObj.ColumnName ?? null; + // TODO: should validate type? should coarse value to type? + this.type = (columnObj.ColumnType || columnObj.DateType) ?? null; + this.ordinal = ordinal; + } +} + +export class KustoResultTable { + name: string; + id?: number; + kind?: string; + columns: KustoResultColumn[]; + readonly _rows: any[]; + + [row: number]: any; + + constructor(tableObj: Table) { + this.name = tableObj.TableName; + if (tableObj.TableId !== undefined) { + this.id = tableObj.TableId; + } + + if (tableObj.TableKind) { + this.kind = tableObj.TableKind; + } + + this.columns = tableObj.Columns.map((item, index) => new KustoResultColumn(item, index)); + this._rows = tableObj.Rows; + + if (this._rows && this._rows.length > 0) { + for (let i = 0; i < tableObj.Rows.length; i++) { + Object.defineProperty(this, i, {get: () => new KustoResultRow(this.columns, this._rows[i])}); + } + } + + } + + * rows() { + for (const row of this._rows) { + yield new KustoResultRow(this.columns, row); + } + } + + toJson() { + const table: any = {}; + + table.name = this.name; + table.data = []; + for (const row of this.rows()) { + table.data.push(row.toJson()); + } + + return table; + } + + toString() { + return JSON.stringify(this.toJson()); + } +} diff --git a/azure-kusto-data/source/response.js b/azure-kusto-data/source/response.ts similarity index 53% rename from azure-kusto-data/source/response.js rename to azure-kusto-data/source/response.ts index 9030522..ffdb38a 100644 --- a/azure-kusto-data/source/response.js +++ b/azure-kusto-data/source/response.ts @@ -1,10 +1,53 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -const { KustoResultTable, WellKnownDataSet } = require("./models"); +import {KustoResultTable, Table, WellKnownDataSet} from "./models"; -class KustoResponseDataSet { - constructor(tables) { +interface V2DataSetHeaderFrame { + FrameType: "DataSetHeader" + IsProgressive: boolean + Version: string +} + +interface V2DataSetTableFrame extends Table { + FrameType: "DataTable" + TableId: number + TableName: string + TableKind: string + Columns: Column[] + Rows: any[][] +} + +interface V2DataSetCompletionFrame { + FrameType: "DataSetCompletion" + HasErrors: boolean + Cancelled: boolean +} + +type V2Frames = (V2DataSetHeaderFrame | V2DataSetTableFrame | V2DataSetCompletionFrame)[]; + +type V1 = { Tables: Table[] }; + +interface Column { + ColumnName: string + ColumnType: string +} + + +export abstract class KustoResponseDataSet { + tables: KustoResultTable[]; + tableNames: string[]; + primaryResults: KustoResultTable[]; + statusTable?: KustoResultTable; + abstract dataSetCompletion: { HasErrors: boolean, OneApiErrors?: any[] } | null; + + abstract getStatusColumn(): string; + + abstract getErrorColumn(): string; + + abstract getCridColumn(): string; + + protected constructor(tables: Table[]) { let _tables = tables; if (!Array.isArray(tables)) { @@ -14,8 +57,8 @@ class KustoResponseDataSet { this.tables = []; this.tableNames = []; this.primaryResults = []; - for (let table of _tables) { - let resultTable = new KustoResultTable(table); + for (const table of _tables) { + const resultTable = new KustoResultTable(table); this.tables.push(resultTable); this.tableNames.push(resultTable.name); @@ -33,8 +76,8 @@ class KustoResponseDataSet { if (this.statusTable && this.statusTable._rows.length != 0) { let minLevel = 4; - const errorColumn = this.constructor.getErrorColumn(); - for (let row of this.statusTable.rows()) { + const errorColumn = this.getErrorColumn(); + for (const row of this.statusTable.rows()) { if (row[errorColumn] < 4) { if (row[errorColumn] < minLevel) { minLevel = row[errorColumn]; @@ -45,7 +88,7 @@ class KustoResponseDataSet { } } } - if (this.dataSetCompletion && this.dataSetCompletion["HasErrors"]) { + if (this.dataSetCompletion && this.dataSetCompletion.HasErrors) { errors += 1; } @@ -56,18 +99,18 @@ class KustoResponseDataSet { const result = []; if (this.statusTable && this.statusTable._rows.length != 0) { - const errorColumn = this.constructor.getErrorColumn(); - const cridColumn = this.constructor.getCridColumn(); - const statusColumn = this.constructor.getStatusColumn(); - for (let row of this.statusTable.rows()) { + const errorColumn = this.getErrorColumn(); + const cridColumn = this.getCridColumn(); + const statusColumn = this.getStatusColumn(); + for (const row of this.statusTable.rows()) { if (row[errorColumn] < 4) { result.push(`Please provide the following data to Kusto: CRID=${row[cridColumn]} Description: ${row[statusColumn]}`); } } } - if (this.dataSetCompletion && this.dataSetCompletion["HasErrors"]) { - for (let row of this.dataSetCompletion["OneApiErrors"]) { - result.push( row["error"]["@message"]); + if (this.dataSetCompletion && this.dataSetCompletion.HasErrors && this.dataSetCompletion.OneApiErrors) { + for (const row of this.dataSetCompletion.OneApiErrors) { + result.push(row.error["@message"]); } } return result; @@ -75,12 +118,23 @@ class KustoResponseDataSet { } // TODO: should only expose 1 response type, versioning should be handled internally -module.exports.KustoResponseDataSetV1 = class KustoResponseDataSetV1 extends KustoResponseDataSet { - static getStatusColumn() { return "StatusDescription"; } - static getCridColumn() { return "ClientActivityId"; } - static getErrorColumn() { return "Severity"; } +export class KustoResponseDataSetV1 extends KustoResponseDataSet { + version: string; + dataSetCompletion: null = null; - static getTablesKinds() { + getStatusColumn() { + return "StatusDescription"; + } + + getCridColumn() { + return "ClientActivityId"; + } + + getErrorColumn() { + return "Severity"; + } + + static getTablesKinds(): { [name: string]: WellKnownDataSet } { return { "QueryResult": WellKnownDataSet.PrimaryResult, "QueryProperties": WellKnownDataSet.QueryProperties, @@ -88,7 +142,7 @@ module.exports.KustoResponseDataSetV1 = class KustoResponseDataSetV1 extends Kus }; } - constructor(data) { + constructor(data: V1) { super(data.Tables); if (this.tables.length <= 2) { @@ -108,26 +162,38 @@ module.exports.KustoResponseDataSetV1 = class KustoResponseDataSetV1 extends Kus toc.kind = WellKnownDataSet.TableOfContents; toc.id = this.tables.length - 1; for (let i = 0; i < this.tables.length - 1; i++) { - this.tables[i].name = toc[i]["Name"]; - this.tables[i].id = toc[i]["Id"]; - this.tables[i].kind = KustoResponseDataSetV1.getTablesKinds()[toc[i]["Kind"]]; + this.tables[i].name = toc[i].Name; + this.tables[i].id = toc[i].Id; + this.tables[i].kind = KustoResponseDataSetV1.getTablesKinds()[toc[i].Kind]; } } this.version = "1.0"; } -}; +} // TODO: should only expose 1 response type, versioning should be handled internally -module.exports.KustoResponseDataSetV2 = class KustoResponseDataSetV2 extends KustoResponseDataSet { - static getStatusColumn() { return "Payload"; } - static getErrorColumn() { return "Level"; } - static getCridColumn() { return "ClientRequestId"; } +export class KustoResponseDataSetV2 extends KustoResponseDataSet { + dataSetHeader: V2DataSetHeaderFrame | null; + dataSetCompletion: V2DataSetCompletionFrame | null; + version: string; - constructor(data) { - let dataTables = []; - let dataSetHeader; - let dataSetCompletion; + getStatusColumn() { + return "Payload"; + } + + getErrorColumn() { + return "Level"; + } + + getCridColumn() { + return "ClientRequestId"; + } + + constructor(data: V2Frames) { + const dataTables: V2DataSetTableFrame[] = []; + let dataSetHeader: V2DataSetHeaderFrame | null = null; + let dataSetCompletion: V2DataSetCompletionFrame | null = null; data.forEach(frame => { switch (frame.FrameType) { case "DataTable": diff --git a/azure-kusto-data/source/security.js b/azure-kusto-data/source/security.js deleted file mode 100644 index 432e11d..0000000 --- a/azure-kusto-data/source/security.js +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - - -const { AuthenticationContext } = require("adal-node"); -const acquireManagedIdentityToken = require("./managedIdentitiesClient"); -const azLoginIndentityToken = require("./azLoginIdentityClient"); - -const AuthenticationMethod = Object.freeze({ - username: 0, - appKey: 1, - appCertificate: 2, - deviceLogin: 3, - managedIdentities: 4, - azLogin: 5, - accessToken: 6, -}); - - -module.exports = class AadHelper { - constructor(kcsb) { - this.token = {}; - - const authority = kcsb.authorityId || "common"; - let url; - - // support node compatibility - try { - url = new URL(kcsb.dataSource); - } catch (e) { - const URL = require("url").URL; - url = new URL(kcsb.dataSource); - } - - const aadAuthorityUri = process.env.AadAuthorityUri; - const fullAuthorityUri = aadAuthorityUri ? - aadAuthorityUri + (aadAuthorityUri.endsWith("/") ? "" : "/") + authority - : `https://login.microsoftonline.com/${authority}`; - - this.kustoCluster = `${url.protocol}//${url.hostname}`; - this.adalContext = new AuthenticationContext(fullAuthorityUri); - if (!!kcsb.aadUserId && !!kcsb.password) { - this.authMethod = AuthenticationMethod.username; - this.clientId = "db662dc1-0cfe-4e1c-a843-19a68e65be58"; - this.username = kcsb.aadUserId; - this.password = kcsb.password; - } else if (!!kcsb.applicationClientId && !!kcsb.applicationKey) { - this.authMethod = AuthenticationMethod.appKey; - this.clientId = kcsb.applicationClientId; - this.clientSecret = kcsb.applicationKey; - } else if (!!kcsb.applicationClientId && - !!kcsb.applicationCertificate && !!kcsb.applicationCertificateThumbprint) { - this.authMethod = AuthenticationMethod.appCertificate; - this.clientId = kcsb.applicationClientId; - this.certificate = kcsb.applicationCertificate; - this.thumbprint = kcsb.applicationCertificateThumbprint; - } else if (kcsb.managedIdentity) { - this.authMethod = AuthenticationMethod.managedIdentities; - this.msiEndpoint = kcsb.msiEndpoint; - this.msiSecret = kcsb.msiSecret; - this.msiClientId = kcsb.msiClientId; - } else if (kcsb.azLoginIdentity) { - this.authMethod = AuthenticationMethod.azLogin; - } else if (kcsb.accessToken) { - this.authMethod = AuthenticationMethod.accessToken; - this.accessToken = kcsb.accessToken; - } else { - this.authMethod = AuthenticationMethod.deviceLogin; - this.clientId = "db662dc1-0cfe-4e1c-a843-19a68e65be58"; - this.authCallback = kcsb.AuthorizationCallback; - } - } - - _getAuthHeader() { - return new Promise((resolve, reject) => { - this._getAuthHeaderWithCallback((error, authHeader) => { - if (error) { - reject(error); - } else { - resolve(authHeader); - } - }); - }); - } - - _getAuthHeaderWithCallback(cb) { - let resource = this.kustoCluster; - let formatHeader = ({ tokenType, accessToken }) => `${tokenType} ${accessToken}`; - - switch (this.authMethod) { - case AuthenticationMethod.username: - return this.adalContext.acquireTokenWithUsernamePassword( - resource, this.username, this.password, this.clientId, (err, tokenResponse) => { - return cb(err, tokenResponse && formatHeader(tokenResponse)); - } - ); - case AuthenticationMethod.appKey: - return this.adalContext.acquireTokenWithClientCredentials( - resource, this.clientId, this.clientSecret, (err, tokenResponse) => { - return cb(err, tokenResponse && formatHeader(tokenResponse)); - } - ); - case AuthenticationMethod.appCertificate: - return this.adalContext.acquireTokenWithClientCertificate( - resource, this.clientId, this.certificate, this.thumbprint, (err, tokenResponse) => { - return cb(err, tokenResponse && formatHeader(tokenResponse)); - } - ); - case AuthenticationMethod.deviceLogin: - return this.adalContext.acquireUserCode(resource, this.clientId, null, (err, tokenResponse) => { - if (err) { - return cb(err); - } else { - if (this.authCallback) { - this.authCallback(tokenResponse); - } else { - console.log(tokenResponse.message); - } - - return this.adalContext.acquireTokenWithDeviceCode(resource, this.clientId, tokenResponse, (err, tokenResponse) => { - if (err) { - return cb(err); - } - - return cb(err, tokenResponse && formatHeader(tokenResponse)); - }); - - } - }); - case AuthenticationMethod.managedIdentities: - return acquireManagedIdentityToken( - resource, this.msiEndpoint, this.msiClientId, this.msiSecret, (err, tokenResponse) => { - if (err) { - return cb(err); - } - - return cb(err, tokenResponse && formatHeader(tokenResponse)); - } - ); - case AuthenticationMethod.azLogin: - return azLoginIndentityToken(resource, (err, tokenResponse) => { - if(err) { - return cb(err); - } - - return cb(err, tokenResponse && formatHeader(tokenResponse)); - }); - case AuthenticationMethod.accessToken: - return cb(undefined, `Bearer ${this.accessToken}`); - default: - return cb("Couldn't Authenticate, something went wrong trying to choose authentication method"); - } - - } -}; diff --git a/azure-kusto-data/source/security.ts b/azure-kusto-data/source/security.ts new file mode 100644 index 0000000..59348aa --- /dev/null +++ b/azure-kusto-data/source/security.ts @@ -0,0 +1,234 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + + +import {AuthenticationContext, TokenResponse, UserCodeInfo} from "adal-node"; +import acquireManagedIdentityToken from "./managedIdentitiesClient"; +import azLoginIdentityToken from "./azLoginIdentityClient"; +import KustoConnectionStringBuilder from "./connectionBuilder"; + +enum AuthenticationMethod { + username = 0, + appKey = 1, + appCertificate = 2, + deviceLogin = 3, + managedIdentities = 4, + azLogin = 5, + accessToken = 6, +} + +interface UsernameMethod { + authMethod: AuthenticationMethod.username; + clientId: string; + username: string; + password: string; +} + +interface AppKeyMethod { + authMethod: AuthenticationMethod.appKey; + clientId: string; + clientSecret: string; +} + +interface AppCertificateMethod { + authMethod: AuthenticationMethod.appCertificate; + clientId: string; + certificate: string; + thumbprint: string; +} + +interface AppManagedIdentityMethod { + authMethod: AuthenticationMethod.managedIdentities; + msiEndpoint: string; + msiSecret: string; + msiClientId: string; +} + +interface AzLoginMethod { + authMethod: AuthenticationMethod.azLogin; +} + +interface AccessTokenMethod { + authMethod: AuthenticationMethod.accessToken; + accessToken: string; +} + +interface DeviceLoginMethod { + authMethod: AuthenticationMethod.deviceLogin; + clientId: string; + authCallback: (info: UserCodeInfo) => void; +} + +type Method = + UsernameMethod + | AppKeyMethod + | AppCertificateMethod + | AppManagedIdentityMethod + | AzLoginMethod + | AccessTokenMethod + | DeviceLoginMethod; + +export class AadHelper { + token: {}; + kustoCluster: string; + adalContext: AuthenticationContext; + method: Method; + + constructor(kcsb: KustoConnectionStringBuilder) { + this.token = {}; + + const authority = kcsb.authorityId || "common"; + let url; + + if (!kcsb.dataSource) { + throw new Error("Invalid string builder - missing dataSource"); + } + + // support node compatibility + try { + url = new URL(kcsb.dataSource); // CHANGE + } catch (e) { + const URL = require("url").URL; + url = new URL(kcsb.dataSource); + } + + const aadAuthorityUri = process.env.AadAuthorityUri; + const fullAuthorityUri = aadAuthorityUri ? + aadAuthorityUri + (aadAuthorityUri.endsWith("/") ? "" : "/") + authority + : `https://login.microsoftonline.com/${authority}`; + + this.kustoCluster = `${url.protocol}//${url.hostname}`; + this.adalContext = new AuthenticationContext(fullAuthorityUri); + if (!!kcsb.aadUserId && !!kcsb.password) { + this.method = { + authMethod: AuthenticationMethod.username, + clientId: "db662dc1-0cfe-4e1c-a843-19a68e65be58", + username: kcsb.aadUserId, + password: kcsb.password, + } + } else if (!!kcsb.applicationClientId && !!kcsb.applicationKey) { + this.method = { + authMethod: AuthenticationMethod.appKey, + clientId: kcsb.applicationClientId, + clientSecret: kcsb.applicationKey, + } + } else if (!!kcsb.applicationClientId && + !!kcsb.applicationCertificate && !!kcsb.applicationCertificateThumbprint) { + this.method = { + authMethod: AuthenticationMethod.appCertificate, + clientId: kcsb.applicationClientId, + certificate: kcsb.applicationCertificate, + thumbprint: kcsb.applicationCertificateThumbprint + } + } else if (kcsb.managedIdentity) { + this.method = { + authMethod: AuthenticationMethod.managedIdentities, + msiEndpoint: kcsb.msiEndpoint as string, + msiSecret: kcsb.msiSecret as string, + msiClientId: kcsb.msiClientId as string + } + } else if (kcsb.azLoginIdentity) { + this.method = {authMethod: AuthenticationMethod.azLogin} + } else if (kcsb.accessToken) { + this.method = { + authMethod: AuthenticationMethod.accessToken, + accessToken: kcsb.accessToken as string + } + } else { + this.method = { + authMethod: AuthenticationMethod.deviceLogin, + clientId: "db662dc1-0cfe-4e1c-a843-19a68e65be58", + authCallback: kcsb.AuthorizationCallback as (info: UserCodeInfo) => void + } + } + } + + _getAuthHeader(): Promise { + return new Promise((resolve, reject) => { + this._getAuthHeaderWithCallback((error, authHeader) => { + if (error) { + reject(error); + } else { + resolve(authHeader as string); + } + }); + }); + } + + _getAuthHeaderWithCallback(cb: (e: string | Error | null | undefined, token?: string) => any) { + const resource = this.kustoCluster; + const formatHeader = ({ + tokenType, + accessToken + }: TokenResponse) => `${tokenType} ${accessToken}`; + + switch (this.method.authMethod) { + case AuthenticationMethod.username: + return this.adalContext.acquireTokenWithUsernamePassword( + resource, this.method.username, this.method.password, this.method.clientId, (err, tokenResponse) => { + return cb(err, tokenResponse && formatHeader(tokenResponse as TokenResponse)); + } + ); + case AuthenticationMethod.appKey: + return this.adalContext.acquireTokenWithClientCredentials( + resource, this.method.clientId, this.method.clientSecret, (err, tokenResponse) => { + return cb(err, tokenResponse && formatHeader(tokenResponse as TokenResponse)); + } + ); + case AuthenticationMethod.appCertificate: + return this.adalContext.acquireTokenWithClientCertificate( + resource, this.method.clientId, this.method.certificate, this.method.thumbprint, (err, tokenResponse) => { + return cb(err, tokenResponse && formatHeader(tokenResponse as TokenResponse)); + } + ); + case AuthenticationMethod.deviceLogin: + return this.adalContext.acquireUserCode(resource, this.method.clientId, "", (err, tokenResponse) => { + this.method = (this.method as DeviceLoginMethod); + if (err) { + return cb(err); + } else { + if (this.method.authCallback) { + this.method.authCallback(tokenResponse); + } else { + // tslint:disable-next-line:no-console + console.log(tokenResponse.message); + } + + return this.adalContext.acquireTokenWithDeviceCode(resource, this.method.clientId, tokenResponse, (innerError, innerResponse) => { + if (innerError) { + return cb(innerError); + } + + return cb(innerError, innerResponse && formatHeader(innerResponse as TokenResponse)); + }); + + } + }); + case AuthenticationMethod.managedIdentities: + return acquireManagedIdentityToken( + resource, this.method.msiEndpoint, this.method.msiClientId, this.method.msiSecret, (err, tokenResponse) => { + if (err) { + return cb(err); + } + + return cb(err, tokenResponse && formatHeader(tokenResponse as TokenResponse)); + } + ); + case AuthenticationMethod.azLogin: + return azLoginIdentityToken(resource, (err, tokenResponse) => { + if (err) { + return cb(err); + } + + return cb(err, tokenResponse && formatHeader(tokenResponse as TokenResponse)); + }); + case AuthenticationMethod.accessToken: + return cb(undefined, `Bearer ${this.method.accessToken}`); + default: + return cb("Couldn't Authenticate, something went wrong trying to choose authentication method"); + } + + } +}; + +export default AadHelper; \ No newline at end of file diff --git a/azure-kusto-data/test/clientTest.js b/azure-kusto-data/test/clientTest.js deleted file mode 100644 index 0036d55..0000000 --- a/azure-kusto-data/test/clientTest.js +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -const assert = require("assert"); -const v2Response = require("./data/response/v2"); -const v2ResponseError = require("./data/response/v2error"); -const v1Response = require("./data/response/v1"); -const v1_2Response = require("./data/response/v1_2"); -const uuidv4 = require("uuid/v4"); -const moment = require("moment"); -const sinon = require("sinon"); - -const KustoClient = require("../source/client"); -const KustoClientRequestProperties = require("../source/clientRequestProperties"); - -const ExecutionType = Object.freeze({ - Mgmt: 0, - Query: 1, - Ingest: 2 -}); - -describe("KustoClient", function () { - describe("#constructor", function () { - it("valid", function () { - let url = "https://cluster.kusto.windows.net"; - let client = new KustoClient(url); - - assert.equal(client.connectionString.authorityId, "common"); - assert.equal(client.connectionString.dataSource, url); - - assert.equal(client.aadHelper.authMethod, 3); - assert.equal(client.aadHelper.kustoCluster, url); - }); - }); - - describe("#_parseResponse()", function () { - it("valid v1", function () { - let url = "https://cluster.kusto.windows.net"; - let client = new KustoClient(url); - - const response = client._parseResponse(v1Response, ExecutionType.Mgmt); - assert.equal(response.version, "1.0"); - }); - - it("valid v1 more data", function () { - let url = "https://cluster.kusto.windows.net"; - let client = new KustoClient(url); - - const response = client._parseResponse(v1_2Response, ExecutionType.Mgmt); - assert.equal(response.version, "1.0"); - }); - - it("valid v2", function () { - let url = "https://cluster.kusto.windows.net"; - let client = new KustoClient(url); - - - const response = client._parseResponse(v2Response, ExecutionType.Query); - assert.equal(response.version, "2.0"); - }); - - it("valid v2 raw", function () { - let url = "https://cluster.kusto.windows.net"; - let client = new KustoClient(url); - - const response = client._parseResponse(v2Response, ExecutionType.Query, { raw: true }); - assert.equal(response, v2Response); - }); - - it("malformed body", function () { - let url = "https://cluster.kusto.windows.net"; - let client = new KustoClient(url); - try{ - const response = client._parseResponse({}, ExecutionType.Query); - } - catch(ex){ - assert.equal(ex, "Failed to parse response ({undefined}) with the following error [TypeError: data.forEach is not a function]."); - return; - } - assert.fail(); - }); - - it("erred v2 not partial", function () { - let url = "https://cluster.kusto.windows.net"; - let client = new KustoClient(url); - - try{ - const response = client._parseResponse(v2ResponseError, ExecutionType.Query); - } - catch(ex){ - assert.equal(ex.startsWith("Kusto request had errors"), true); - return; - } - assert.fail(); - }); - - it("setTimout for request", async function () { - let url = "https://cluster.kusto.windows.net"; - let client = new KustoClient(url); - - let clientRequestProps = new KustoClientRequestProperties(); - let timeoutMs = moment.duration(2.51, "minutes").asMilliseconds(); - clientRequestProps.setTimeout(timeoutMs); - client.aadHelper._getAuthHeader = () => { return "MockToken" }; - client._doRequest = (endpoint, executionType, headers, payload, timeout, properties) => { - let payloadObj = JSON.parse(payload); - assert.equal(payloadObj.properties.Options.servertimeout, "00:02:30.6"); - assert.equal(timeout, timeoutMs + moment.duration(0.5, "minutes").asMilliseconds()); - }; - - await client.execute("Database", "Table | count", clientRequestProps); - }); - - it("setClientTimout for request", async function () { - let url = "https://cluster.kusto.windows.net"; - let client = new KustoClient(url); - - let clientRequestProps = new KustoClientRequestProperties(); - let timeoutMs = moment.duration(2.51, "minutes").asMilliseconds(); - clientRequestProps.setClientTimeout(timeoutMs); - client.aadHelper._getAuthHeader = () => { return "MockToken" }; - client._doRequest = (endpoint, executionType, headers, payload, timeout, properties) => { - let payloadObj = JSON.parse(payload); - assert.equal(timeout, timeoutMs); - }; - - await client.execute("Database", "Table | count", clientRequestProps); - }); - - it("default timeout for query", async function () { - let url = "https://cluster.kusto.windows.net"; - let client = new KustoClient(url); - - client.aadHelper._getAuthHeader = () => { return "MockToken" }; - client._doRequest = (endpoint, executionType, headers, payload, timeout, properties) => { - assert.equal(timeout, moment.duration(4.5, "minutes").asMilliseconds()); - }; - - await client.execute("Database", "Table | count", () => { }); - }); - - it("default timeout for admin", async function () { - let url = "https://cluster.kusto.windows.net"; - let client = new KustoClient(url); - client.aadHelper._getAuthHeader = () => { return "MockToken" }; - client._doRequest = (endpoint, executionType, headers, payload, timeout, properties) => { - assert.equal(timeout, moment.duration(10.5, "minutes").asMilliseconds()); - }; - - await client.execute("Database", ".show database DataBase schema"); - }); - - it("set clientRequestId for request", async function () { - let url = "https://cluster.kusto.windows.net"; - let client = new KustoClient(url); - const clientRequestId = `MyApp.MyActivity;${uuidv4()}`; - - let clientRequestProps = new KustoClientRequestProperties(); - clientRequestProps.clientRequestId = clientRequestId; - client.aadHelper._getAuthHeader = () => { return "MockToken" }; - client._doRequest = (endpoint, executionType, headers, payload, timeout, properties) => { - assert.equal(headers["x-ms-client-request-id"], clientRequestId); - }; - - await client.execute("Database", "Table | count", clientRequestProps); - }); - }); -}); diff --git a/azure-kusto-data/test/clientTest.ts b/azure-kusto-data/test/clientTest.ts new file mode 100644 index 0000000..66793a7 --- /dev/null +++ b/azure-kusto-data/test/clientTest.ts @@ -0,0 +1,185 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import assert from "assert"; + +// tslint:disable-next-line:no-var-requires +import uuid from "uuid"; +import moment from "moment"; + +import {KustoClient} from "../source/client"; + +import {ClientRequestProperties} from "../source/clientRequestProperties"; +import { + KustoResponseDataSet, + KustoResponseDataSetV1, + KustoResponseDataSetV2 +} from "../source/response"; + +// tslint:disable-next-line:no-var-requires +const v2Response = require("./data/response/v2"); +// tslint:disable-next-line:no-var-requires +const v2ResponseError = require("./data/response/v2error"); +// tslint:disable-next-line:no-var-requires +const v1Response = require("./data/response/v1"); +// tslint:disable-next-line:no-var-requires variable-name +const v1_2Response = require("./data/response/v1_2"); + +const ExecutionType = Object.freeze({ + Mgmt: 0, + Query: 1, + Ingest: 2 +}); + +describe("KustoClient", function () { + describe("#constructor", function () { + it("valid", function () { + const url = "https://cluster.kusto.windows.net"; + const client = new KustoClient(url); + + assert.equal(client.connectionString.authorityId, "common"); + assert.equal(client.connectionString.dataSource, url); + + assert.equal(client.aadHelper.method.authMethod, 3); + assert.equal(client.aadHelper.kustoCluster, url); + }); + }); + + describe("#_parseResponse()", function () { + it("valid v1", function () { + const url = "https://cluster.kusto.windows.net"; + const client = new KustoClient(url); + + const response = client._parseResponse(v1Response, ExecutionType.Mgmt); + assert.equal((response as KustoResponseDataSetV1).version, "1.0"); + }); + + it("valid v1 more data", function () { + const url = "https://cluster.kusto.windows.net"; + const client = new KustoClient(url); + + const response = client._parseResponse(v1_2Response, ExecutionType.Mgmt); + assert.equal((response as KustoResponseDataSetV1).version, "1.0"); + }); + + it("valid v2", function () { + const url = "https://cluster.kusto.windows.net"; + const client = new KustoClient(url); + + + const response = client._parseResponse(v2Response, ExecutionType.Query); + assert.equal((response as KustoResponseDataSetV2).version, "2.0"); + }); + + it("valid v2 raw", function () { + const url = "https://cluster.kusto.windows.net"; + const client = new KustoClient(url); + + const response = client._parseResponse(v2Response, ExecutionType.Query, { raw: true } as ClientRequestProperties); + assert.equal(response, v2Response); + }); + + it("malformed body", function () { + const url = "https://cluster.kusto.windows.net"; + const client = new KustoClient(url); + try{ + const response = client._parseResponse({}, ExecutionType.Query); + } + catch(ex){ + ex.message.startsWith("Failed to parse response ({undefined}) with the following error [TypeError: data.forEach is not a function]."); + return; + } + assert.fail(); + }); + + it("erred v2 not partial", function () { + const url = "https://cluster.kusto.windows.net"; + const client = new KustoClient(url); + + try{ + const response = client._parseResponse(v2ResponseError, ExecutionType.Query); + } + catch(ex){ + assert.equal(ex.message.startsWith("Kusto request had errors"), true); + return; + } + assert.fail(); + }); + + it("setTimout for request", async function () { + const url = "https://cluster.kusto.windows.net"; + const client = new KustoClient(url); + + const clientRequestProps = new ClientRequestProperties(); + const timeoutMs = moment.duration(2.51, "minutes").asMilliseconds(); + clientRequestProps.setTimeout(timeoutMs); + client.aadHelper._getAuthHeader = () => { return Promise.resolve("MockToken") }; + client._doRequest = (endpoint, executionType, headers, payload, timeout, properties) => { + const payloadObj = JSON.parse(payload); + assert.equal(payloadObj.properties.Options.servertimeout, "00:02:30.6"); + assert.equal(timeout, timeoutMs + moment.duration(0.5, "minutes").asMilliseconds()); + return Promise.resolve(new KustoResponseDataSetV2([])); + }; + + await client.execute("Database", "Table | count", clientRequestProps); + }); + + it("setClientTimout for request", async function () { + const url = "https://cluster.kusto.windows.net"; + const client = new KustoClient(url); + + const clientRequestProps = new ClientRequestProperties(); + const timeoutMs = moment.duration(2.51, "minutes").asMilliseconds(); + clientRequestProps.setClientTimeout(timeoutMs); + client.aadHelper._getAuthHeader = () => { return Promise.resolve("MockToken") }; + client._doRequest = (endpoint, executionType, headers, payload, timeout, properties) => { + const payloadObj = JSON.parse(payload); + assert.equal(timeout, timeoutMs); + return Promise.resolve(new KustoResponseDataSetV2([])); + }; + + await client.execute("Database", "Table | count", clientRequestProps); + }); + + it("default timeout for query", async function () { + const url = "https://cluster.kusto.windows.net"; + const client = new KustoClient(url); + + client.aadHelper._getAuthHeader = () => { return Promise.resolve("MockToken") }; + client._doRequest = (endpoint, executionType, headers, payload, timeout, properties) => { + assert.equal(timeout, moment.duration(4.5, "minutes").asMilliseconds()); + return Promise.resolve(new KustoResponseDataSetV2([])); + }; + + await client.execute("Database", "Table | count"); + }); + + it("default timeout for admin", async function () { + const url = "https://cluster.kusto.windows.net"; + const client = new KustoClient(url); + client.aadHelper._getAuthHeader = () => { return Promise.resolve("MockToken") }; + client._doRequest = (endpoint, executionType, headers, payload, timeout, properties) => { + assert.equal(timeout, moment.duration(10.5, "minutes").asMilliseconds()); + return Promise.resolve(new KustoResponseDataSetV2([])); + }; + + await client.execute("Database", ".show database DataBase schema"); + }); + + it("set clientRequestId for request", async function () { + const url = "https://cluster.kusto.windows.net"; + const client = new KustoClient(url); + const clientRequestId = `MyApp.MyActivity;${uuid.v4()}`; + + const clientRequestProps = new ClientRequestProperties(); + clientRequestProps.clientRequestId = clientRequestId; + client.aadHelper._getAuthHeader = () => { return Promise.resolve("MockToken") }; + client._doRequest = (endpoint, executionType, headers, payload, timeout, properties) => { + assert.equal(headers["x-ms-client-request-id"], clientRequestId); + return Promise.resolve(new KustoResponseDataSetV2([])); + }; + + await client.execute("Database", "Table | count", clientRequestProps); + }); + }); +}); diff --git a/azure-kusto-data/test/connectionBuilderTest.js b/azure-kusto-data/test/connectionBuilderTest.ts similarity index 74% rename from azure-kusto-data/test/connectionBuilderTest.js rename to azure-kusto-data/test/connectionBuilderTest.ts index 205b4d0..002b648 100644 --- a/azure-kusto-data/test/connectionBuilderTest.js +++ b/azure-kusto-data/test/connectionBuilderTest.ts @@ -1,14 +1,16 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -const assert = require("assert"); -const uuidv4 = require("uuid/v4"); +import assert from "assert"; + +import uuid from "uuid"; + +import {KustoConnectionStringBuilder} from "../source/connectionBuilder"; -const KustoConnectionStringBuilder = require("../source/connectionBuilder"); describe("KustoConnectionStringBuilder", function () { describe("#constructor(connectionString)", function () { it("from string with no creds", function () { - let kcsbs = [ + const kcsbs = [ new KustoConnectionStringBuilder("localhost"), new KustoConnectionStringBuilder("data Source=localhost"), new KustoConnectionStringBuilder("Addr=localhost"), @@ -16,11 +18,11 @@ describe("KustoConnectionStringBuilder", function () { KustoConnectionStringBuilder.withAadDeviceAuthentication("localhost", "common"), ]; - for (let kcsb of kcsbs) { + for (const kcsb of kcsbs) { assert.equal(kcsb.dataSource, "localhost"); assert.equal(kcsb.authorityId, "common"); - let emptyFields = ["aadUserId", "password", "applicationClientId", "applicationKey"]; - for (let field of emptyFields) { + const emptyFields = ["aadUserId", "password", "applicationClientId", "applicationKey"]; + for (const field of emptyFields) { assert.equal(kcsb[field], null); } } @@ -41,13 +43,13 @@ describe("KustoConnectionStringBuilder", function () { kcsb1.password = expectedPassword; kcsbs.push(kcsb1); - for (let kcsb of kcsbs) { + for (const kcsb of kcsbs) { assert.equal(kcsb.dataSource, "localhost"); assert.equal(kcsb.aadUserId, expectedUser); assert.equal(kcsb.password, expectedPassword); assert.equal(kcsb.authorityId, "common"); - let emptyFields = ["applicationClientId", "applicationKey"]; - for (let field of emptyFields) { + const emptyFields = ["applicationClientId", "applicationKey"]; + for (const field of emptyFields) { assert.equal(kcsb[field], null); } } @@ -55,35 +57,35 @@ describe("KustoConnectionStringBuilder", function () { it("from string with app auth", function () { - const uuid = uuidv4(); + const uuidv4 = uuid.v4(); const key = "key of application"; - let kcsbs = [ - new KustoConnectionStringBuilder(`localhost;Application client Id=${uuid};application Key=${key}`), - new KustoConnectionStringBuilder(`Data Source=localhost ; Application Client Id=${uuid}; Appkey =${key}`), - new KustoConnectionStringBuilder(` Addr = localhost ; AppClientId = ${uuid} ; AppKey =${key}`), - new KustoConnectionStringBuilder(`Network Address = localhost; AppClientId = ${uuid} ; AppKey =${key}`), - KustoConnectionStringBuilder.withAadApplicationKeyAuthentication("localhost", uuid, key) + const kcsbs = [ + new KustoConnectionStringBuilder(`localhost;Application client Id=${uuidv4};application Key=${key}`), + new KustoConnectionStringBuilder(`Data Source=localhost ; Application Client Id=${uuidv4}; Appkey =${key}`), + new KustoConnectionStringBuilder(` Addr = localhost ; AppClientId = ${uuidv4} ; AppKey =${key}`), + new KustoConnectionStringBuilder(`Network Address = localhost; AppClientId = ${uuidv4} ; AppKey =${key}`), + KustoConnectionStringBuilder.withAadApplicationKeyAuthentication("localhost", uuidv4, key) ]; - let kcsb1 = new KustoConnectionStringBuilder("server=localhost"); - kcsb1.applicationClientId = uuid; + const kcsb1 = new KustoConnectionStringBuilder("server=localhost"); + kcsb1.applicationClientId = uuidv4; kcsb1.applicationKey = key; kcsbs.push(kcsb1); - for (let kcsb of kcsbs) { + for (const kcsb of kcsbs) { assert.equal(kcsb.dataSource, "localhost"); - assert.equal(kcsb.applicationClientId, uuid); + assert.equal(kcsb.applicationClientId, uuidv4); assert.equal(kcsb.applicationKey, key); assert.equal(kcsb.authorityId, "common"); - let emptyFields = ["aadUserId", "password"]; - for (let field of emptyFields) { + const emptyFields = ["aadUserId", "password"]; + for (const field of emptyFields) { assert.equal(kcsb[field], null); } } }); - it("from string with managed identity", function () { + it("from string with managed identity", function () { const kcsb1 = KustoConnectionStringBuilder.withAadManagedIdentities("https://dadubovs1.westus.kusto.windows.net"); assert.equal(kcsb1.msiEndpoint, "http://169.254.169.254/metadata/identity/oauth2/token"); diff --git a/azure-kusto-data/test/modelsTest.js b/azure-kusto-data/test/modelsTest.ts similarity index 90% rename from azure-kusto-data/test/modelsTest.js rename to azure-kusto-data/test/modelsTest.ts index 88c34a8..f3479ed 100644 --- a/azure-kusto-data/test/modelsTest.js +++ b/azure-kusto-data/test/modelsTest.ts @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -const assert = require("assert"); -const moment = require("moment"); - -const { KustoResultTable, KustoResultColumn, KustoResultRow } = require("../source/models"); +import assert from "assert"; +import moment from "moment"; +import {KustoResultColumn, KustoResultRow, KustoResultTable} from "../source/models"; +// tslint:disable-next-line:no-var-requires const v2Response = require("./data/response/v2.json"); describe("KustoResultRow", function () { @@ -64,31 +64,31 @@ describe("KustoResultRow", function () { 3493235670000 ]; - const reverseOrderColumns = rawColumns.slice().reverse(); + const reverseOrderColumns = rawColumns.slice().reverse(); const actual = new KustoResultRow( reverseOrderColumns.map((c, i) => new KustoResultColumn(c, rawColumns.length - i - 1)), inputValues ); - let asJson = actual.toJson(); - let expectedValues = [ - moment(inputValues[0]), + const asJson = actual.toJson(); + const expectedValues = [ + moment(inputValues[0] as string), inputValues[1], inputValues[2], inputValues[3], inputValues[4], - moment(inputValues[5]), + moment(inputValues[5] as number), ]; - for (let index = 0; index < inputColumns.length; index++) { - let actual = asJson[inputColumns[index].name]; + for (let index = 0; index < inputColumns.length; index++) { + const currentActual = asJson[inputColumns[index].name as string]; if (inputColumns[index].type === "timespan") { - assert.equal(Number(actual), expectedValues[index]); + assert.equal(Number(currentActual), expectedValues[index]); } - else if (typeof(actual) == "object") { - assert.equal(actual.toString(), expectedValues[index].toString()); + else if (typeof(currentActual) == "object") { + assert.equal(currentActual.toString(), expectedValues[index].toString()); } else { - assert.equal(actual, expectedValues[index]); + assert.equal(currentActual, expectedValues[index]); } } @@ -156,7 +156,7 @@ describe("KustoResultRow", function () { let i = 0; - for (let v of actual.values()) { + for (const v of actual.values()) { assert.equal(v, inputValues[i]); values.push(v); i++; @@ -284,8 +284,8 @@ describe("KustoResultTable", function () { it("iterate over rows", function () { const actual = new KustoResultTable(v2Response[2]); - let rows = []; - for (let row of actual.rows()) { + const rows = []; + for (const row of actual.rows()) { rows.push(row); assert.equal( JSON.stringify(row), diff --git a/azure-kusto-data/test/responseTest.js b/azure-kusto-data/test/responseTest.ts similarity index 100% rename from azure-kusto-data/test/responseTest.js rename to azure-kusto-data/test/responseTest.ts diff --git a/azure-kusto-data/tsconfig.json b/azure-kusto-data/tsconfig.json new file mode 100644 index 0000000..0cfc81d --- /dev/null +++ b/azure-kusto-data/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "esModuleInterop": true, + "strict": true, + "incremental": true, + "sourceMap": true, + "resolveJsonModule": true, + "allowJs": false, + "allowSyntheticDefaultImports": true, + "declaration": true + }, + "include": ["source/**/*.ts", "test/**/*.ts", "index.ts"] +} \ No newline at end of file diff --git a/azure-kusto-data/tslint.json b/azure-kusto-data/tslint.json new file mode 100644 index 0000000..15dab24 --- /dev/null +++ b/azure-kusto-data/tslint.json @@ -0,0 +1,18 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:recommended" + ], + "jsRules": {}, + "rules": { + "max-classes-per-file": false, + "triple-equals": false, + "only-arrow-functions": false + }, + "linterOptions": { + "exclude": [ + "./test/**/*" + ] + }, + "rulesDirectory": [] +} \ No newline at end of file diff --git a/azure-kusto-ingest/.mocharc.json b/azure-kusto-ingest/.mocharc.json new file mode 100644 index 0000000..73ef947 --- /dev/null +++ b/azure-kusto-ingest/.mocharc.json @@ -0,0 +1,3 @@ +{ + "enable-source-maps": true +} \ No newline at end of file diff --git a/azure-kusto-ingest/example.js b/azure-kusto-ingest/example.js index fe2f1a3..48774e8 100644 --- a/azure-kusto-ingest/example.js +++ b/azure-kusto-ingest/example.js @@ -2,7 +2,7 @@ // Licensed under the MIT License. const IngestClient = require("azure-kusto-ingest").IngestClient; -const IngestStatusQueues = require("azure-kusto-ingest").KustoIngestStatusQueues; +const IngestStatusQueues = require("azure-kusto-ingest").IngestStatusQueues; const IngestionProps = require("azure-kusto-ingest").IngestionProperties; const { ReportLevel, ReportMethod } = require("azure-kusto-ingest").IngestionPropertiesEnums; const KustoConnectionStringBuilder = require("azure-kusto-data").KustoConnectionStringBuilder; @@ -115,7 +115,7 @@ async function startStreamingIngestion() { } // Ingest from stream with either ReadStream or StreamDescriptor - const stream = fs.createReadStream("file.json"); + let stream = fs.createReadStream("file.json"); try { await streamingIngestClient.ingestFromStream("file.json", props2); console.log("Ingestion done"); @@ -125,7 +125,7 @@ async function startStreamingIngestion() { } // For gzip data set StreamDescriptor.compressionType to CompressionType.GZIP - const stream = fs.createReadStream("file.json.gz"); + stream = fs.createReadStream("file.json.gz"); const streamDescriptor = new StreamDescriptor(stream, "id", CompressionType.GZIP); try { await streamingIngestClient.ingestFromStream(streamDescriptor, props2); diff --git a/azure-kusto-ingest/index.js b/azure-kusto-ingest/index.ts similarity index 65% rename from azure-kusto-ingest/index.js rename to azure-kusto-ingest/index.ts index 443dece..852204f 100644 --- a/azure-kusto-ingest/index.js +++ b/azure-kusto-ingest/index.ts @@ -1,30 +1,32 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -const client = require("./source/ingestClient"); -const streamingIngestClient = require("./source/streamingIngestClient"); -const KustoIngestStatusQueues = require("./source/status"); -const { +import client from "./source/ingestClient"; + +import streamingIngestClient from "./source/streamingIngestClient"; + +import KustoIngestStatusQueues from "./source/status"; + +import { + CsvColumnMapping, DataFormat, IngestionMappingType, IngestionProperties, JsonColumnMapping, - CsvColumnMapping, - ValidationPolicy, ReportLevel, ReportMethod, ValidationImplications, ValidationOptions, - DataFormat, - IngestionMappingType -} = require("./source/ingestionProperties"); + ValidationPolicy +} from "./source/ingestionProperties"; -const { + +import { BlobDescriptor, + CompressionType, FileDescriptor, - StreamDescriptor, - CompressionType -} = require("./source/descriptors"); + StreamDescriptor +} from "./source/descriptors"; -module.exports = { +const out = { IngestClient: client, StreamingIngestClient: streamingIngestClient, IngestStatusQueues: KustoIngestStatusQueues, @@ -47,3 +49,5 @@ module.exports = { CompressionType } }; + +export default out; \ No newline at end of file diff --git a/azure-kusto-ingest/package-lock.json b/azure-kusto-ingest/package-lock.json index 9c3c027..9cc9230 100644 --- a/azure-kusto-ingest/package-lock.json +++ b/azure-kusto-ingest/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-kusto-ingest", - "version": "1.0.1", + "version": "2.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -155,9 +155,9 @@ "integrity": "sha512-dG76W7ElfLi+fbTjnZVGj+M9e0BIEJmRxU6fHaUQ12bZBe8EJKYb2GV50YWNaP2uJiVQ5+7nXEVj1VN1UQtaEw==" }, "@azure/ms-rest-js": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.0.8.tgz", - "integrity": "sha512-PO4pnYaF66IAB/RWbhrTprGyhOzDzsgcbT7z8k3O38JKlwifbrhW+8M0fzx0ScZnaacP8rZyBazYMUF9P12c0g==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.1.0.tgz", + "integrity": "sha512-4BXLVImYRt+jcUmEJ5LUWglI8RBNVQndY6IcyvQ4U8O4kIXdmlRz3cJdA/RpXf5rKT38KOoTO2T6Z1f6Z1HDBg==", "requires": { "@types/node-fetch": "^2.3.7", "@types/tunnel": "0.0.1", @@ -181,11 +181,6 @@ "mime-types": "^2.1.12" } }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, "tough-cookie": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", @@ -195,27 +190,13 @@ "psl": "^1.1.28", "punycode": "^2.1.1" } - }, - "xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - }, - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" } } }, "@azure/ms-rest-nodeauth": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@azure/ms-rest-nodeauth/-/ms-rest-nodeauth-3.0.5.tgz", - "integrity": "sha512-GoP9tn4rFNHJqE00+ARtHmPKufC3h4j7xEuyveOueUrguLT/Q0c5aEPgS9bmXWiHGoreRn2hVGGwd3m8oDdV3g==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-nodeauth/-/ms-rest-nodeauth-3.0.6.tgz", + "integrity": "sha512-2twuzsXHdKMzEFI2+Sr82o6yS4ppNGZceYwil8PFo+rJxOZIoBm9e0//YC+dKilV/3F+6K/HuW8LdskDrJEQWA==", "requires": { "@azure/ms-rest-azure-env": "^2.0.0", "@azure/ms-rest-js": "^2.0.4", @@ -332,16 +313,16 @@ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "dev": true }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "@types/mocha": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.0.tgz", + "integrity": "sha512-/Sge3BymXo4lKc31C8OINJgXLaw+7vL1/L1pGiBNpGrBiT8FQiaFpSYV0uhTaG4y78vcMBTMFsWaHDvuD+xGzQ==", "dev": true }, "@types/node": { - "version": "8.10.59", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.59.tgz", - "integrity": "sha512-8RkBivJrDCyPpBXhVZcjh7cQxVBSmRk9QM7hOketZzp6Tg79c0N8kkpAIito9bnJ3HCVCHVYz+KHTEbfQNfeVQ==" + "version": "14.14.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.13.tgz", + "integrity": "sha512-vbxr0VZ8exFMMAjCW8rJwaya0dMCDyYW2ZRdTyjtrCvJoENMpdUHOT/eTzvgyA5ZnqRZ/sI0NwqAxNHKYokLJQ==" }, "@types/node-fetch": { "version": "2.5.7", @@ -364,6 +345,21 @@ } } }, + "@types/sinon": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.9.tgz", + "integrity": "sha512-z/y8maYOQyYLyqaOB+dYQ6i0pxKLOsfwCmHmn4T7jS/SDHicIslr37oE3Dg8SCqKrKeBy6Lemu7do2yy+unLrw==", + "dev": true, + "requires": { + "@types/sinonjs__fake-timers": "*" + } + }, + "@types/sinonjs__fake-timers": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz", + "integrity": "sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg==", + "dev": true + }, "@types/tunnel": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.1.tgz", @@ -372,6 +368,18 @@ "@types/node": "*" } }, + "@types/uuid": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz", + "integrity": "sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==", + "dev": true + }, + "@types/uuid-validate": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@types/uuid-validate/-/uuid-validate-0.0.1.tgz", + "integrity": "sha512-RbX9q0U00SLoV+l7loYX0Wrtv4QTClBC0QcdNts6x2b5G1HJN8NI9YlS1HNA6THrI9EH3OXSgya6eMQIlDjKFA==", + "dev": true + }, "abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -380,18 +388,6 @@ "event-target-shim": "^5.0.0" } }, - "acorn": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", - "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", - "dev": true - }, - "acorn-jsx": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", - "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", - "dev": true - }, "adal-node": { "version": "0.1.28", "resolved": "https://registry.npmjs.org/adal-node/-/adal-node-0.1.28.tgz", @@ -406,6 +402,13 @@ "uuid": "^3.1.0", "xmldom": ">= 0.1.x", "xpath.js": "~1.1.0" + }, + "dependencies": { + "@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==" + } } }, "ajv": { @@ -425,23 +428,6 @@ "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", "dev": true }, - "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", - "dev": true, - "requires": { - "type-fest": "^0.11.0" - }, - "dependencies": { - "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", - "dev": true - } - } - }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", @@ -467,6 +453,12 @@ "picomatch": "^2.0.4" } }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -495,12 +487,6 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true - }, "async": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/async/-/async-3.1.0.tgz", @@ -530,9 +516,9 @@ } }, "azure-kusto-data": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/azure-kusto-data/-/azure-kusto-data-1.0.0.tgz", - "integrity": "sha512-AaDRgR96P33tdMqs+MNQmX8TGLYBOU0EsHGapLk2netQ/Ww5YLNV5zdDGQs7LQ10+yec7KgV05RBtAlUkjAkFg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/azure-kusto-data/-/azure-kusto-data-1.0.3.tgz", + "integrity": "sha512-f2LECBAuUA1k2n5wA6HJ2MIgKR1P9eTUWu2IAcaGA3UsKg0j0VCglCpdlzKKYB24LzsxjNUgcvqFSmSKSY9/8g==", "requires": { "@azure/ms-rest-nodeauth": "^3.0.3", "adal-node": "^0.1.28", @@ -541,11 +527,6 @@ "uuid": "^3.4.0" }, "dependencies": { - "moment": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.28.0.tgz", - "integrity": "sha512-Z5KOjYmnHyd/ukynmFd/WwyXHd7L4J9vTI/nn5Ap9AVUgaAE15VvQ9MOGmJJygEUklupqIrFnor/tjTwRU+tQw==" - }, "uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", @@ -603,10 +584,15 @@ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "dev": true }, "camelcase": { @@ -642,12 +628,6 @@ } } }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, "chokidar": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", @@ -664,21 +644,6 @@ "readdirp": "~3.2.0" } }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true - }, "cliui": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", @@ -741,6 +706,12 @@ "delayed-stream": "~1.0.0" } }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -752,18 +723,11 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, - "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" - } + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true }, "dashdash": { "version": "1.14.1", @@ -793,12 +757,6 @@ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -819,15 +777,6 @@ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -887,169 +836,12 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, - "eslint": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", - "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.10.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^1.4.3", - "eslint-visitor-keys": "^1.1.0", - "espree": "^6.1.2", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^7.0.0", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.14", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.3", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^6.1.2", - "strip-ansi": "^5.2.0", - "strip-json-comments": "^3.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - } - } - }, - "eslint-scope": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", - "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - }, - "espree": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", - "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", - "dev": true, - "requires": { - "acorn": "^7.1.1", - "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.1.0" - } - }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, - "esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", - "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, "event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -1065,17 +857,6 @@ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", @@ -1091,30 +872,6 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", - "dev": true, - "requires": { - "flat-cache": "^2.0.1" - } - }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -1142,23 +899,6 @@ "is-buffer": "~2.0.3" } }, - "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", - "dev": true, - "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" - } - }, - "flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "dev": true - }, "follow-redirects": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", @@ -1216,12 +956,6 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -1259,15 +993,6 @@ "is-glob": "^4.0.1" } }, - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -1325,37 +1050,6 @@ "sshpk": "^1.7.0" } }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1372,117 +1066,6 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "inquirer": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.2.tgz", - "integrity": "sha512-DF4osh1FM6l0RJc5YWYhSDB6TawiBRlbV9Cox8MWlidU218Tb7fm3lQTULyUJDfJ0tjbzl0W4q651mrCCEM55w==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.16", - "mute-stream": "0.0.8", - "run-async": "^2.4.0", - "rxjs": "^6.6.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, "ip-regex": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", @@ -1509,6 +1092,15 @@ "integrity": "sha512-wliAfSzx6V+6WfMOmus1xy0XvSgf/dlStkvTfq7F0g4bOIW0PSUbnyse3NhDwdyYS1ozfUtAAySqTws3z9Eqgg==", "dev": true }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "is-date-object": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", @@ -1607,12 +1199,6 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -1654,16 +1240,6 @@ "safe-buffer": "^5.0.1" } }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -1695,6 +1271,12 @@ "integrity": "sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg==", "dev": true }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "mime-db": { "version": "1.42.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", @@ -1708,12 +1290,6 @@ "mime-db": "1.42.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 - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -1771,9 +1347,9 @@ } }, "moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" }, "ms": { "version": "2.1.1", @@ -1781,24 +1357,6 @@ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, "nise": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.2.tgz", @@ -1881,35 +1439,6 @@ "wrappy": "1" } }, - "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -1934,15 +1463,6 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -1955,10 +1475,10 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, "path-to-regexp": { @@ -1989,23 +1509,11 @@ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, "psl": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", @@ -2030,12 +1538,6 @@ "picomatch": "^2.0.4" } }, - "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", - "dev": true - }, "request": { "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", @@ -2075,44 +1577,14 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", "dev": true, "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true - }, - "rxjs": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.0.tgz", - "integrity": "sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==", - "dev": true, - "requires": { - "tslib": "^1.9.0" + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" } }, "safe-buffer": { @@ -2142,27 +1614,6 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, "sinon": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.5.0.tgz", @@ -2189,15 +1640,18 @@ } } }, - "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "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==" + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, "sprintf-js": { @@ -2276,67 +1730,6 @@ "has-flag": "^3.0.0" } }, - "table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", - "dev": true, - "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2362,11 +1755,71 @@ } } }, + "ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + } + } + }, "tslib": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" }, + "tslint": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", + "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^4.0.1", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.3", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.13.0", + "tsutils": "^2.29.0" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + } + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, "tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", @@ -2385,25 +1838,16 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "typescript": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", + "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", "dev": true }, "underscore": { @@ -2434,12 +1878,6 @@ "resolved": "https://registry.npmjs.org/uuid-validate/-/uuid-validate-0.0.3.tgz", "integrity": "sha512-Fykw5U4eZESbq739BeLvEBFRuJODfrlmjx5eJux7W817LjRaq4b7/i4t2zxQmhcX+fAj4nMfRdTzO4tmwLKn0w==" }, - "v8-compile-cache": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", - "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", - "dev": true - }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -2474,12 +1912,6 @@ "string-width": "^1.0.2 || 2" } }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, "wrap-ansi": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", @@ -2525,15 +1957,6 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, "xml2js": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", @@ -2630,6 +2053,12 @@ "lodash": "^4.17.15", "yargs": "^13.3.0" } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true } } } diff --git a/azure-kusto-ingest/package.json b/azure-kusto-ingest/package.json index 0e0b6f9..293c575 100644 --- a/azure-kusto-ingest/package.json +++ b/azure-kusto-ingest/package.json @@ -1,6 +1,6 @@ { "name": "azure-kusto-ingest", - "version": "1.0.1", + "version": "2.0.0", "description": "Azure Data Explorer Ingestion SDK", "main": "index.js", "engines": { @@ -17,10 +17,13 @@ "kusto" ], "scripts": { - "example": "node example.js", - "lint": "eslint source --quiet", - "test": "mocha", - "allTests": "mocha --timeout 240000 --recursive" + "build": "npm link ../azure-kusto-data && tsc -b", + "prepublish": "npm run build", + "example": "npm run build && node example.js", + "lint": "npm run build && tslint --project tsconfig.json --quiet", + "test": "npm run build && mocha --require ts-node/register", + "e2e": "npm run build && mocha --require ts-node/register test/e2eTests/e2eTest.ts", + "allTests": "npm run build && mocha --timeout 240000 --recursive --require ts-node/register" }, "author": "", "license": "ISC", @@ -35,8 +38,15 @@ "uuid-validate": "0.0.3" }, "devDependencies": { - "eslint": "^6.8.0", + "@types/mocha": "^8.2.0", + "@types/node": "^14.14.13", + "@types/sinon": "^9.0.9", + "@types/uuid": "^8.3.0", + "@types/uuid-validate": "0.0.1", "mocha": "^7.2.0", - "sinon": "^7.2.3" + "sinon": "^7.2.3", + "ts-node": "^9.1.1", + "tslint": "^6.1.3", + "typescript": "^4.1.3" } } diff --git a/azure-kusto-ingest/source/abstractKustoClient.ts b/azure-kusto-ingest/source/abstractKustoClient.ts new file mode 100644 index 0000000..99bc1ce --- /dev/null +++ b/azure-kusto-ingest/source/abstractKustoClient.ts @@ -0,0 +1,26 @@ +import IngestionProperties from "./ingestionProperties"; +import {FileDescriptor, StreamDescriptor} from "./descriptors"; +import fs from "fs"; + +export abstract class AbstractKustoClient { + protected constructor(public defaultProps: IngestionProperties | null = null) { + } + + _mergeProps(newProperties?: IngestionProperties | null): IngestionProperties { + // no default props + if (newProperties == null || Object.keys(newProperties).length == 0) { + return this.defaultProps || new IngestionProperties({}); + } + + // no new props + if (this.defaultProps == null || Object.keys(this.defaultProps).length == 0) { + return newProperties || new IngestionProperties({}); + } + // both exist - merge + return this.defaultProps.merge(newProperties) || new IngestionProperties({}); + } + + abstract ingestFromStream(stream: StreamDescriptor | fs.ReadStream, ingestionProperties: IngestionProperties): Promise; + + abstract ingestFromFile(file: FileDescriptor | string, ingestionProperties: IngestionProperties): Promise; +} \ No newline at end of file diff --git a/azure-kusto-ingest/source/descriptors.js b/azure-kusto-ingest/source/descriptors.js deleted file mode 100644 index 1d3f700..0000000 --- a/azure-kusto-ingest/source/descriptors.js +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -const fs = require("fs"); -const path = require("path"); -const zlib = require("zlib"); -const uuidValidate = require("uuid-validate"); -const uuidv4 = require("uuid/v4"); - -const CompressionType = Object.freeze({ - ZIP : ".zip", - GZIP : ".gz", - None : "" -}); - -function getSourceId(sourceId){ - if(sourceId){ - if(!uuidValidate(sourceId, 4)){ - throw Error("sourceId is not a valid uuid/v4"); - } - return sourceId; - } - return uuidv4(); -} - -class FileDescriptor { - constructor(filePath, sourceId = null, size = null) { - this.filePath = filePath; - this.name = path.basename(this.filePath); - this.extension = path.extname(this.filePath).toLowerCase(); - this.size = size; - this.zipped = this.extension === ".gz" || this.extension === ".zip"; - this.sourceId = getSourceId(sourceId); - } - - async _gzip() { - let zipper = zlib.createGzip(); - let input = fs.createReadStream(this.filePath, { autoClose: true }); - let output = fs.createWriteStream(this.filePath + ".gz"); - - await new Promise((resolve, reject) => { - input.pipe(zipper).pipe(output) - .on("error", (err) => { - reject(err); - }); - output.once("close", function() { - resolve(); - }); - }); - - return this.filePath + ".gz"; - } - - async prepare() { - if(this.zipped){ - if (this.size == null || this.size <= 0) { - this.size = fs.statSync(this.filePath).size * 11; - } - return this.filePath; - } - else{ - await this._gzip(); - if (this.size == null || this.size <= 0) { - this.size = fs.statSync(this.filePath).size; - } - return this.filePath + ".gz"; - } - } -} - -class StreamDescriptor { - constructor(stream, sourceId = null, compressionType = CompressionType.None) { - this.stream = stream; - this.name = "stream"; - this.size = null; - this.compressionType = compressionType; - this.sourceId = getSourceId(sourceId); - } -} - -class BlobDescriptor { - constructor(path, size = null, sourceId = null) { - this.path = path; - this.size = size; - this.sourceId = getSourceId(sourceId); - } -} - -module.exports.FileDescriptor = FileDescriptor; -module.exports.BlobDescriptor = BlobDescriptor; -module.exports.StreamDescriptor = StreamDescriptor; -module.exports.CompressionType = CompressionType; diff --git a/azure-kusto-ingest/source/descriptors.ts b/azure-kusto-ingest/source/descriptors.ts new file mode 100644 index 0000000..3c99ea1 --- /dev/null +++ b/azure-kusto-ingest/source/descriptors.ts @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import uuid from "uuid"; +import uuidValidate from "uuid-validate"; +import zlib from "zlib"; +import pathlib from "path"; +import fs, {ReadStream} from "fs"; + +export enum CompressionType { + ZIP= ".zip", + GZIP= ".gz", + None= "", +} + +function getSourceId(sourceId: string | null): string { + if (sourceId) { + if (!uuidValidate(sourceId, 4)) { + throw Error("sourceId is not a valid uuid/v4"); + } + return sourceId; + } + return uuid.v4(); +} + +export class FileDescriptor { + readonly name: string; + readonly extension: string; + size: number | null; + sourceId: string; + zipped: boolean; + + constructor(readonly filePath: string, sourceId: string | null = null, size: number | null = null) { + this.name = pathlib.basename(this.filePath); + this.extension = pathlib.extname(this.filePath).toLowerCase(); + this.size = size; + this.zipped = this.extension === ".gz" || this.extension === ".zip"; + this.sourceId = getSourceId(sourceId); + } + + async _gzip(): Promise { + const zipper = zlib.createGzip(); + const input = fs.createReadStream(this.filePath, {autoClose: true}); + const output = fs.createWriteStream(this.filePath + ".gz"); + + await new Promise((resolve, reject) => { + input.pipe(zipper).pipe(output) + .on("error", (err) => { + reject(err); + }); + output.once("close", function () { + resolve(null); + }); + }); + + return this.filePath + ".gz"; + } + + async prepare(): Promise { + if (this.zipped) { + if (this.size == null || this.size <= 0) { + this.size = fs.statSync(this.filePath).size * 11; + } + return this.filePath; + } + + await this._gzip(); + if (this.size == null || this.size <= 0) { + this.size = fs.statSync(this.filePath).size; + } + return this.filePath + ".gz"; + + } +} + +export class StreamDescriptor { + name: string; + size: number | null; + compressionType: CompressionType; + sourceId: string; + constructor(readonly stream: ReadStream, sourceId: string | null = null, compressionType: CompressionType = CompressionType.None) { + this.name = "stream"; + this.size = null; + this.compressionType = compressionType; + this.sourceId = getSourceId(sourceId); + } +} + +export class BlobDescriptor { + size: number | null; + sourceId: string; + constructor(readonly path: string, size: number | null = null, sourceId: string | null = null) { + this.size = size; + this.sourceId = getSourceId(sourceId); + } +} diff --git a/azure-kusto-ingest/source/ingestClient.js b/azure-kusto-ingest/source/ingestClient.ts similarity index 55% rename from azure-kusto-ingest/source/ingestClient.js rename to azure-kusto-ingest/source/ingestClient.ts index 2c5e0c0..3d0dc8e 100644 --- a/azure-kusto-ingest/source/ingestClient.js +++ b/azure-kusto-ingest/source/ingestClient.ts @@ -1,54 +1,54 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -const KustoClient = require("azure-kusto-data").Client; -const { FileDescriptor, BlobDescriptor, StreamDescriptor } = require("./descriptors"); -const { ResourceManager } = require("./resourceManager"); -const IngestionBlobInfo = require("./ingestionBlobInfo"); -const { QueueClient } = require("@azure/storage-queue"); -const { ContainerClient } = require("@azure/storage-blob"); +// @ts-ignore +import {Client as KustoClient, KustoConnectionStringBuilder} from "azure-kusto-data"; -module.exports = class KustoIngestClient { - constructor(kcsb, defaultProps) { +import {BlobDescriptor, CompressionType, FileDescriptor, StreamDescriptor} from "./descriptors"; + +import ResourceManager from "./resourceManager"; + +import IngestionBlobInfo from "./ingestionBlobInfo"; + +import {QueueClient, QueueSendMessageResponse} from "@azure/storage-queue"; + +import {ContainerClient} from "@azure/storage-blob"; +import IngestionProperties from "./ingestionProperties"; +import {ReadStream} from "fs"; +import {AbstractKustoClient} from "./abstractKustoClient"; + + +export class KustoIngestClient extends AbstractKustoClient{ + resourceManager: ResourceManager; + + constructor(kcsb: string | KustoConnectionStringBuilder, public defaultProps: IngestionProperties | null = null) { + super(defaultProps); this.resourceManager = new ResourceManager(new KustoClient(kcsb)); - this.defaultProps = defaultProps; } - _mergeProps(newProperties) { - // no default props - if (newProperties == null || Object.keys(newProperties).length == 0) { - return this.defaultProps; - } - - // no new props - if (this.defaultProps == null || Object.keys(this.defaultProps) == 0) { - return newProperties; - } - // both exist - merge - return this.defaultProps.merge(newProperties); - } - - _getBlobNameSuffix(format, compressionType) { + _getBlobNameSuffix(format : string | null, compressionType: CompressionType) { const formatSuffix = format ? `.${format}` : ""; return `${formatSuffix}${compressionType}`; } - async _getBlockBlobClient(blobName){ + async _getBlockBlobClient(blobName: string) { const containers = await this.resourceManager.getContainers(); + if (containers == null) { + throw new Error("Failed to get containers"); + } const container = containers[Math.floor(Math.random() * containers.length)]; const containerClient = new ContainerClient(container.getSASConnectionString(), container.objectName); - const blockBlobClient = containerClient.getBlockBlobClient(blobName); - return blockBlobClient; + return containerClient.getBlockBlobClient(blobName); } - async ingestFromStream(stream, ingestionProperties) { + async ingestFromStream(stream: ReadStream | StreamDescriptor, ingestionProperties: IngestionProperties): Promise { const props = this._mergeProps(ingestionProperties); props.validate(); const descriptor = stream instanceof StreamDescriptor ? stream : new StreamDescriptor(stream); const blobName = `${props.database}__${props.table}__${descriptor.sourceId}` + - `${this._getBlobNameSuffix(props.format, descriptor.compressionType)}`; + `${this._getBlobNameSuffix(props.format ?? "", descriptor.compressionType)}`; const blockBlobClient = await this._getBlockBlobClient(blobName); await blockBlobClient.uploadStream(descriptor.stream); @@ -56,7 +56,7 @@ module.exports = class KustoIngestClient { return this.ingestFromBlob(new BlobDescriptor(blockBlobClient.url), props); // descriptor.size? } - async ingestFromFile(file, ingestionProperties) { + async ingestFromFile(file: string | FileDescriptor, ingestionProperties: IngestionProperties | null = null): Promise { const props = this._mergeProps(ingestionProperties); props.validate(); @@ -71,13 +71,18 @@ module.exports = class KustoIngestClient { return this.ingestFromBlob(new BlobDescriptor(blockBlobClient.url, descriptor.size, descriptor.sourceId), props); } - async ingestFromBlob(blob, ingestionProperties) { + async ingestFromBlob(blob: string | BlobDescriptor, ingestionProperties: IngestionProperties | null = null) : Promise { const props = this._mergeProps(ingestionProperties); props.validate(); const descriptor = blob instanceof BlobDescriptor ? blob : new BlobDescriptor(blob); - let queues = await this.resourceManager.getIngestionQueues(); - let authorizationContext = await this.resourceManager.getAuthorizationContext(); + const queues = await this.resourceManager.getIngestionQueues(); + if (queues == null) + { + throw new Error("Failed to get queues"); + } + + const authorizationContext = await this.resourceManager.getAuthorizationContext(); const queueDetails = queues[Math.floor(Math.random() * queues.length)]; @@ -89,4 +94,6 @@ module.exports = class KustoIngestClient { return queueClient.sendMessage(encoded); } -}; +} + +export default KustoIngestClient; diff --git a/azure-kusto-ingest/source/ingestionBlobInfo.js b/azure-kusto-ingest/source/ingestionBlobInfo.ts similarity index 53% rename from azure-kusto-ingest/source/ingestionBlobInfo.js rename to azure-kusto-ingest/source/ingestionBlobInfo.ts index 3f5f463..0b3d85f 100644 --- a/azure-kusto-ingest/source/ingestionBlobInfo.js +++ b/azure-kusto-ingest/source/ingestionBlobInfo.ts @@ -1,27 +1,42 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -const uuidv4 = require("uuid/v4"); -const moment = require("moment"); +import uuid from "uuid"; +import moment from "moment"; +import {BlobDescriptor} from "./descriptors"; +import IngestionProperties, {ReportLevel, ReportMethod} from "./ingestionProperties"; -module.exports = class IngestionBlobInfo { - constructor(blobDescriptor, ingestionProperties, authContext) { +export class IngestionBlobInfo { + BlobPath: string; + RawDataSize: number | null; + DatabaseName: string | null; + TableName: string | null; + RetainBlobOnSuccess: boolean; + FlushImmediately: boolean; + IgnoreSizeLimit: boolean; + ReportLevel: ReportLevel | null; + ReportMethod: ReportMethod | null; + SourceMessageCreationTime: moment.Moment; + Id: string; + AdditionalProperties: { [additional: string]: any; }; + + constructor(blobDescriptor: BlobDescriptor, ingestionProperties: IngestionProperties, authContext: string | null = null) { this.BlobPath = blobDescriptor.path; this.RawDataSize = blobDescriptor.size; - this.DatabaseName = ingestionProperties.database; - this.TableName = ingestionProperties.table; + this.DatabaseName = ingestionProperties.database ?? null; + this.TableName = ingestionProperties.table ?? null; this.RetainBlobOnSuccess = true; this.FlushImmediately = !!ingestionProperties.flushImmediately; this.IgnoreSizeLimit = false; - this.ReportLevel = ingestionProperties.reportLevel; - this.ReportMethod = ingestionProperties.reportMethod; + this.ReportLevel = ingestionProperties.reportLevel ?? null; + this.ReportMethod = ingestionProperties.reportMethod ?? null; this.SourceMessageCreationTime = moment.utc(); - this.Id = blobDescriptor.sourceId || uuidv4(); + this.Id = blobDescriptor.sourceId || uuid.v4(); - let additionalProperties = ingestionProperties.additionalProperties || {}; + const additionalProperties = ingestionProperties.additionalProperties || {}; additionalProperties.authorizationContext = authContext; - let tags = []; + const tags: string[] = []; if (ingestionProperties.additionalTags) { tags.concat(ingestionProperties.additionalTags); } @@ -42,11 +57,11 @@ module.exports = class IngestionBlobInfo { if (ingestionProperties.ingestionMapping && ingestionProperties.ingestionMapping.length > 0) { // server expects a string - additionalProperties["ingestionMapping"] = JSON.stringify(ingestionProperties.ingestionMapping); + additionalProperties.ingestionMapping = JSON.stringify(ingestionProperties.ingestionMapping); } if (ingestionProperties.ingestionMappingReference) { - additionalProperties["ingestionMappingReference"] = ingestionProperties.ingestionMappingReference; + additionalProperties.ingestionMappingReference = ingestionProperties.ingestionMappingReference; } if (ingestionProperties.validationPolicy) { @@ -59,4 +74,6 @@ module.exports = class IngestionBlobInfo { this.AdditionalProperties = additionalProperties; } -}; +} + +export default IngestionBlobInfo; \ No newline at end of file diff --git a/azure-kusto-ingest/source/ingestionProperties.js b/azure-kusto-ingest/source/ingestionProperties.js deleted file mode 100644 index 0b84bc1..0000000 --- a/azure-kusto-ingest/source/ingestionProperties.js +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - - -const DataFormat = Object.freeze({ - CSV: "csv", - TSV: "tsv", - SCSV: "scsv", - SOHSV: "sohsv", - PSV: "psv", - TXT: "txt", - JSON: "json", - SINGLEJSON: "singlejson", - AVRO: "avro", - PARQUET: "parquet", - TSVE: "tsve", - ORC: "orc" -}); - -module.exports.DataFormat = DataFormat; - -const IngestionMappingType = Object.freeze({ - CSV: "Csv", - PARQUET: "Parquet", - AVRO: "Avro", - JSON: "Json", - ORC: "orc" -}); - -module.exports.IngestionMappingType = IngestionMappingType; - -const ValidationOptions = Object.freeze({ - DoNotValidate: 0, - ValidateCsvInputConstantColumns: 1, - ValidateCsvInputColumnLevelOnly: 2 -}); - -module.exports.ValidationOptions = ValidationOptions; - -let ValidationImplications = Object.freeze({ - Fail: 0, - BestEffort: 1 -}); - -module.exports.ValidationImplications = ValidationImplications; - -module.exports.ValidationPolicy = class ValidationPolicy { - constructor(validationOptions = ValidationOptions.DoNotValidate, validationImplications = ValidationImplications.BestEffort) { - this.ValidationOptions = validationOptions; - this.ValidationImplications = validationImplications; - } -}; - -const ReportLevel = Object.freeze({ - FailuresOnly: 0, - DoNotReport: 1, - FailuresAndSuccesses: 2 -}); - -module.exports.ReportLevel = ReportLevel; - -const ReportMethod = Object.freeze({ - Queue: 0 -}); - -module.exports.ReportMethod = ReportMethod; - -class ColumnMapping { } - -module.exports.CsvColumnMapping = class CsvColumnMapping extends ColumnMapping { - constructor(columnName, cslDataType, ordinal) { - super(); - this.Name = columnName; - this.DataType = cslDataType; - this.Ordinal = ordinal; - } -}; - -module.exports.JsonColumnMapping = class JsonColumnMapping extends ColumnMapping { - constructor(columnName, jsonPath, cslDataType = null) { - super(); - this.column = columnName; - this.path = jsonPath; - this.datatype = cslDataType; - } -}; - -module.exports.IngestionProperties = class IngestionProperties { - constructor({ - database = null, - table = null, - format = null, - ingestionMapping = null, - ingestionMappingReference = null, - ingestionMappingType = null, - additionalTags = null, - ingestIfNotExists = null, - ingestByTags = null, - dropByTags = null, - flushImmediately = null, - reportLevel = null, - reportMethod = null, - validationPolicy = null, - additionalProperties = null - }) { - if (ingestionMapping && ingestionMappingReference) throw new Error("Both mapping and a mapping reference detected"); - - this.database = database; - this.table = table; - this.format = format; - this.ingestionMapping = ingestionMapping; - this.ingestionMappingType = ingestionMappingType; - this.ingestionMappingReference = ingestionMappingReference; - this.additionalTags = additionalTags; - this.ingestIfNotExists = ingestIfNotExists; - this.ingestByTags = ingestByTags; - this.dropByTags = dropByTags; - this.flushImmediately = flushImmediately; - this.reportLevel = reportLevel; - this.reportMethod = reportMethod; - this.validationPolicy = validationPolicy; - this.additionalProperties = additionalProperties; - } - - validate() { - - if (!this.flushImmediately) this.flushImmediately = false; - if (!this.reportLevel) this.reportLevel = ReportLevel.DoNotReport; - if (!this.reportMethod) this.reportMethod = ReportMethod.Queue; - - if (!this.database) throw new Error("Must define a target database"); - if (!this.table) throw new Error("Must define a target table"); - if (!this.format) throw new Error("Must define a data format"); - if (this.ingestionMapping && this.ingestionMappingReference) - throw new Error("Both mapping and a mapping reference detected"); - if (!this.ingestionMapping && !this.ingestionMappingReference && this.format === DataFormat.JSON) - throw new Error("Json must have a mapping defined"); - } - - merge(extraProps) { - const merged = new IngestionProperties(this); - - for (let key of Object.keys(extraProps)) { - if (extraProps[key] != null) { - merged[key] = extraProps[key]; - } - } - - return merged; - } -}; diff --git a/azure-kusto-ingest/source/ingestionProperties.ts b/azure-kusto-ingest/source/ingestionProperties.ts new file mode 100644 index 0000000..50a67d3 --- /dev/null +++ b/azure-kusto-ingest/source/ingestionProperties.ts @@ -0,0 +1,156 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +export enum DataFormat { + CSV = "csv", + TSV = "tsv", + SCSV = "scsv", + SOHSV = "sohsv", + PSV = "psv", + TXT = "txt", + JSON = "json", + SINGLEJSON = "singlejson", + AVRO = "avro", + PARQUET = "parquet", + TSVE = "tsve", + ORC = "orc" +} + +export enum IngestionMappingType { + CSV = "Csv", + PARQUET = "Parquet", + AVRO = "Avro", + JSON = "Json", + ORC = "orc" +} + +export enum ValidationOptions { + DoNotValidate = 0, + ValidateCsvInputConstantColumns = 1, + ValidateCsvInputColumnLevelOnly = 2 +} + + +export enum ValidationImplications { + Fail = 0, + BestEffort = 1 +} + + +export class ValidationPolicy { + constructor(readonly validationOptions: ValidationOptions = ValidationOptions.DoNotValidate, readonly validationImplications: ValidationImplications = ValidationImplications.BestEffort) { + } +} + +export enum ReportLevel { + FailuresOnly = 0, + DoNotReport = 1, + FailuresAndSuccesses = 2 +} + +export enum ReportMethod { + Queue = 0 +} + +class ColumnMapping { +} + +export class CsvColumnMapping extends ColumnMapping { + constructor(readonly columnName: string, readonly cslDataType: string, readonly ordinal: string) { + super(); + } +} + +export class JsonColumnMapping extends ColumnMapping { + constructor(readonly columnName: string, readonly jsonPath: string, readonly cslDataType: string | null = null) { + super(); + } +} + +class IngestionPropertiesFields { + database?: string | null = null; + table?: string | null = null; + format?: string | null = null; + ingestionMapping?: ColumnMapping[] | null = null; + ingestionMappingReference?: string | null = null; + ingestionMappingType?: string | null = null; + additionalTags?: string | null = null; + ingestIfNotExists?: string | null = null; + ingestByTags?: string[] | null = null; + dropByTags?: string[] | null = null; + flushImmediately?: boolean | null = null; + reportLevel?: ReportLevel | null = null; + reportMethod?: ReportMethod | null = null; + validationPolicy?: string | null = null; + additionalProperties?: {[additional:string] : any} | null = null; +} + +export class IngestionProperties extends IngestionPropertiesFields { + constructor({ + database = null, + table = null, + format = null, + ingestionMapping = null, + ingestionMappingReference = null, + ingestionMappingType = null, + additionalTags = null, + ingestIfNotExists = null, + ingestByTags = null, + dropByTags = null, + flushImmediately = null, + reportLevel = null, + reportMethod = null, + validationPolicy = null, + additionalProperties = null + }: IngestionPropertiesFields) { + super(); + if (ingestionMapping && ingestionMappingReference) throw new Error("Both mapping and a mapping reference detected"); + + this.database = database; + this.table = table; + this.format = format; + this.ingestionMapping = ingestionMapping; + this.ingestionMappingType = ingestionMappingType; + this.ingestionMappingReference = ingestionMappingReference; + this.additionalTags = additionalTags; + this.ingestIfNotExists = ingestIfNotExists; + this.ingestByTags = ingestByTags; + this.dropByTags = dropByTags; + this.flushImmediately = flushImmediately; + this.reportLevel = reportLevel; + this.reportMethod = reportMethod; + this.validationPolicy = validationPolicy; + this.additionalProperties = additionalProperties; + } + + validate() { + + if (!this.flushImmediately) this.flushImmediately = false; + if (!this.reportLevel) this.reportLevel = ReportLevel.DoNotReport; + if (!this.reportMethod) this.reportMethod = ReportMethod.Queue; + + if (!this.database) throw new Error("Must define a target database"); + if (!this.table) throw new Error("Must define a target table"); + if (!this.format) throw new Error("Must define a data format"); + if (this.ingestionMapping && this.ingestionMappingReference) + throw new Error("Both mapping and a mapping reference detected"); + if (!this.ingestionMapping && !this.ingestionMappingReference && this.format === DataFormat.JSON) + throw new Error("Json must have a mapping defined"); + } + + [extraProps: string] : any; + + merge(extraProps: any) { + const merged = new IngestionProperties(this); + + for (const key of Object.keys(extraProps)) { + if (extraProps[key] != null) { + merged[key] = extraProps[key]; + } + } + + return merged; + } +} + +export default IngestionProperties; diff --git a/azure-kusto-ingest/source/resourceManager.js b/azure-kusto-ingest/source/resourceManager.js deleted file mode 100644 index b3a36d6..0000000 --- a/azure-kusto-ingest/source/resourceManager.js +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -const moment = require("moment"); - -const URI_FORMAT = /https:\/\/(\w+).(queue|blob|table).core.windows.net\/([\w,-]+)\?(.*)/; - - -class ResourceURI { - constructor(storageAccountName, objectType, objectName, sas) { - this.storageAccountName = storageAccountName; - this.objectType = objectType; - this.objectName = objectName; - this.sas = sas; - } - - static fromURI(uri) { - const match = URI_FORMAT.exec(uri); - return new ResourceURI(match[1], match[2], match[3], match[4]); - } - - getSASConnectionString() { - if(this.objectType == "queue"){ - return `QueueEndpoint=https://${this.storageAccountName}.queue.core.windows.net/;SharedAccessSignature=${this.sas}`; - } - if(this.objectType == "blob"){ - return `BlobEndpoint=https://${this.storageAccountName}.blob.core.windows.net/;SharedAccessSignature=${this.sas}`; - } - } -} - -module.exports.ResourceURI = ResourceURI; - -class IngestClientResources { - constructor( - securedReadyForAggregationQueues = null, - failedIngestionsQueues = null, - successfulIngestionsQueues = null, - containers = null - ) { - this.securedReadyForAggregationQueues = securedReadyForAggregationQueues; - this.failedIngestionsQueues = failedIngestionsQueues; - this.successfulIngestionsQueues = successfulIngestionsQueues; - this.containers = containers; - } - - valid() { - let resources = [ - this.securedReadyForAggregationQueues, - this.failedIngestionsQueues, - this.failedIngestionsQueues, - this.containers - ]; - return resources.reduce((prev, current) => prev && current, true); - } -} - -module.exports.IngestClientResources = IngestClientResources; - -module.exports.ResourceManager = class ResourceManager { - constructor(kustoClient) { - this.kustoClient = kustoClient; - this.refreshPeriod = moment.duration(1, "h"); - - this.ingestClientResources = null; - this.ingestClientResourcesLastUpdate = null; - - this.authorizationContext = null; - this.authorizationContextLastUpdate = null; - - } - - async refreshIngestClientResources() { - let now = moment.now(); - if (!this.ingestClientResources || - (this.ingestClientResourcesLastUpdate + this.refreshPeriod) <= now || !this.ingestClientResources.valid()) { - this.ingestClientResources = await this.getIngestClientResourcesFromService(); - this.ingestClientResourcesLastUpdate = now; - } - } - - async getIngestClientResourcesFromService() { - let response = await this.kustoClient.execute("NetDefaultDB", ".get ingestion resources"); - const table = response.primaryResults[0]; - - const resources = new IngestClientResources( - this.getResourceByName(table, "SecuredReadyForAggregationQueue"), - this.getResourceByName(table, "FailedIngestionsQueue"), - this.getResourceByName(table, "SuccessfulIngestionsQueue"), - this.getResourceByName(table, "TempStorage") - ); - return resources; - } - - getResourceByName(table, resourceName) { - let result = []; - for (let row of table.rows()) { - if (row.ResourceTypeName == resourceName) { - result.push(ResourceURI.fromURI(row.StorageRoot)); - } - } - return result; - } - - async refreshAuthorizationContext() { - let now = moment.utc(); - if (!this.authorizationContext || this.authorizationContext.trim() || - (this.authorizationContextLastUpdate + this.refreshPeriod) <= now) { - this.authorizationContext = await this.getAuthorizationContextFromService(); - this.authorizationContextLastUpdate = now; - } - } - - async getAuthorizationContextFromService() { - let response = await this.kustoClient.execute("NetDefaultDB", ".get kusto identity token"); - const authContext = response.primaryResults[0].rows().next().value.AuthorizationContext; - return authContext; - } - - async getIngestionQueues() { - await this.refreshIngestClientResources(); - return this.ingestClientResources.securedReadyForAggregationQueues; - } - - async getFailedIngestionsQueues() { - await this.refreshIngestClientResources(); - return this.ingestClientResources.failedIngestionsQueues; - } - - async getSuccessfulIngestionsQueues() { - await this.refreshIngestClientResources(); - return this.ingestClientResources.successfulIngestionsQueues; - } - - async getContainers() { - await this.refreshIngestClientResources(); - return this.ingestClientResources.containers; - } - - async getAuthorizationContext() { - await this.refreshAuthorizationContext(); - return this.authorizationContext; - } -}; diff --git a/azure-kusto-ingest/source/resourceManager.ts b/azure-kusto-ingest/source/resourceManager.ts new file mode 100644 index 0000000..ceed4d2 --- /dev/null +++ b/azure-kusto-ingest/source/resourceManager.ts @@ -0,0 +1,147 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import {Client} from "azure-kusto-data"; +import moment from "moment"; + +const URI_FORMAT = /https:\/\/(\w+).(queue|blob|table).core.windows.net\/([\w,-]+)\?(.*)/; + +export class ResourceURI { + constructor(readonly storageAccountName: string, readonly objectType: string, readonly objectName: string, readonly sas: string) { + } + + static fromURI(uri: string) { + const match = URI_FORMAT.exec(uri); + if (match == null || match.length < 5) { + throw Error(`Failed to create ResourceManager from URI - invalid uri (${uri})`); + } + return new ResourceURI(match[1], match[2], match[3], match[4]); + } + + getSASConnectionString(): string { + if (this.objectType == "queue") { + return `QueueEndpoint=https://${this.storageAccountName}.queue.core.windows.net/;SharedAccessSignature=${this.sas}`; + } + if (this.objectType == "blob") { + return `BlobEndpoint=https://${this.storageAccountName}.blob.core.windows.net/;SharedAccessSignature=${this.sas}`; + } + + throw new Error(`Can't make the current object type (${this.objectType}) to connection string`) + } +} + +export class IngestClientResources { + constructor( + readonly securedReadyForAggregationQueues: ResourceURI[] | null = null, + readonly failedIngestionsQueues: ResourceURI[] | null = null, + readonly successfulIngestionsQueues: ResourceURI[] | null = null, + readonly containers: ResourceURI[] | null = null + ) { + } + + valid() { + const resources = [ + this.securedReadyForAggregationQueues, + this.failedIngestionsQueues, + this.failedIngestionsQueues, + this.containers + ]; + return resources.reduce((prev, current) => !!(prev && current), true); + } +} + +export class ResourceManager { + public readonly refreshPeriod: moment.Duration; + public ingestClientResources: IngestClientResources | null; + public ingestClientResourcesLastUpdate: moment.Moment | null; + public authorizationContext: string | null; + public authorizationContextLastUpdate: moment.Moment | null; + + constructor(readonly kustoClient: Client) { + this.refreshPeriod = moment.duration(1, "h"); + + this.ingestClientResources = null; + this.ingestClientResourcesLastUpdate = null; + + this.authorizationContext = null; + this.authorizationContextLastUpdate = null; + } + + async refreshIngestClientResources(): Promise { + const now = moment(); + if (!this.ingestClientResources || + !this.ingestClientResourcesLastUpdate || + (this.ingestClientResourcesLastUpdate.add(this.refreshPeriod) <= now) || + !this.ingestClientResources.valid()) { + this.ingestClientResources = await this.getIngestClientResourcesFromService(); + this.ingestClientResourcesLastUpdate = now; + } + + return this.ingestClientResources; + } + + async getIngestClientResourcesFromService(): Promise { + const response = await this.kustoClient.execute("NetDefaultDB", ".get ingestion resources"); + const table = response.primaryResults[0]; + + return new IngestClientResources( + this.getResourceByName(table, "SecuredReadyForAggregationQueue"), + this.getResourceByName(table, "FailedIngestionsQueue"), + this.getResourceByName(table, "SuccessfulIngestionsQueue"), + this.getResourceByName(table, "TempStorage") + ); + } + + getResourceByName(table: { rows: () => any; }, resourceName: string): ResourceURI[] { + const result = []; + for (const row of table.rows()) { + if (row.ResourceTypeName == resourceName) { + result.push(ResourceURI.fromURI(row.StorageRoot)); + } + } + return result; + } + + async refreshAuthorizationContext(): Promise { + const now = moment.utc(); + if (!this.authorizationContext?.trim() || + !this.authorizationContextLastUpdate || + (this.authorizationContextLastUpdate.add(this.refreshPeriod)) <= now) { + this.authorizationContext = await this.getAuthorizationContextFromService(); + this.authorizationContextLastUpdate = now; + + if (this.authorizationContext == null) { + throw new Error("Authorization context can't be null"); + } + } + + return this.authorizationContext; + } + + async getAuthorizationContextFromService() { + const response = await this.kustoClient.execute("NetDefaultDB", ".get kusto identity token"); + return (response.primaryResults[0].rows().next().value as any).AuthorizationContext; + } + + async getIngestionQueues() { + return (await this.refreshIngestClientResources()).securedReadyForAggregationQueues; + } + + async getFailedIngestionsQueues() { + return (await this.refreshIngestClientResources()).failedIngestionsQueues; + } + + async getSuccessfulIngestionsQueues() { + return (await this.refreshIngestClientResources()).successfulIngestionsQueues; + } + + async getContainers() { + return (await this.refreshIngestClientResources()).containers; + } + + async getAuthorizationContext(): Promise { + return this.refreshAuthorizationContext(); + } +} + +export default ResourceManager; diff --git a/azure-kusto-ingest/source/status.js b/azure-kusto-ingest/source/status.ts similarity index 51% rename from azure-kusto-ingest/source/status.js rename to azure-kusto-ingest/source/status.ts index 27d776c..e9699cb 100644 --- a/azure-kusto-ingest/source/status.js +++ b/azure-kusto-ingest/source/status.ts @@ -1,10 +1,21 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -const StatusQueue = require("./statusQ"); -class StatusMessage { - constructor(raw, obj, extraProps) { - let props = [ +import {StatusQueue} from "./statusQ"; +import KustoIngestClient from "./ingestClient"; +import {ResourceURI} from "./resourceManager"; + +export class StatusMessage { + OperationId?: string; + Database?: string; + Table?: string; + IngestionSourceId?: string; + IngestionSourcePath?: string; + RootActivityId?: string; + + [other: string] : any; + constructor(raw: any, obj: any, extraProps: string[] | null) { + let props : string[] = [ "OperationId", "Database", "Table", "IngestionSourceId", "IngestionSourcePath", "RootActivityId" ]; @@ -15,7 +26,7 @@ class StatusMessage { const _obj = obj || JSON.parse(raw || JSON.stringify(raw)); - for (let prop of props) { + for (const prop of props) { this[prop] = _obj[prop]; } } @@ -23,7 +34,9 @@ class StatusMessage { class SuccessMessage extends StatusMessage { - constructor(raw, obj) { + SucceededOn?: string; + + constructor(raw: any, obj: any) { super(raw, obj, [ "SucceededOn" ]); @@ -32,7 +45,13 @@ class SuccessMessage extends StatusMessage { class FailureMessage extends StatusMessage { - constructor(raw, obj) { + FailedOn? : string; + Details? : string; + ErrorCode? : string; + FailureStatus? : string; + OriginatesFromUpdatePolicy? : string; + ShouldRetry? : string; + constructor(raw: any, obj: any) { super(raw, obj, [ "FailedOn", "Details", @@ -45,15 +64,19 @@ class FailureMessage extends StatusMessage { } -module.exports = class KustoIngestStatusQueues { - constructor(kustoIngestClient) { +export class KustoIngestStatusQueues { + success: StatusQueue; + failure: StatusQueue; + constructor(kustoIngestClient: KustoIngestClient) { this.success = new StatusQueue( - () => kustoIngestClient.resourceManager.getSuccessfulIngestionsQueues(), + () => kustoIngestClient.resourceManager.getSuccessfulIngestionsQueues().then(r => r as ResourceURI[]), SuccessMessage ); this.failure = new StatusQueue( - () => kustoIngestClient.resourceManager.getFailedIngestionsQueues(), + () => kustoIngestClient.resourceManager.getFailedIngestionsQueues().then(r => r as ResourceURI[]), FailureMessage ); } -}; +} + +export default KustoIngestStatusQueues; \ No newline at end of file diff --git a/azure-kusto-ingest/source/statusQ.js b/azure-kusto-ingest/source/statusQ.js deleted file mode 100644 index 347e63b..0000000 --- a/azure-kusto-ingest/source/statusQ.js +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -const { QueueClient } = require("@azure/storage-queue"); - -class QueueDetails { - constructor(name, service) { - this.name = name; - this.service = service; - } -} - - -function shuffle(a) { - for (let i = a.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - let temp = a[j]; - a[j] = a[i]; - a[i] = temp; - } - - return a; -} - -module.exports = class StatusQueue { - constructor(getQueuesFunc, messageCls) { - this.getQueuesFunc = getQueuesFunc; - this.messageCls = messageCls; - } - - _getQServices(queuesDetails) { - return queuesDetails.map(q => new QueueDetails(q.objectName, - new QueueClient(q.getSASConnectionString(), q.objectName))); - } - - async isEmpty() { - const result = await this.peek(1, { raw: true }); - return !result || result.length === 0; - } - - decodeContent(content) { - return Buffer.from(content, "base64").toString("ascii"); - } - - deserializeMessage(m) { - return new this.messageCls(this.decodeContent(m.messageText)); - } - - async _peek(qs, n, options) { - const result = []; - const nonEmptyQs = []; - - for (let i = 0; i < qs.length; i++) { - let q = qs[i]; - let response = await q.service.peekMessages(); - let messages = response.peekedMessageItems; - - if (messages && messages.length > 0) { - nonEmptyQs.push(q); - } - - for (let m of messages) { - if (m && Object.keys(m).length > 0) { - result.push(options && options.raw ? m : this.deserializeMessage(m)); - - if (result.length == n) { - return { done: true, nonEmptyQs, result }; - } - } - } - } - return { done: nonEmptyQs.length === 0, nonEmptyQs, result }; - } - - async peek(n = 1, options = null) { - const queues = await this.getQueuesFunc(); - const qServices = shuffle(this._getQServices(queues)); - const perQ = qServices.length > 1 ? Math.floor(n / qServices.length) : qServices.length; - - // First, iterate evenly and randomly on status queues - const partial = await this._peek(qServices, perQ, options); - - if (partial.done) { - return partial.result; - } - let messagesLeftToPeek = n - partial.result.length; - - // In case queues are uneven, iterate again. This time, request for all n messages and trim - return await this._peek(partial.result.nonEmptyQs, messagesLeftToPeek, options); - } - - async _pop(qs, n, options) { - const nonEmptyQs = []; - const result = []; - - for (let i = 0; i < qs.length; i++) { - let q = qs[i]; - const response = await q.service.receiveMessages({ numOfMessages: n }); - let messages = response.receivedMessageItems; - for (let m of messages) { - if (m && Object.keys(m).length > 0) { - result.push(options && options.raw ? m : this.deserializeMessage(m)); - - if (!(options && options.remove === false)) { - q.service.deleteMessage(m.messageId, m.popReceipt); - } - if (result.length == n) { - return { done: true, nonEmptyQs, result }; - } - } - } - } - return { done: nonEmptyQs.length === 0, nonEmptyQs, result }; - } - - - async pop(n = 1, options = null) { - const queues = await this.getQueuesFunc(); - const qServices = shuffle(this._getQServices(queues)); - const perQ = qServices.length > 1 ? Math.floor(n / qServices.length) : qServices.length; - - // First, iterate evenly and randomly on status queues - const partial = await this._pop(qServices, perQ, options); - if (partial.done) { - return partial.result; - } - - let messagesLeftToPop = n - partial.result.length; - - // In case queues are uneven, iterate again. This time, request for all n messages and trim - const final = await this._pop(partial.result.nonEmptyQs, messagesLeftToPop, options); - return partial.result.concat(final.result); - } -}; diff --git a/azure-kusto-ingest/source/statusQ.ts b/azure-kusto-ingest/source/statusQ.ts new file mode 100644 index 0000000..cb54394 --- /dev/null +++ b/azure-kusto-ingest/source/statusQ.ts @@ -0,0 +1,149 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import {PeekedMessageItem, QueueClient} from "@azure/storage-queue"; +import {ResourceURI} from "./resourceManager" +import {StatusMessage} from "./status"; + +class QueueDetails { + constructor(readonly name: string, readonly service: QueueClient) { + } +} + + +function shuffle(a: T[]): T[] { + for (let i = a.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + const temp = a[j]; + a[j] = a[i]; + a[i] = temp; + } + + return a; +} + +interface PeekParams { + raw: boolean; +} + +interface PopParams { + raw: boolean; + remove: boolean; +} + +type Message = PeekedMessageItem | StatusMessage; + +export class StatusQueue { + constructor(readonly getQueuesFunc: () => Promise, readonly messageCls: typeof StatusMessage) { + } + + _getQServices(queuesDetails: ResourceURI[]) { + return queuesDetails.map(function (q) { + const sasConnectionString = q.getSASConnectionString(); + if (!sasConnectionString) { + throw new Error("Empty or null connection string"); + } + return new QueueDetails(q.objectName, + new QueueClient(sasConnectionString, q.objectName)); + }); + } + + async isEmpty() { + const result = await this.peek(1, {raw: true}); + return !result || result.length === 0; + } + + decodeContent(content: string) { + return Buffer.from(content, "base64").toString("ascii"); + } + + deserializeMessage(m: PeekedMessageItem): StatusMessage { + return new this.messageCls(this.decodeContent(m.messageText), null, null); + } + + async _peek(qs: QueueDetails[], n: number, options: PeekParams | null): Promise<{ result: Message[]; nonEmptyQs: QueueDetails[]; done: boolean }> { + const result: Message[] = []; + const nonEmptyQs: QueueDetails[] = []; + + for (const q of qs) { + const response = await q.service.peekMessages(); + const messages = response.peekedMessageItems; + + if (messages && messages.length > 0) { + nonEmptyQs.push(q); + } + + for (const m of messages) { + if (m && Object.keys(m).length > 0) { + result.push(options && options.raw ? m : this.deserializeMessage(m)); + + if (result.length == n) { + return {done: true, nonEmptyQs, result}; + } + } + } + } + return {done: nonEmptyQs.length === 0, nonEmptyQs, result}; + } + + async peek(n = 1, options: PeekParams | null = null): Promise { + const queues = await this.getQueuesFunc(); + const qServices: QueueDetails[] = shuffle(this._getQServices(queues)); + const perQ = qServices.length > 1 ? Math.floor(n / qServices.length) : qServices.length; + + // First, iterate evenly and randomly on status queues + const partial = await this._peek(qServices, perQ, options); + + if (partial.done) { + return partial.result; + } + const messagesLeftToPeek = n - partial.result.length; + + // In case queues are uneven, iterate again. This time, request for all n messages and trim + return (await this._peek(partial.nonEmptyQs, messagesLeftToPeek, options)).result; + } + + async _pop(qs: QueueDetails[], n: number, options: PopParams | null): + Promise<{ result: Message[] & {nonEmptyQs?: QueueDetails[]}; nonEmptyQs: any[]; done: boolean }> { + const nonEmptyQs: any[] = []; + const result = []; + + for (const q of qs) { + const response = await q.service.receiveMessages({numOfMessages: n}); + const messages = response.receivedMessageItems; + for (const m of messages) { + if (m && Object.keys(m).length > 0) { + result.push(options && options.raw ? m : this.deserializeMessage(m)); + + if (!(options && !options.remove)) { + q.service.deleteMessage(m.messageId, m.popReceipt); + } + if (result.length == n) { + return {done: true, nonEmptyQs, result}; + } + } + } + } + + return {done: nonEmptyQs.length === 0, nonEmptyQs, result}; + } + + + async pop(n = 1, options: PopParams | null = null): Promise { + const queues = await this.getQueuesFunc(); + const qServices = shuffle(this._getQServices(queues)); + const perQ = qServices.length > 1 ? Math.floor(n / qServices.length) : qServices.length; + + // First, iterate evenly and randomly on status queues + const partial = await this._pop(qServices, perQ, options); + if (partial.done) { + return partial.result; + } + + const messagesLeftToPop = n - partial.result.length; + + // In case queues are uneven, iterate again. This time, request for all n messages and trim + const final = await this._pop(partial.result.nonEmptyQs ?? [], messagesLeftToPop, options); + return partial.result.concat(final.result); + } +} diff --git a/azure-kusto-ingest/source/streamingIngestClient.js b/azure-kusto-ingest/source/streamingIngestClient.js deleted file mode 100644 index 39eb740..0000000 --- a/azure-kusto-ingest/source/streamingIngestClient.js +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -const KustoClient = require("azure-kusto-data").Client; -const { FileDescriptor, StreamDescriptor, CompressionType } = require("./descriptors"); -const DataFormat = require("./resourceManager"); -const zlib = require("zlib"); -const fs = require("fs"); - -module.exports = class KustoStreamingIngestClient { - constructor(kcsb, defaultProps) { - this.kustoClient = new KustoClient(kcsb); - this.defaultProps = defaultProps; - this._mapping_required_formats = Object.freeze([ DataFormat.JSON, DataFormat.SINGLEJSON, DataFormat.AVRO, DataFormat.ORC ]); - } - - _mergeProps(newProperties) { - // no default props - if (newProperties == null || Object.keys(newProperties).length == 0) { - return this.defaultProps; - } - - // no new props - if (this.defaultProps == null || Object.keys(this.defaultProps) == 0) { - return newProperties; - } - // both exist - merge - return this.defaultProps.merge(newProperties); - } - - async ingestFromStream(stream, ingestionProperties) { - const props = this._mergeProps(ingestionProperties); - props.validate(); - - const descriptor = stream instanceof StreamDescriptor ? stream : new StreamDescriptor(stream); - const compressedStream = - descriptor.compressionType == CompressionType.None ? descriptor.stream.pipe(zlib.createGzip()) : descriptor.stream; - - if (props.ingestionMappingReference == null && this._mapping_required_formats.includes(props.format)) { - throw new Error(`Mapping reference required for format ${props.foramt}.`); - } - - return this.kustoClient.executeStreamingIngest( - props.database, - props.table, - compressedStream, - props.format, - props.ingestionMappingReference); - } - - async ingestFromFile(file, ingestionProperties) { - const props = this._mergeProps(ingestionProperties); - props.validate(); - - const fileDescriptor = file instanceof FileDescriptor ? file : new FileDescriptor(file); - - const stream = fs.createReadStream(fileDescriptor.filePath); - - const compressionType = fileDescriptor.zipped ? CompressionType.GZIP : CompressionType.None; - const streamDescriptor = new StreamDescriptor(stream, fileDescriptor.sourceId, compressionType); - - return this.ingestFromStream(streamDescriptor, ingestionProperties); - } -}; diff --git a/azure-kusto-ingest/source/streamingIngestClient.ts b/azure-kusto-ingest/source/streamingIngestClient.ts new file mode 100644 index 0000000..e4322b9 --- /dev/null +++ b/azure-kusto-ingest/source/streamingIngestClient.ts @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import IngestionProperties, {DataFormat} from "./ingestionProperties"; + +import {CompressionType, FileDescriptor, StreamDescriptor} from "./descriptors"; +import zlib from "zlib"; +import fs from "fs"; +import {AbstractKustoClient} from "./abstractKustoClient"; +import {Client as KustoClient, KustoConnectionStringBuilder} from "azure-kusto-data"; +import {KustoResponseDataSet} from "azure-kusto-data/source/response"; + +class KustoStreamingIngestClient extends AbstractKustoClient { + private kustoClient: KustoClient; + // tslint:disable-next-line:variable-name + private _mapping_required_formats: readonly any[]; + + constructor(kcsb: string | KustoConnectionStringBuilder, defaultProps: IngestionProperties | null = null) { + super(defaultProps); + this.kustoClient = new KustoClient(kcsb); + this._mapping_required_formats = Object.freeze([DataFormat.JSON, DataFormat.SINGLEJSON, DataFormat.AVRO, DataFormat.ORC]); + } + async ingestFromStream(stream: StreamDescriptor | fs.ReadStream, ingestionProperties: IngestionProperties): Promise { + const props = this._mergeProps(ingestionProperties); + props.validate(); + const descriptor = stream instanceof StreamDescriptor ? stream : new StreamDescriptor(stream); + const compressedStream = + descriptor.compressionType == CompressionType.None ? descriptor.stream.pipe(zlib.createGzip()) : descriptor.stream; + if (props.ingestionMappingReference == null && this._mapping_required_formats.includes(props.format)) { + throw new Error(`Mapping reference required for format ${props.foramt}.`); + } + return this.kustoClient.executeStreamingIngest( + props.database as string, + props.table as string, + compressedStream, + props.format, + props.ingestionMappingReference ?? null); + } + async ingestFromFile(file: FileDescriptor | string, ingestionProperties: IngestionProperties): Promise { + const props = this._mergeProps(ingestionProperties); + props.validate(); + const fileDescriptor = file instanceof FileDescriptor ? file : new FileDescriptor(file); + const stream = fs.createReadStream(fileDescriptor.filePath); + const compressionType = fileDescriptor.zipped ? CompressionType.GZIP : CompressionType.None; + const streamDescriptor = new StreamDescriptor(stream, fileDescriptor.sourceId, compressionType); + return this.ingestFromStream(streamDescriptor, ingestionProperties); + } +} + +export default KustoStreamingIngestClient; \ No newline at end of file diff --git a/azure-kusto-ingest/test/descriptorsTest.js b/azure-kusto-ingest/test/descriptorsTest.js deleted file mode 100644 index 35cf199..0000000 --- a/azure-kusto-ingest/test/descriptorsTest.js +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -const assert = require("assert"); -const { FileDescriptor } = require("../source/descriptors"); - - -describe("FileDescriptor", function () { - describe("#constructor()", function () { - it("valid input zipped", function () { - let desc = new FileDescriptor("./data/events.json.gz"); - - assert.equal(desc.name, "events.json.gz"); - assert.equal(desc.extension, ".gz"); - assert.equal(desc.zipped, true); - - }); - - it("valid input json", function () { - let desc = new FileDescriptor("./data/events.json"); - - assert.equal(desc.name, "events.json"); - assert.equal(desc.extension, ".json"); - assert.equal(desc.zipped, false); - - }); - }); -}); - diff --git a/azure-kusto-ingest/test/descriptorsTest.ts b/azure-kusto-ingest/test/descriptorsTest.ts new file mode 100644 index 0000000..f8ba0ec --- /dev/null +++ b/azure-kusto-ingest/test/descriptorsTest.ts @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import assert from "assert"; +import {FileDescriptor} from "../source/descriptors"; + +describe("FileDescriptor", function () { + describe("#constructor()", function () { + it("valid input zipped", function () { + const desc = new FileDescriptor("./data/events.json.gz"); + + assert.strictEqual(desc.name, "events.json.gz"); + assert.strictEqual(desc.extension, ".gz"); + assert.strictEqual(desc.zipped, true); + + }); + + it("valid input json", function () { + const desc = new FileDescriptor("./data/events.json"); + + assert.strictEqual(desc.name, "events.json"); + assert.strictEqual(desc.extension, ".json"); + assert.strictEqual(desc.zipped, false); + + }); + }); +}); + diff --git a/azure-kusto-ingest/test/e2eTests/e2eTest.js b/azure-kusto-ingest/test/e2eTests/e2eTest.js deleted file mode 100644 index ecaaa74..0000000 --- a/azure-kusto-ingest/test/e2eTests/e2eTest.js +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -const assert = require("assert"); -const fs = require('fs'); -const path = require('path') - -const IngestClient = require("../../source/ingestClient"); -const KustoIngestStatusQueues = require("../../source/status"); -const ConnectionStringBuilder = require("../../node_modules/azure-kusto-data").KustoConnectionStringBuilder; -const Client = require("../.././node_modules/azure-kusto-data").Client; -const StreamingIngestClient = require("../../source/streamingIngestClient"); -const { FileDescriptor, StreamDescriptor, CompressionType } = require("../../source/descriptors"); -const { IngestionProperties, DataFormat, ReportLevel } = require("../../source/ingestionProperties"); - -const databaseName = process.env.TEST_DATABASE; -const appId = process.env.APP_ID; -const appKey = process.env.APP_KEY; -const tenantId = process.env.TENANT_ID; - -if (!databaseName || !appId || !appKey || !tenantId) { - process.stdout.write("Skip E2E test - Missing env variables"); - return; -} - -const engineKcsb = ConnectionStringBuilder.withAadApplicationKeyAuthentication(process.env.ENGINE_CONNECTION_STRING, appId, appKey, tenantId); -const queryClient = new Client(engineKcsb); -const streamingIngestClient = new StreamingIngestClient(engineKcsb); -const dmKcsb = ConnectionStringBuilder.withAadApplicationKeyAuthentication(process.env.DM_CONNECTION_STRING, appId, appKey, tenantId); -const ingestClient = new IngestClient(dmKcsb); -const statusQueues = new KustoIngestStatusQueues(ingestClient); - -class testDataItem { - constructor(description, path, rows, ingestionProperties, testOnstreamingIngestion = true) { - this.description = description; - this.path = path; - this.rows = rows; - this.ingestionProperties = ingestionProperties; - this.testOnstreamingIngestion = testOnstreamingIngestion; - } -} - -const tableName = "NodeTest" + Date.now(); -const mappingName = "mappingRef"; -const tableColumns = "(rownumber:int, rowguid:string, xdouble:real, xfloat:real, xbool:bool, xint16:int, xint32:int, xint64:long, xuint8:long, xuint16:long, xuint32:long, xuint64:long, xdate:datetime, xsmalltext:string, xtext:string, xnumberAsText:string, xtime:timespan, xtextWithNulls:string, xdynamicWithNulls:dynamic)"; - -const mapping = fs.readFileSync(getTestResourcePath("dataset_mapping.json"), { encoding: 'utf8' }); -const columnmapping = JSON.parse(mapping); - -const ingestionPropertiesWithoutMapping = new IngestionProperties({ database: databaseName, table: tableName, format: DataFormat.CSV, flushImmediately: true }); -const ingestionPropertiesWithMappingReference = new IngestionProperties({ database: databaseName, table: tableName, format: DataFormat.JSON, ingestionMappingReference: mappingName, flushImmediately: true }); -const ingestionPropertiesWithColumnMapping = new IngestionProperties({ database: databaseName, table: tableName, format: DataFormat.JSON, ingestionMapping: columnmapping, flushImmediately: true }); - -const testItems = [ - new testDataItem("csv", getTestResourcePath("dataset.csv"), 10, ingestionPropertiesWithoutMapping), - new testDataItem("csv.gz", getTestResourcePath("dataset_gzip.csv.gz"), 10, ingestionPropertiesWithoutMapping), - new testDataItem("json with mapping ref", getTestResourcePath("dataset.json"), 2, ingestionPropertiesWithMappingReference), - new testDataItem("json.gz with mapping ref", getTestResourcePath("dataset_gzip.json.gz"), 2, ingestionPropertiesWithMappingReference), - new testDataItem("json with mapping", getTestResourcePath("dataset.json"), 2, ingestionPropertiesWithColumnMapping, false), - new testDataItem("json.gz with mapping", getTestResourcePath("dataset_gzip.json.gz"), 2, ingestionPropertiesWithColumnMapping, false) -]; - -var currentCount = 0; - -describe(`E2E Tests - ${tableName}`, function () { - after(async function () { - try { - await queryClient.execute(databaseName, `.drop table ${tableName} ifexists`); - } - catch (err) { - assert.fail("Failed to drop table"); - } - }); - - describe('SetUp', function () { - it('Create table', async function () { - try { - await queryClient.execute(databaseName, `.create table ${tableName} ${tableColumns}`); - } - catch (err) { - assert.fail("Failed to create table"); - } - }); - - it('Create table ingestion mapping', async function () { - try { - await queryClient.execute(databaseName, `.create-or-alter table ${tableName} ingestion json mapping '${mappingName}' '${mapping}'`); - } - catch (err) { - assert.fail("Failed to create table ingestion mapping" + err); - } - }); - }); - - describe('ingestClient', function () { - it('ingestFromFile', async function () { - for (let item of testItems) { - try { - await ingestClient.ingestFromFile(item.path, item.ingestionProperties); - } - catch (err) { - console.error(err); - assert.fail(`Failed to ingest ${item.description}`); - } - await assertRowsCount(item); - } - }).timeout(240000); - - it('ingestFromStream', async function () { - for (let item of testItems) { - let stream = fs.createReadStream(item.path); - if (item.path.endsWith('gz')) { - stream = new StreamDescriptor(stream, null, CompressionType.GZIP); - } - try { - await ingestClient.ingestFromStream(stream, item.ingestionProperties); - } - catch (err) { - assert.fail(`Failed to ingest ${item.description}`); - } - await assertRowsCount(item); - } - }).timeout(240000); - }); - - describe('StreamingIngestClient', function () { - it('ingestFromFile', async function () { - for (let item of testItems.filter(item => item.testOnstreamingIngestion)) { - try { - await streamingIngestClient.ingestFromFile(item.path, item.ingestionProperties); - } - catch (err) { - console.error(err); - assert.fail(`Failed to ingest ${item.description}`); - } - await assertRowsCount(item); - } - }).timeout(240000); - - it('ingestFromStream', async function () { - for (let item of testItems.filter(item => item.testOnstreamingIngestion)) { - let stream = fs.createReadStream(item.path); - if (item.path.endsWith('gz')) { - stream = new StreamDescriptor(stream, null, CompressionType.GZIP); - } - try { - await streamingIngestClient.ingestFromStream(stream, item.ingestionProperties); - } - catch (err) { - assert.fail(`Failed to ingest ${item.description}`); - } - await assertRowsCount(item); - } - }).timeout(240000); - }); - - describe('KustoIngestStatusQueues', function () { - it('CleanStatusQueues', async function () { - try { - await cleanStatusQueues(); - } - catch (err) { - console.error(err); - assert.fail(`Failed to Clean status queues`); - } - }).timeout(240000); - - it('CheckSucceededIngestion', async function () { - item = testItems[0]; - item.ingestionProperties.reportLevel = ReportLevel.FailuresAndSuccesses; - try { - await ingestClient.ingestFromFile(item.path, item.ingestionProperties); - const status = await waitForStatus(); - assert.equal(status.SuccessCount, 1); - assert.equal(status.FailureCount, 0); - } - catch (err) { - console.error(err); - assert.fail(`Failed to ingest ${item.description}`); - } - }).timeout(240000); - - it('CheckFailedIngestion', async function () { - item = testItems[0]; - item.ingestionProperties.reportLevel = ReportLevel.FailuresAndSuccesses; - item.ingestionProperties.database = "invalid"; - try { - await ingestClient.ingestFromFile(item.path, item.ingestionProperties); - const status = await waitForStatus(); - assert.equal(status.SuccessCount, 0); - assert.equal(status.FailureCount, 1); - } - catch (err) { - console.error(err); - assert.fail(`Failed to ingest ${item.description}`); - } - }).timeout(240000); - }); - - describe('QueryClient', function () { - it('General BadRequest', async function () { - try { - response = await queryClient.executeQuery(databaseName, "invalidSyntax "); - } - catch (ex) { - return; - } - assert.fail(`General BadRequest ${item.description}`); - }); - - it('PartialQueryFailure', async function () { - try { - response = await queryClient.executeQuery(databaseName, "invalidSyntax "); - - } - catch (ex) { - return; - } - assert.fail(`Didn't throw PartialQueryFailure ${item.description}`); - }); - }); -}); - -async function cleanStatusQueues() { - while (!await statusQueues.failure.isEmpty()) { - await statusQueues.failure.pop(); - } - - while (!await statusQueues.success.isEmpty()) { - await statusQueues.success.pop(); - } -} - -async function waitForStatus() { - while (await statusQueues.failure.isEmpty() && await statusQueues.success.isEmpty()) { - await sleep(1000); - } - - const failures = await statusQueues.failure.pop(); - const successes = await statusQueues.success.pop(); - - return { "SuccessCount": successes.length, "FailureCount": failures.length } -} - -function sleep(ms) { - return new Promise((resolve) => { setTimeout(resolve, ms); }); -} - -function getTestResourcePath(name) { - return __dirname + `/e2eData/${name}`; -} - -async function assertRowsCount(testItem) { - var count = 0; - var expected = testItem.rows; - // Timeout = 3 min - for (var i = 0; i < 18; i++) { - await sleep(10000); - let results; - try { - results = await queryClient.execute(databaseName, `${tableName} | count `); - } - catch (ex) { - continue; - } - - count = results.primaryResults[0][0].Count - currentCount; - if (count >= expected) { - break; - } - } - currentCount += count; - assert.equal(count, expected, `Failed to ingest ${testItem.description}`); -} diff --git a/azure-kusto-ingest/test/e2eTests/e2eTest.ts b/azure-kusto-ingest/test/e2eTests/e2eTest.ts new file mode 100644 index 0000000..f286679 --- /dev/null +++ b/azure-kusto-ingest/test/e2eTests/e2eTest.ts @@ -0,0 +1,281 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import assert from "assert"; +import fs, {ReadStream} from 'fs'; +import IngestClient from "../../source/ingestClient"; +import KustoIngestStatusQueues from "../../source/status"; +import { + Client, + KustoConnectionStringBuilder as ConnectionStringBuilder + // @ts-ignore +} from "../.././node_modules/azure-kusto-data"; +import StreamingIngestClient from "../../source/streamingIngestClient"; +import {CompressionType, StreamDescriptor} from "../../source/descriptors"; +import {DataFormat, IngestionProperties, ReportLevel} from "../../source/ingestionProperties"; + +const databaseName = process.env.TEST_DATABASE; +const appId = process.env.APP_ID; +const appKey = process.env.APP_KEY; +const tenantId = process.env.TENANT_ID; + +function main(): void { + + if (!databaseName || !appId || !appKey || !tenantId) { + process.stdout.write("Skip E2E test - Missing env variables"); + return; + } + + const engineKcsb = ConnectionStringBuilder.withAadApplicationKeyAuthentication(process.env.ENGINE_CONNECTION_STRING ?? "", appId, appKey, tenantId); + const queryClient = new Client(engineKcsb); + const streamingIngestClient = new StreamingIngestClient(engineKcsb); + const dmKcsb = ConnectionStringBuilder.withAadApplicationKeyAuthentication(process.env.DM_CONNECTION_STRING ?? "", appId, appKey, tenantId); + const ingestClient = new IngestClient(dmKcsb); + const statusQueues = new KustoIngestStatusQueues(ingestClient); + + class testDataItem { + constructor(public description: string, public path: string, public rows: number, public ingestionProperties: IngestionProperties, public testOnstreamingIngestion = true) { + } + } + + const tableName = "NodeTest" + Date.now(); + const mappingName = "mappingRef"; + const tableColumns = "(rownumber:int, rowguid:string, xdouble:real, xfloat:real, xbool:bool, xint16:int, xint32:int, xint64:long, xuint8:long, xuint16:long, xuint32:long, xuint64:long, xdate:datetime, xsmalltext:string, xtext:string, xnumberAsText:string, xtime:timespan, xtextWithNulls:string, xdynamicWithNulls:dynamic)"; + + const mapping = fs.readFileSync(getTestResourcePath("dataset_mapping.json"), {encoding: 'utf8'}); + const columnmapping = JSON.parse(mapping); + + const ingestionPropertiesWithoutMapping = new IngestionProperties({ + database: databaseName, + table: tableName, + format: DataFormat.CSV, + flushImmediately: true + }); + const ingestionPropertiesWithMappingReference = new IngestionProperties({ + database: databaseName, + table: tableName, + format: DataFormat.JSON, + ingestionMappingReference: mappingName, + flushImmediately: true + }); + const ingestionPropertiesWithColumnMapping = new IngestionProperties({ + database: databaseName, + table: tableName, + format: DataFormat.JSON, + ingestionMapping: columnmapping, + flushImmediately: true + }); + + const testItems = [ + new testDataItem("csv", getTestResourcePath("dataset.csv"), 10, ingestionPropertiesWithoutMapping), + new testDataItem("csv.gz", getTestResourcePath("dataset_gzip.csv.gz"), 10, ingestionPropertiesWithoutMapping), + new testDataItem("json with mapping ref", getTestResourcePath("dataset.json"), 2, ingestionPropertiesWithMappingReference), + new testDataItem("json.gz with mapping ref", getTestResourcePath("dataset_gzip.json.gz"), 2, ingestionPropertiesWithMappingReference), + new testDataItem("json with mapping", getTestResourcePath("dataset.json"), 2, ingestionPropertiesWithColumnMapping, false), + new testDataItem("json.gz with mapping", getTestResourcePath("dataset_gzip.json.gz"), 2, ingestionPropertiesWithColumnMapping, false) + ]; + + let currentCount = 0; + + describe(`E2E Tests - ${tableName}`, function () { + after(async function () { + try { + await queryClient.execute(databaseName, `.drop table ${tableName} ifexists`); + } catch (err) { + assert.fail("Failed to drop table"); + } + }); + + describe('SetUp', function () { + it('Create table', async function () { + try { + await queryClient.execute(databaseName, `.create table ${tableName} ${tableColumns}`); + } catch (err) { + assert.fail("Failed to create table"); + } + }); + + it('Create table ingestion mapping', async function () { + try { + await queryClient.execute(databaseName, `.create-or-alter table ${tableName} ingestion json mapping '${mappingName}' '${mapping}'`); + } catch (err) { + assert.fail("Failed to create table ingestion mapping" + err); + } + }); + }); + + describe('ingestClient', function () { + it('ingestFromFile', async function () { + for (const item of testItems) { + try { + await ingestClient.ingestFromFile(item.path, item.ingestionProperties); + } catch (err) { + console.error(err); + assert.fail(`Failed to ingest ${item.description}`); + } + await assertRowsCount(item); + } + }).timeout(240000); + + it('ingestFromStream', async function () { + for (const item of testItems) { + let stream: ReadStream | StreamDescriptor = fs.createReadStream(item.path); + if (item.path.endsWith('gz')) { + stream = new StreamDescriptor(stream, null, CompressionType.GZIP); + } + try { + await ingestClient.ingestFromStream(stream, item.ingestionProperties); + } catch (err) { + assert.fail(`Failed to ingest ${item.description}`); + } + await assertRowsCount(item); + } + }).timeout(240000); + }); + + describe('StreamingIngestClient', function () { + it('ingestFromFile', async function () { + for (const item of testItems.filter(item => item.testOnstreamingIngestion)) { + try { + await streamingIngestClient.ingestFromFile(item.path, item.ingestionProperties); + } catch (err) { + console.error(err); + assert.fail(`Failed to ingest ${item.description}`); + } + await assertRowsCount(item); + } + }).timeout(240000); + + it('ingestFromStream', async function () { + for (const item of testItems.filter(item => item.testOnstreamingIngestion)) { + let stream: ReadStream | StreamDescriptor = fs.createReadStream(item.path); + if (item.path.endsWith('gz')) { + stream = new StreamDescriptor(stream, null, CompressionType.GZIP); + } + try { + await streamingIngestClient.ingestFromStream(stream, item.ingestionProperties); + } catch (err) { + assert.fail(`Failed to ingest ${item.description}`); + } + await assertRowsCount(item); + } + }).timeout(240000); + }); + + describe('KustoIngestStatusQueues', function () { + it('CleanStatusQueues', async function () { + try { + await cleanStatusQueues(); + } catch (err) { + console.error(err); + assert.fail(`Failed to Clean status queues`); + } + }).timeout(240000); + + it('CheckSucceededIngestion', async function () { + const item = testItems[0]; + item.ingestionProperties.reportLevel = ReportLevel.FailuresAndSuccesses; + try { + await ingestClient.ingestFromFile(item.path, item.ingestionProperties); + const status = await waitForStatus(); + assert.equal(status.SuccessCount, 1); + assert.equal(status.FailureCount, 0); + } catch (err) { + console.error(err); + assert.fail(`Failed to ingest ${item.description}`); + } + }).timeout(240000); + + it('CheckFailedIngestion', async function () { + const item = testItems[0]; + item.ingestionProperties.reportLevel = ReportLevel.FailuresAndSuccesses; + item.ingestionProperties.database = "invalid"; + try { + await ingestClient.ingestFromFile(item.path, item.ingestionProperties); + const status = await waitForStatus(); + assert.equal(status.SuccessCount, 0); + assert.equal(status.FailureCount, 1); + } catch (err) { + console.error(err); + assert.fail(`Failed to ingest ${item.description}`); + } + }).timeout(240000); + }); + + describe('QueryClient', function () { + it('General BadRequest', async function () { + try { + const response = await queryClient.executeQuery(databaseName, "invalidSyntax "); + } catch (ex) { + return; + } + assert.fail(`General BadRequest`); + }); + + it('PartialQueryFailure', async function () { + try { + const response = await queryClient.executeQuery(databaseName, "invalidSyntax "); + + } catch (ex) { + return; + } + assert.fail(`Didn't throw PartialQueryFailure`); + }); + }); + }); + + async function cleanStatusQueues() { + while (!await statusQueues.failure.isEmpty()) { + await statusQueues.failure.pop(); + } + + while (!await statusQueues.success.isEmpty()) { + await statusQueues.success.pop(); + } + } + + async function waitForStatus() { + while (await statusQueues.failure.isEmpty() && await statusQueues.success.isEmpty()) { + await sleep(1000); + } + + const failures = await statusQueues.failure.pop(); + const successes = await statusQueues.success.pop(); + + return {"SuccessCount": successes.length, "FailureCount": failures.length} + } + + function sleep(ms: number) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); + } + + function getTestResourcePath(name: string) { + return __dirname + `/e2eData/${name}`; + } + + async function assertRowsCount(testItem: testDataItem) { + let count = 0; + const expected = testItem.rows; + // Timeout = 3 min + for (let i = 0; i < 18; i++) { + await sleep(10000); + let results; + try { + results = await queryClient.execute(databaseName ?? "", `${tableName} | count `); + } catch (ex) { + continue; + } + + count = results.primaryResults[0][0].Count - currentCount; + if (count >= expected) { + break; + } + } + currentCount += count; + assert.equal(count, expected, `Failed to ingest ${testItem.description}`); + } +} + +main(); \ No newline at end of file diff --git a/azure-kusto-ingest/test/ingestClientTest.js b/azure-kusto-ingest/test/ingestClientTest.js deleted file mode 100644 index d7f72c2..0000000 --- a/azure-kusto-ingest/test/ingestClientTest.js +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -const assert = require("assert"); - -const KustoIngestClient = require("../source/ingestClient"); -const { IngestionProperties , DataFormat } = require("../source/ingestionProperties"); - -describe("KustoIngestClient", function () { - describe("#constructor()", function () { - it("valid input", function () { - let ingestClient = new KustoIngestClient("https://cluster.kusto.windows.net", { - database: "db", - table: "table", - format: "csv" - }); - - assert.equal(ingestClient.resourceManager.kustoClient.cluster, "https://cluster.kusto.windows.net"); - assert.equal(ingestClient.defaultProps.database, "db"); - assert.equal(ingestClient.defaultProps.table, "table"); - assert.equal(ingestClient.defaultProps.format, "csv"); - }); - }); - - describe("#_resolveProperties()", function () { - it("empty default props", function () { - let newProps = new IngestionProperties({database: "db", table: "table", format: DataFormat.CSV}); - // TODO: not sure a unit test will be useful here - let client = new KustoIngestClient('https://cluster.region.kusto.windows.net'); - let actual = client._mergeProps(newProps); - - assert.equal(actual.database, "db"); - assert.equal(actual.table, "table"); - assert.equal(actual.format, "csv"); - }); - - it("empty new props", function () { - // TODO: not sure a unit test will be useful here - let defaultProps = new IngestionProperties({database: "db", table: "table", format: DataFormat.CSV}); - // TODO: not sure a unit test will be useful here - let client = new KustoIngestClient('https://cluster.region.kusto.windows.net', defaultProps); - let actual = client._mergeProps(null); - - assert.equal(actual.database, "db"); - assert.equal(actual.table, "table"); - assert.equal(actual.format, "csv"); - }); - - it("both exist props", function () { - let defaultProps = new IngestionProperties({database: "db", table: "table", format: DataFormat.CSV}); - let newProps = new IngestionProperties({}); - newProps.database = "db2"; - newProps.ingestionMappingReference = "MappingRef"; - - let client = new KustoIngestClient('https://cluster.region.kusto.windows.net', defaultProps); - let actual = client._mergeProps(newProps); - - assert.equal(actual.database, "db2"); - assert.equal(actual.table, "table"); - assert.equal(actual.format, "csv"); - assert.equal(actual.ingestionMappingReference, "MappingRef"); - }); - - it("empty both", function () { - let client = new KustoIngestClient('https://cluster.region.kusto.windows.net'); - - let actual = client._mergeProps(); - assert.equal(actual, undefined); - }); - }); - - describe("#ingestFromFile()", function () { - it("valid input", function () { - // TODO: not sure a unit test will be useful here - }); - }); - - describe("#ingestFromStream()", function () { - it("valid input", function () { - // TODO: not sure a unit test will be useful here - }); - }); - - describe("#ingestFromBlob()", function () { - it("valid input", function () { - // TODO: not sure a unit test will be useful here - }); - }); -}); diff --git a/azure-kusto-ingest/test/ingestClientTest.ts b/azure-kusto-ingest/test/ingestClientTest.ts new file mode 100644 index 0000000..98e1a08 --- /dev/null +++ b/azure-kusto-ingest/test/ingestClientTest.ts @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import assert from "assert"; +import {KustoIngestClient} from "../source/ingestClient"; +import {DataFormat, IngestionProperties} from "../source/ingestionProperties"; + + +describe("KustoIngestClient", function () { + describe("#constructor()", function () { + it("valid input", function () { + const ingestClient = new KustoIngestClient("https://cluster.kusto.windows.net", { + database: "db", + table: "table", + format: "csv" + } as IngestionProperties); + + assert.notStrictEqual(ingestClient.defaultProps, null); + assert.strictEqual(ingestClient.resourceManager.kustoClient.cluster, "https://cluster.kusto.windows.net"); + assert.strictEqual(ingestClient.defaultProps!.database, "db"); + assert.strictEqual(ingestClient.defaultProps!.table, "table"); + assert.strictEqual(ingestClient.defaultProps!.format, "csv"); + }); + }); + + describe("#_resolveProperties()", function () { + it("empty default props", function () { + const newProps = new IngestionProperties({ + database: "db", + table: "table", + format: DataFormat.CSV + }); + // TODO: not sure a unit test will be useful here + const client = new KustoIngestClient('https://cluster.region.kusto.windows.net'); + const actual = client._mergeProps(newProps); + + assert.strictEqual(actual.database, "db"); + assert.strictEqual(actual.table, "table"); + assert.strictEqual(actual.format, "csv"); + }); + + it("empty new props", function () { + // TODO: not sure a unit test will be useful here + const defaultProps = new IngestionProperties({ + database: "db", + table: "table", + format: DataFormat.CSV + }); + // TODO: not sure a unit test will be useful here + const client = new KustoIngestClient('https://cluster.region.kusto.windows.net', defaultProps); + const actual = client._mergeProps(null); + + assert.strictEqual(actual.database, "db"); + assert.strictEqual(actual.table, "table"); + assert.strictEqual(actual.format, "csv"); + }); + + it("both exist props", function () { + const defaultProps = new IngestionProperties({ + database: "db", + table: "table", + format: DataFormat.CSV + }); + const newProps = new IngestionProperties({}); + newProps.database = "db2"; + newProps.ingestionMappingReference = "MappingRef"; + + const client = new KustoIngestClient('https://cluster.region.kusto.windows.net', defaultProps); + const actual = client._mergeProps(newProps); + + assert.strictEqual(actual.database, "db2"); + assert.strictEqual(actual.table, "table"); + assert.strictEqual(actual.format, "csv"); + assert.strictEqual(actual.ingestionMappingReference, "MappingRef"); + }); + + it("empty both", function () { + const client = new KustoIngestClient('https://cluster.region.kusto.windows.net'); + + const actual = client._mergeProps(); + assert.deepStrictEqual(actual, new IngestionProperties({})); + }); + }); + + describe("#ingestFromFile()", function () { + it("valid input", function () { + // TODO: not sure a unit test will be useful here + }); + }); + + describe("#ingestFromStream()", function () { + it("valid input", function () { + // TODO: not sure a unit test will be useful here + }); + }); + + describe("#ingestFromBlob()", function () { + it("valid input", function () { + // TODO: not sure a unit test will be useful here + }); + }); +}); diff --git a/azure-kusto-ingest/test/ingestionPropsTest.js b/azure-kusto-ingest/test/ingestionPropsTest.js deleted file mode 100644 index 9e09750..0000000 --- a/azure-kusto-ingest/test/ingestionPropsTest.js +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -const assert = require("assert"); - -const IngestionProperties = require("../source/ingestionProperties").IngestionProperties; -const JsonColumnMapping = require("../source/ingestionProperties").JsonColumnMapping; -const IngestionBlobInfo = require("../source/ingestionBlobInfo"); -const { DataFormat } = require("../source/ingestionProperties"); - -describe("IngestionProperties", function () { - describe("#constructor()", function () { - it("valid input", function () { - let props = new IngestionProperties({database: "db", table: "table", format: DataFormat.CSV}); - - assert.equal(props.database, "db"); - assert.equal(props.table, "table"); - assert.equal(props.format, DataFormat.CSV); - }); - }); - - describe("#merge()", function () { - it("valid input", function () { - let props = new IngestionProperties({database: "db", table: "table", format: DataFormat.CSV}); - - let otherProps = new IngestionProperties({ingestionMappingReference: "CsvMappingRef"}); - - let merged = props.merge(otherProps); - - assert.equal(merged.database, "db"); - assert.equal(merged.table, "table"); - assert.equal(merged.format, DataFormat.CSV); - assert.equal(merged.ingestionMappingReference, "CsvMappingRef"); - }); - }); - - describe("#validate()", function () { - it("valid input", function () { - let props = new IngestionProperties({database: "db", table: "table", format: DataFormat.CSV, ingestionMappingReference: "CsvMappingRef"}); - - try { - props.validate(); - } catch (ex) { - assert.fail(ex); - } - }); - - it("invalid input", function () { - let props = new IngestionProperties({}); - - try { - props.validate(); - } catch (ex) { - assert.equal(ex.message, "Must define a target database"); - } - }); - - it("invalid input json", function () { - let props = new IngestionProperties({database: "db", table: "table", format: DataFormat.JSON}); - - try { - props.validate(); - } catch (ex) { - assert.equal(ex.message, "Json must have a mapping defined"); - } - }); - - it("json mapping as additional props on ingestion blob info", function () { - let columns = [new JsonColumnMapping('Id', '$.Id', 'int'), new JsonColumnMapping('Value', '$.value', 'dynamic')]; - let props = new IngestionProperties({database: "db", table: "table", format: DataFormat.CSV, ingestionMapping: columns}); - let ingestionBlobInfo = new IngestionBlobInfo('https://account.blob.core.windows.net/blobcontainer/blobfile.json', props); - - assert.deepEqual(JSON.parse(ingestionBlobInfo.AdditionalProperties.ingestionMapping), props.ingestionMapping); - }); - }); -}); diff --git a/azure-kusto-ingest/test/ingestionPropsTest.ts b/azure-kusto-ingest/test/ingestionPropsTest.ts new file mode 100644 index 0000000..a5027fb --- /dev/null +++ b/azure-kusto-ingest/test/ingestionPropsTest.ts @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import assert from "assert"; +import {IngestionProperties, JsonColumnMapping} from "../source/ingestionProperties"; + +import {IngestionBlobInfo} from "../source/ingestionBlobInfo"; +import {BlobDescriptor} from "../source/descriptors"; + +const { DataFormat } = require("../source/ingestionProperties"); + +describe("IngestionProperties", function () { + describe("#constructor()", function () { + it("valid input", function () { + const props = new IngestionProperties({database: "db", table: "table", format: DataFormat.CSV}); + + assert.strictEqual(props.database, "db"); + assert.strictEqual(props.table, "table"); + assert.strictEqual(props.format, DataFormat.CSV); + }); + }); + + describe("#merge()", function () { + it("valid input", function () { + const props = new IngestionProperties({database: "db", table: "table", format: DataFormat.CSV}); + + const otherProps = new IngestionProperties({ingestionMappingReference: "CsvMappingRef"}); + + const merged = props.merge(otherProps); + + assert.strictEqual(merged.database, "db"); + assert.strictEqual(merged.table, "table"); + assert.strictEqual(merged.format, DataFormat.CSV); + assert.strictEqual(merged.ingestionMappingReference, "CsvMappingRef"); + }); + }); + + describe("#validate()", function () { + it("valid input", function () { + const props = new IngestionProperties({database: "db", table: "table", format: DataFormat.CSV, ingestionMappingReference: "CsvMappingRef"}); + + try { + props.validate(); + } catch (ex) { + assert.fail(ex); + } + }); + + it("invalid input", function () { + const props = new IngestionProperties({}); + + try { + props.validate(); + } catch (ex) { + assert.strictEqual(ex.message, "Must define a target database"); + } + }); + + it("invalid input json", function () { + const props = new IngestionProperties({database: "db", table: "table", format: DataFormat.JSON}); + + try { + props.validate(); + } catch (ex) { + assert.strictEqual(ex.message, "Json must have a mapping defined"); + } + }); + + it("json mapping as additional props on ingestion blob info", function () { + const columns = [new JsonColumnMapping('Id', '$.Id', 'int'), new JsonColumnMapping('Value', '$.value', 'dynamic')]; + const props = new IngestionProperties({database: "db", table: "table", format: DataFormat.CSV, ingestionMapping: columns}); + const ingestionBlobInfo = new IngestionBlobInfo(new BlobDescriptor('https://account.blob.core.windows.net/blobcontainer/blobfile.json'), props); + const reParsed = JSON.parse(JSON.stringify(props.ingestionMapping)); // Stringify and pass to make the object identical to a json one + assert.deepStrictEqual(JSON.parse(ingestionBlobInfo.AdditionalProperties.ingestionMapping), reParsed); + }); + }); +}); diff --git a/azure-kusto-ingest/test/resourceManagerTest.js b/azure-kusto-ingest/test/resourceManagerTest.ts similarity index 53% rename from azure-kusto-ingest/test/resourceManagerTest.js rename to azure-kusto-ingest/test/resourceManagerTest.ts index 8b5f42c..238a172 100644 --- a/azure-kusto-ingest/test/resourceManagerTest.js +++ b/azure-kusto-ingest/test/resourceManagerTest.ts @@ -1,15 +1,16 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -const assert = require("assert"); -const moment = require("moment"); -const sinon = require("sinon"); +import assert from "assert"; -const KustoClient = require("azure-kusto-data").Client; +import moment from "moment"; -const ResourceManager = require("../source/resourceManager").ResourceManager; -const IngestClientResources = require("../source/resourceManager").IngestClientResources; -const ResourceURI = require("../source/resourceManager").ResourceURI; +import sinon from "sinon"; + +import {Client as KustoClient} from "azure-kusto-data"; + +import {IngestClientResources, ResourceManager, ResourceURI} from "../source/resourceManager"; +import {KustoResponseDataSet} from "azure-kusto-data/source/response"; describe("ResourceURI", function () { describe("#fromUri()", function () { @@ -19,13 +20,13 @@ describe("ResourceURI", function () { const objectName = "container"; const sas = "sas"; - let uri = `https://${accountName}.${objectType}.core.windows.net/${objectName}?${sas}`; + const uri = `https://${accountName}.${objectType}.core.windows.net/${objectName}?${sas}`; const storageUrl = ResourceURI.fromURI(uri); - assert.equal(storageUrl.storageAccountName, accountName); - assert.equal(storageUrl.objectType, objectType); - assert.equal(storageUrl.objectName, objectName); - assert.equal(storageUrl.sas, sas); + assert.strictEqual(storageUrl.storageAccountName, accountName); + assert.strictEqual(storageUrl.objectType, objectType); + assert.strictEqual(storageUrl.objectName, objectName); + assert.strictEqual(storageUrl.sas, sas); }); }); @@ -39,7 +40,7 @@ describe("ResourceURI", function () { const storageUrl = new ResourceURI(accountName, objectType, objectName, sas); - assert.equal(storageUrl.getSASConnectionString(), `BlobEndpoint=https://${accountName}.blob.core.windows.net/;SharedAccessSignature=${sas}`); + assert.strictEqual(storageUrl.getSASConnectionString(), `BlobEndpoint=https://${accountName}.blob.core.windows.net/;SharedAccessSignature=${sas}`); }); }); }); @@ -57,8 +58,8 @@ describe("ResourceManager", function () { const mockedResourcesResponse = { primaryResults: [{ - rows: function* () { - for (let row of rows) { + *rows () { + for (const row of rows) { yield row; } } @@ -67,38 +68,38 @@ describe("ResourceManager", function () { describe("#constructor()", function () { it("valid input", function () { - let resourceManager = new ResourceManager(new KustoClient("https://cluster.kusto.windows.net")); + const resourceManager = new ResourceManager(new KustoClient("https://cluster.kusto.windows.net")); - assert.equal(resourceManager.ingestClientResources, null); - assert.equal(resourceManager.authorizationContext, null); + assert.strictEqual(resourceManager.ingestClientResources, null); + assert.strictEqual(resourceManager.authorizationContext, null); }); }); describe("#getIngestClientResourcesFromService()", function () { it("valid input", async function () { const client = new KustoClient("https://cluster.kusto.windows.net") - sinon.stub(client, "execute").returns(mockedResourcesResponse); + sinon.stub(client, "execute").returns(Promise.resolve(mockedResourcesResponse as KustoResponseDataSet)); - let resourceManager = new ResourceManager(client); + const resourceManager = new ResourceManager(client); const resources = await resourceManager.getIngestClientResourcesFromService(); - assert.equal(resources.containers.length, 2); - assert.equal(resources.successfulIngestionsQueues.length, 1); - assert.equal(resources.failedIngestionsQueues.length, 1); - assert.equal(resources.securedReadyForAggregationQueues.length, 2); + assert.strictEqual(resources.containers!!.length, 2); + assert.strictEqual(resources.successfulIngestionsQueues!!.length, 1); + assert.strictEqual(resources.failedIngestionsQueues!!.length, 1); + assert.strictEqual(resources.securedReadyForAggregationQueues!!.length, 2); }); it("error response", async function () { const client = new KustoClient("https://cluster.kusto.windows.net"); - sinon.stub(client, "execute").throwsException("Kusto request erred (403)"); + sinon.stub(client, "execute").throwsException(new Error("Kusto request erred (403)")); const resourceManager = new ResourceManager(client); try{ await resourceManager.getIngestClientResourcesFromService(); } catch(ex){ - assert.equal(ex, "Kusto request erred (403)"); + assert(ex.message.startsWith( "Kusto request erred (403)")); return; } assert.fail(); @@ -107,32 +108,32 @@ describe("ResourceManager", function () { describe("#getResourceByName()", function () { it("valid input", function () { - let resourceManager = new ResourceManager(new KustoClient("https://cluster.kusto.windows.net")); + const resourceManager = new ResourceManager(new KustoClient("https://cluster.kusto.windows.net")); - let resources = resourceManager.getResourceByName(mockedResourcesResponse.primaryResults[0], "TempStorage"); - assert.equal(resources.length, 2); + const resources = resourceManager.getResourceByName(mockedResourcesResponse.primaryResults[0], "TempStorage"); + assert.strictEqual(resources.length, 2); }); }); describe("#refreshIngestClientResources()", function () { it("should refresh", async function () { - let resourceManager = new ResourceManager(new KustoClient("https://cluster.kusto.windows.net")); + const resourceManager = new ResourceManager(new KustoClient("https://cluster.kusto.windows.net")); - let call = sinon.stub(resourceManager, "getIngestClientResourcesFromService"); + const call = sinon.stub(resourceManager, "getIngestClientResourcesFromService"); await resourceManager.refreshIngestClientResources(); - assert.equal(call.calledOnce, true); + assert.strictEqual(call.calledOnce, true); }); it("shouldn't refresh", async function () { - let resourceManager = new ResourceManager(new KustoClient("https://cluster.kusto.windows.net")); + const resourceManager = new ResourceManager(new KustoClient("https://cluster.kusto.windows.net")); - let call = sinon.stub(resourceManager, "getIngestClientResourcesFromService"); - resourceManager.ingestClientResourcesLastUpdate = moment.now(); - resourceManager.ingestClientResources = new IngestClientResources({}, {}, {}, {}); + const call = sinon.stub(resourceManager, "getIngestClientResourcesFromService"); + resourceManager.ingestClientResourcesLastUpdate = moment(); + resourceManager.ingestClientResources = new IngestClientResources([], [], [], []); await resourceManager.refreshIngestClientResources(); - assert.equal(call.calledOnce, false); + assert.strictEqual(call.calledOnce, false); }); }); }); diff --git a/azure-kusto-ingest/tsconfig.json b/azure-kusto-ingest/tsconfig.json new file mode 100644 index 0000000..3c5d0fd --- /dev/null +++ b/azure-kusto-ingest/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "esModuleInterop": true, + "strict": true, + "incremental": true, + "sourceMap": true, + "resolveJsonModule": true, + "allowJs": false, + "declaration": true + }, + "include": ["source/**/*.ts", "test/**/*.ts", "index.ts"] +} \ No newline at end of file diff --git a/azure-kusto-ingest/tslint.json b/azure-kusto-ingest/tslint.json new file mode 100644 index 0000000..15dab24 --- /dev/null +++ b/azure-kusto-ingest/tslint.json @@ -0,0 +1,18 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:recommended" + ], + "jsRules": {}, + "rules": { + "max-classes-per-file": false, + "triple-equals": false, + "only-arrow-functions": false + }, + "linterOptions": { + "exclude": [ + "./test/**/*" + ] + }, + "rulesDirectory": [] +} \ No newline at end of file